Added ListenableFuture interface

Added extension to Future with capabilities for registering callbacks
when the future is complete.

- Added ListenableFuture, ListenableFutureCallback,
  ListenableFutureCallbackRegistry, and ListenableFutureTask.
- Using ListenableFuture in AsyncRestOperations/AsyncRestTemplate.
- Added AsyncListenableTaskExecutor, implemented in
  SimpleAsyncTaskExecutor.
- Added FutureAdapter and ListenableFutureAdapter.
This commit is contained in:
Arjen Poutsma
2013-09-02 15:23:57 +02:00
parent 1c47c8f35c
commit d0aa158aef
23 changed files with 1227 additions and 230 deletions

View File

@@ -18,10 +18,10 @@ package org.springframework.http.client;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.Future;
import org.springframework.http.HttpHeaders;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Abstract base for {@link AsyncClientHttpRequest} that makes sure that headers and body
@@ -49,15 +49,15 @@ abstract class AbstractAsyncClientHttpRequest implements AsyncClientHttpRequest
}
@Override
public Future<ClientHttpResponse> executeAsync() throws IOException {
public ListenableFuture<ClientHttpResponse> executeAsync() throws IOException {
assertNotExecuted();
Future<ClientHttpResponse> result = executeInternal(this.headers);
ListenableFuture<ClientHttpResponse> result = executeInternal(this.headers);
this.executed = true;
return result;
}
/**
* Asserts that this request has not been {@linkplain #execute() executed} yet.
* Asserts that this request has not been {@linkplain #executeAsync() executed} yet.
*
* @throws IllegalStateException if this request has been executed
*/
@@ -74,10 +74,12 @@ abstract class AbstractAsyncClientHttpRequest implements AsyncClientHttpRequest
protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;
/**
* Abstract template method that writes the given headers and content to the HTTP request.
* Abstract template method that writes the given headers and content to the HTTP
* request.
* @param headers the HTTP headers
* @return the response object for the executed request
*/
protected abstract Future<ClientHttpResponse> executeInternal(HttpHeaders headers) throws IOException;
protected abstract ListenableFuture<ClientHttpResponse> executeInternal(
HttpHeaders headers) throws IOException;
}

View File

@@ -19,9 +19,9 @@ package org.springframework.http.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.Future;
import org.springframework.http.HttpHeaders;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Abstract base for {@link org.springframework.http.client.ClientHttpRequest} that buffers output in a byte array before sending it over the wire.
@@ -40,12 +40,13 @@ abstract class AbstractBufferingAsyncClientHttpRequest
}
@Override
protected Future<ClientHttpResponse> executeInternal(HttpHeaders headers) throws IOException {
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers)
throws IOException {
byte[] bytes = this.bufferedOutput.toByteArray();
if (headers.getContentLength() == -1) {
headers.setContentLength(bytes.length);
}
Future<ClientHttpResponse> result = executeInternal(headers, bytes);
ListenableFuture<ClientHttpResponse> result = executeInternal(headers, bytes);
this.bufferedOutput = null;
return result;
}
@@ -58,8 +59,8 @@ abstract class AbstractBufferingAsyncClientHttpRequest
* @param bufferedOutput the body content
* @return the response object for the executed request
*/
protected abstract Future<ClientHttpResponse> executeInternal(HttpHeaders headers,
byte[] bufferedOutput) throws IOException;
protected abstract ListenableFuture<ClientHttpResponse> executeInternal(
HttpHeaders headers, byte[] bufferedOutput) throws IOException;
}

View File

@@ -17,10 +17,10 @@
package org.springframework.http.client;
import java.io.IOException;
import java.util.concurrent.Future;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpRequest;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Represents a client-side asynchronous HTTP request. Created via an implementation of
@@ -41,7 +41,7 @@ public interface AsyncClientHttpRequest extends HttpRequest, HttpOutputMessage {
* @return the future response result of the execution
* @throws java.io.IOException in case of I/O errors
*/
Future<ClientHttpResponse> executeAsync() throws IOException;
ListenableFuture<ClientHttpResponse> executeAsync() throws IOException;
}

View File

@@ -18,21 +18,23 @@ package org.springframework.http.client;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.concurrent.FutureAdapter;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.util.concurrent.ListenableFutureCallbackRegistry;
/**
* {@link ClientHttpRequest} implementation that uses Apache HttpComponents HttpClient to
@@ -72,7 +74,7 @@ final class HttpComponentsAsyncClientHttpRequest extends AbstractBufferingAsyncC
}
@Override
protected Future<ClientHttpResponse> executeInternal(HttpHeaders headers,
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers,
byte[] bufferedOutput) throws IOException {
HttpComponentsClientHttpRequest.addHeaders(this.httpRequest, headers);
@@ -83,50 +85,61 @@ final class HttpComponentsAsyncClientHttpRequest extends AbstractBufferingAsyncC
entityEnclosingRequest.setEntity(requestEntity);
}
final HttpResponseFutureCallback callback = new HttpResponseFutureCallback();
final Future<HttpResponse> futureResponse =
this.httpClient.execute(this.httpRequest, this.httpContext, null);
return new ClientHttpResponseFuture(futureResponse);
this.httpClient.execute(this.httpRequest, this.httpContext, callback);
return new ClientHttpResponseFuture(futureResponse, callback);
}
private static class HttpResponseFutureCallback implements FutureCallback<HttpResponse> {
private final ListenableFutureCallbackRegistry<ClientHttpResponse> callbacks =
new ListenableFutureCallbackRegistry<ClientHttpResponse>();
public void addCallback(
ListenableFutureCallback<? super ClientHttpResponse> callback) {
callbacks.addCallback(callback);
}
@Override
public void completed(HttpResponse result) {
callbacks.success(new HttpComponentsAsyncClientHttpResponse(result));
}
@Override
public void failed(Exception ex) {
callbacks.failure(ex);
}
@Override
public void cancelled() {
}
}
private static class ClientHttpResponseFuture implements Future<ClientHttpResponse> {
private static class ClientHttpResponseFuture extends FutureAdapter<ClientHttpResponse, HttpResponse>
implements ListenableFuture<ClientHttpResponse> {
private final Future<HttpResponse> futureResponse;
private final HttpResponseFutureCallback callback;
public ClientHttpResponseFuture(Future<HttpResponse> futureResponse) {
this.futureResponse = futureResponse;
private ClientHttpResponseFuture(Future<HttpResponse> futureResponse,
HttpResponseFutureCallback callback) {
super(futureResponse);
this.callback = callback;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return futureResponse.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return futureResponse.isCancelled();
}
@Override
public boolean isDone() {
return futureResponse.isDone();
}
@Override
public ClientHttpResponse get()
throws InterruptedException, ExecutionException {
HttpResponse response = futureResponse.get();
protected ClientHttpResponse adapt(HttpResponse response) {
return new HttpComponentsAsyncClientHttpResponse(response);
}
@Override
public ClientHttpResponse get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
HttpResponse response = futureResponse.get(timeout, unit);
return new HttpComponentsAsyncClientHttpResponse(response);
public void addCallback(
ListenableFutureCallback<? super ClientHttpResponse> callback) {
this.callback.addCallback(callback);
}
}

View File

@@ -23,12 +23,12 @@ import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* {@link org.springframework.http.client.ClientHttpRequest} implementation that uses
@@ -45,10 +45,10 @@ final class SimpleBufferingAsyncClientHttpRequest extends AbstractBufferingAsync
private final boolean outputStreaming;
private final AsyncTaskExecutor taskExecutor;
private final AsyncListenableTaskExecutor taskExecutor;
SimpleBufferingAsyncClientHttpRequest(HttpURLConnection connection,
boolean outputStreaming, AsyncTaskExecutor taskExecutor) {
boolean outputStreaming, AsyncListenableTaskExecutor taskExecutor) {
this.connection = connection;
this.outputStreaming = outputStreaming;
this.taskExecutor = taskExecutor;
@@ -70,9 +70,9 @@ final class SimpleBufferingAsyncClientHttpRequest extends AbstractBufferingAsync
}
@Override
protected Future<ClientHttpResponse> executeInternal(final HttpHeaders headers,
final byte[] bufferedOutput) throws IOException {
return taskExecutor.submit(new Callable<ClientHttpResponse>() {
protected ListenableFuture<ClientHttpResponse> executeInternal(
final HttpHeaders headers, final byte[] bufferedOutput) throws IOException {
return taskExecutor.submitListenable(new Callable<ClientHttpResponse>() {
@Override
public ClientHttpResponse call() throws Exception {
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {

View File

@@ -23,7 +23,7 @@ import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
@@ -54,7 +54,7 @@ public class SimpleClientHttpRequestFactory
private boolean outputStreaming = true;
private AsyncTaskExecutor taskExecutor;
private AsyncListenableTaskExecutor taskExecutor;
/**
@@ -131,7 +131,7 @@ public class SimpleClientHttpRequestFactory
* request}.
* @param taskExecutor the task executor
*/
public void setTaskExecutor(AsyncTaskExecutor taskExecutor) {
public void setTaskExecutor(AsyncListenableTaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
@@ -149,7 +149,7 @@ public class SimpleClientHttpRequestFactory
/**
* {@inheritDoc}
* <p>Setting the {@link #setTaskExecutor(AsyncTaskExecutor) taskExecutor} property
* <p>Setting the {@link #setTaskExecutor(org.springframework.core.task.AsyncListenableTaskExecutor) taskExecutor} property
* is required before calling this method.
*/
@Override

View File

@@ -24,12 +24,12 @@ import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.StreamUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* {@link org.springframework.http.client.ClientHttpRequest} implementation that uses
@@ -51,10 +51,10 @@ final class SimpleStreamingAsyncClientHttpRequest extends AbstractAsyncClientHtt
private final boolean outputStreaming;
private final AsyncTaskExecutor taskExecutor;
private final AsyncListenableTaskExecutor taskExecutor;
SimpleStreamingAsyncClientHttpRequest(HttpURLConnection connection, int chunkSize,
boolean outputStreaming, AsyncTaskExecutor taskExecutor) {
boolean outputStreaming, AsyncListenableTaskExecutor taskExecutor) {
this.connection = connection;
this.chunkSize = chunkSize;
this.outputStreaming = outputStreaming;
@@ -106,9 +106,9 @@ final class SimpleStreamingAsyncClientHttpRequest extends AbstractAsyncClientHtt
}
@Override
protected Future<ClientHttpResponse> executeInternal(final HttpHeaders headers)
protected ListenableFuture<ClientHttpResponse> executeInternal(final HttpHeaders headers)
throws IOException {
return taskExecutor.submit(new Callable<ClientHttpResponse>() {
return taskExecutor.submitListenable(new Callable<ClientHttpResponse>() {
@Override
public ClientHttpResponse call() throws Exception {
try {

View File

@@ -26,6 +26,7 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Interface specifying a basic set of asynchronous RESTful operations. Implemented by
@@ -54,7 +55,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand the template
* @return the entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
<T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
Object... uriVariables) throws RestClientException;
/**
@@ -66,7 +67,7 @@ public interface AsyncRestOperations {
* @param uriVariables the map containing variables for the URI template
* @return the entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
<T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
/**
@@ -76,7 +77,7 @@ public interface AsyncRestOperations {
* @param responseType the type of the return value
* @return the entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType)
<T> ListenableFuture<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType)
throws RestClientException;
// HEAD
@@ -88,7 +89,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand the template
* @return all HTTP headers of that resource wrapped in a {@link Future}
*/
Future<HttpHeaders> headForHeaders(String url, Object... uriVariables)
ListenableFuture<HttpHeaders> headForHeaders(String url, Object... uriVariables)
throws RestClientException;
/**
@@ -98,7 +99,7 @@ public interface AsyncRestOperations {
* @param uriVariables the map containing variables for the URI template
* @return all HTTP headers of that resource wrapped in a {@link Future}
*/
Future<HttpHeaders> headForHeaders(String url, Map<String, ?> uriVariables)
ListenableFuture<HttpHeaders> headForHeaders(String url, Map<String, ?> uriVariables)
throws RestClientException;
/**
@@ -106,7 +107,7 @@ public interface AsyncRestOperations {
* @param url the URL
* @return all HTTP headers of that resource wrapped in a {@link Future}
*/
Future<HttpHeaders> headForHeaders(URI url) throws RestClientException;
ListenableFuture<HttpHeaders> headForHeaders(URI url) throws RestClientException;
// POST
@@ -121,7 +122,7 @@ public interface AsyncRestOperations {
* @return the value for the {@code Location} header wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
Future<URI> postForLocation(String url, HttpEntity<?> request, Object... uriVariables)
ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, Object... uriVariables)
throws RestClientException;
/**
@@ -135,7 +136,7 @@ public interface AsyncRestOperations {
* @return the value for the {@code Location} header wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
Future<URI> postForLocation(String url, HttpEntity<?> request, Map<String, ?> uriVariables)
ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, Map<String, ?> uriVariables)
throws RestClientException;
/**
@@ -147,7 +148,7 @@ public interface AsyncRestOperations {
* @return the value for the {@code Location} header wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
Future<URI> postForLocation(URI url, HttpEntity<?> request) throws RestClientException;
ListenableFuture<URI> postForLocation(URI url, HttpEntity<?> request) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
@@ -159,7 +160,7 @@ public interface AsyncRestOperations {
* @return the entity wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
<T> Future<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
<T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
Class<T> responseType, Object... uriVariables) throws RestClientException;
/**
@@ -172,7 +173,7 @@ public interface AsyncRestOperations {
* @return the entity wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
<T> Future<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
<T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException;
@@ -184,7 +185,7 @@ public interface AsyncRestOperations {
* @return the entity wrapped in a {@link Future}
* @see org.springframework.http.HttpEntity
*/
<T> Future<ResponseEntity<T>> postForEntity(URI url, HttpEntity<?> request,
<T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url, HttpEntity<?> request,
Class<T> responseType) throws RestClientException;
// PUT
@@ -198,7 +199,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand the template
* @see HttpEntity
*/
Future<?> put(String url, HttpEntity<?> request, Object... uriVariables)
ListenableFuture<?> put(String url, HttpEntity<?> request, Object... uriVariables)
throws RestClientException;
/**
@@ -210,7 +211,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand the template
* @see HttpEntity
*/
Future<?> put(String url, HttpEntity<?> request, Map<String, ?> uriVariables)
ListenableFuture<?> put(String url, HttpEntity<?> request, Map<String, ?> uriVariables)
throws RestClientException;
/**
@@ -220,7 +221,7 @@ public interface AsyncRestOperations {
* @param request the Object to be PUT, may be {@code null}
* @see HttpEntity
*/
Future<?> put(URI url, HttpEntity<?> request) throws RestClientException;
ListenableFuture<?> put(URI url, HttpEntity<?> request) throws RestClientException;
// DELETE
@@ -231,7 +232,7 @@ public interface AsyncRestOperations {
* @param url the URL
* @param uriVariables the variables to expand in the template
*/
Future<?> delete(String url, Object... uriVariables) throws RestClientException;
ListenableFuture<?> delete(String url, Object... uriVariables) throws RestClientException;
/**
* Asynchronously delete the resources at the specified URI.
@@ -240,7 +241,7 @@ public interface AsyncRestOperations {
* @param url the URL
* @param uriVariables the variables to expand in the template
*/
Future<?> delete(String url, Map<String, ?> uriVariables) throws RestClientException;
ListenableFuture<?> delete(String url, Map<String, ?> uriVariables) throws RestClientException;
/**
* Asynchronously delete the resources at the specified URI.
@@ -248,7 +249,7 @@ public interface AsyncRestOperations {
* <p>The Future will return a {@code null} result upon completion.
* @param url the URL
*/
Future<?> delete(URI url) throws RestClientException;
ListenableFuture<?> delete(URI url) throws RestClientException;
// OPTIONS
@@ -259,7 +260,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return the value of the allow header wrapped in a {@link Future}
*/
Future<Set<HttpMethod>> optionsForAllow(String url, Object... uriVariables)
ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Object... uriVariables)
throws RestClientException;
/**
@@ -269,7 +270,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return the value of the allow header wrapped in a {@link Future}
*/
Future<Set<HttpMethod>> optionsForAllow(String url, Map<String, ?> uriVariables)
ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Map<String, ?> uriVariables)
throws RestClientException;
/**
@@ -277,7 +278,7 @@ public interface AsyncRestOperations {
* @param url the URL
* @return the value of the allow header wrapped in a {@link Future}
*/
Future<Set<HttpMethod>> optionsForAllow(URI url) throws RestClientException;
ListenableFuture<Set<HttpMethod>> optionsForAllow(URI url) throws RestClientException;
// exchange
@@ -295,7 +296,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
throws RestClientException;
@@ -312,7 +313,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
@@ -327,7 +328,7 @@ public interface AsyncRestOperations {
* @param responseType the type of the return value
* @return the response as entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> exchange(URI url, HttpMethod method,
<T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType)
throws RestClientException;
@@ -348,7 +349,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Object... uriVariables) throws RestClientException;
@@ -368,7 +369,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return the response as entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
<T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
@@ -387,7 +388,7 @@ public interface AsyncRestOperations {
* @param responseType the type of the return value
* @return the response as entity wrapped in a {@link Future}
*/
<T> Future<ResponseEntity<T>> exchange(URI url, HttpMethod method,
<T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
throws RestClientException;
@@ -406,7 +407,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> Future<T> execute(String url, HttpMethod method,
<T> ListenableFuture<T> execute(String url, HttpMethod method,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor,
Object... uriVariables) throws RestClientException;
@@ -422,7 +423,7 @@ public interface AsyncRestOperations {
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> Future<T> execute(String url, HttpMethod method,
<T> ListenableFuture<T> execute(String url, HttpMethod method,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor,
Map<String, ?> uriVariables) throws RestClientException;
@@ -436,7 +437,7 @@ public interface AsyncRestOperations {
* @param responseExtractor object that extracts the return value from the response
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> Future<T> execute(URI url, HttpMethod method,
<T> ListenableFuture<T> execute(URI url, HttpMethod method,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor)
throws RestClientException;

View File

@@ -24,11 +24,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.http.HttpEntity;
@@ -44,12 +44,15 @@ import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.client.support.AsyncHttpAccessor;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureAdapter;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.util.UriTemplate;
/**
* <strong>Spring's central class for asynchronous client-side HTTP access.</strong>
* Exposes similar methods as {@link RestTemplate}, but returns {@link Future} wrappers
* as opposed to concrete results.
* Exposes similar methods as {@link RestTemplate}, but returns {@link ListenableFuture}
* wrappers as opposed to concrete results.
*
* <p>The {@code AsyncRestTemplate} exposes a synchronous {@link RestTemplate} via the
* {@link #getRestOperations()} method, and it shares its
@@ -69,7 +72,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
/**
* Create a new instance of the {@link AsyncRestTemplate} using default settings.
* Create a new instance of the {@code AsyncRestTemplate} using default settings.
* <p>This constructor uses a {@link SimpleClientHttpRequestFactory} in combination
* with a {@link SimpleAsyncTaskExecutor} for asynchronous execution.
*/
@@ -78,21 +81,22 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
/**
* Create a new instance of the {@link AsyncRestTemplate} using the given
* Create a new instance of the {@code AsyncRestTemplate} using the given
* {@link AsyncTaskExecutor}.
* <p>This constructor uses a {@link SimpleClientHttpRequestFactory} in combination
* with the given {@code AsyncTaskExecutor} for asynchronous execution.
*/
public AsyncRestTemplate(AsyncTaskExecutor taskExecutor) {
public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor) {
Assert.notNull(taskExecutor, "AsyncTaskExecutor must not be null");
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
SimpleClientHttpRequestFactory requestFactory =
new SimpleClientHttpRequestFactory();
requestFactory.setTaskExecutor(taskExecutor);
this.syncTemplate = new RestTemplate(requestFactory);
setAsyncRequestFactory(requestFactory);
}
/**
* Create a new instance of the {@link AsyncRestTemplate} using the given
* Create a new instance of the {@code AsyncRestTemplate} using the given
* {@link AsyncClientHttpRequestFactory}.
* <p>This constructor will cast the given asynchronous
* {@code AsyncClientHttpRequestFactory} to a {@link ClientHttpRequestFactory}. Since
@@ -105,22 +109,24 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
/**
* Creates a new instance of the {@link AsyncRestTemplate} using the given
* Creates a new instance of the {@code AsyncRestTemplate} using the given
* asynchronous and synchronous request factories.
* @param asyncRequestFactory the asynchronous request factory
* @param syncRequestFactory the synchronous request factory
*/
public AsyncRestTemplate(AsyncClientHttpRequestFactory asyncRequestFactory, ClientHttpRequestFactory syncRequestFactory) {
public AsyncRestTemplate(AsyncClientHttpRequestFactory asyncRequestFactory,
ClientHttpRequestFactory syncRequestFactory) {
this(asyncRequestFactory, new RestTemplate(syncRequestFactory));
}
/**
* Create a new instance of the {@link AsyncRestTemplate} using the given
* Create a new instance of the {@code AsyncRestTemplate} using the given
* {@link AsyncClientHttpRequestFactory} and synchronous {@link RestTemplate}.
* @param requestFactory the asynchronous request factory to use
* @param restTemplate the synchronous template to use
*/
public AsyncRestTemplate(AsyncClientHttpRequestFactory requestFactory, RestTemplate restTemplate) {
public AsyncRestTemplate(AsyncClientHttpRequestFactory requestFactory,
RestTemplate restTemplate) {
Assert.notNull(restTemplate, "'restTemplate' must not be null");
this.syncTemplate = restTemplate;
setAsyncRequestFactory(requestFactory);
@@ -165,7 +171,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// GET
@Override
public <T> Future<ResponseEntity<T>> getForEntity(String url, Class<T> responseType, Object... uriVariables)
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException {
AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
@@ -174,7 +180,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
Map<String, ?> urlVariables) throws RestClientException {
AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
@@ -183,7 +189,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType) throws RestClientException {
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType) throws RestClientException {
AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
@@ -192,19 +198,19 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// HEAD
@Override
public Future<HttpHeaders> headForHeaders(String url, Object... uriVariables) throws RestClientException {
public ListenableFuture<HttpHeaders> headForHeaders(String url, Object... uriVariables) throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
return execute(url, HttpMethod.HEAD, null, headersExtractor, uriVariables);
}
@Override
public Future<HttpHeaders> headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException {
public ListenableFuture<HttpHeaders> headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
return execute(url, HttpMethod.HEAD, null, headersExtractor, uriVariables);
}
@Override
public Future<HttpHeaders> headForHeaders(URI url) throws RestClientException {
public ListenableFuture<HttpHeaders> headForHeaders(URI url) throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
return execute(url, HttpMethod.HEAD, null, headersExtractor);
}
@@ -212,39 +218,55 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// POST
@Override
public Future<URI> postForLocation(String url, HttpEntity<?> request,
public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request,
Object... uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
Future<HttpHeaders> headersFuture =
ListenableFuture<HttpHeaders> headersFuture =
execute(url, HttpMethod.POST, requestCallback, headersExtractor,
uriVariables);
return extractLocationHeader(headersFuture);
}
@Override
public Future<URI> postForLocation(String url, HttpEntity<?> request,
public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request,
Map<String, ?> uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
Future<HttpHeaders> headersFuture =
ListenableFuture<HttpHeaders> headersFuture =
execute(url, HttpMethod.POST, requestCallback, headersExtractor,
uriVariables);
return extractLocationHeader(headersFuture);
}
@Override
public Future<URI> postForLocation(URI url, HttpEntity<?> request)
public ListenableFuture<URI> postForLocation(URI url, HttpEntity<?> request)
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
Future<HttpHeaders> headersFuture =
ListenableFuture<HttpHeaders> headersFuture =
execute(url, HttpMethod.POST, requestCallback, headersExtractor);
return extractLocationHeader(headersFuture);
}
private static Future<URI> extractLocationHeader(final Future<HttpHeaders> headersFuture) {
return new Future<URI>() {
private static ListenableFuture<URI> extractLocationHeader(final ListenableFuture<HttpHeaders> headersFuture) {
return new ListenableFuture<URI>() {
@Override
public void addCallback(final ListenableFutureCallback<? super URI> callback) {
headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() {
@Override
public void onSuccess(HttpHeaders result) {
callback.onSuccess(result.getLocation());
}
@Override
public void onFailure(Throwable t) {
callback.onFailure(t);
}
});
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return headersFuture.cancel(mayInterruptIfRunning);
@@ -272,7 +294,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
Class<T> responseType, Object... uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor =
@@ -282,7 +304,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request,
Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
@@ -293,7 +315,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> postForEntity(URI url, HttpEntity<?> request,
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url, HttpEntity<?> request,
Class<T> responseType) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor =
@@ -304,21 +326,21 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// PUT
@Override
public Future<?> put(String url, HttpEntity<?> request, Object... uriVariables)
public ListenableFuture<?> put(String url, HttpEntity<?> request, Object... uriVariables)
throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
}
@Override
public Future<?> put(String url, HttpEntity<?> request,
public ListenableFuture<?> put(String url, HttpEntity<?> request,
Map<String, ?> uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
}
@Override
public Future<?> put(URI url, HttpEntity<?> request) throws RestClientException {
public ListenableFuture<?> put(URI url, HttpEntity<?> request) throws RestClientException {
AsyncRequestCallback requestCallback = httpEntityCallback(request);
return execute(url, HttpMethod.PUT, requestCallback, null);
}
@@ -326,47 +348,64 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// DELETE
@Override
public Future<?> delete(String url, Object... urlVariables)
public ListenableFuture<?> delete(String url, Object... urlVariables)
throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null, urlVariables);
}
@Override
public Future<?> delete(String url, Map<String, ?> urlVariables)
public ListenableFuture<?> delete(String url, Map<String, ?> urlVariables)
throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null, urlVariables);
}
@Override
public Future<?> delete(URI url) throws RestClientException {
public ListenableFuture<?> delete(URI url) throws RestClientException {
return execute(url, HttpMethod.DELETE, null, null);
}
// OPTIONS
@Override
public Future<Set<HttpMethod>> optionsForAllow(String url, Object... uriVariables) throws RestClientException {
public ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Object... uriVariables) throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
Future<HttpHeaders> headersFuture = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
ListenableFuture<HttpHeaders> headersFuture = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
return extractAllowHeader(headersFuture);
}
@Override
public Future<Set<HttpMethod>> optionsForAllow(String url, Map<String, ?> uriVariables) throws RestClientException {
public ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Map<String, ?> uriVariables) throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
Future<HttpHeaders> headersFuture = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
ListenableFuture<HttpHeaders> headersFuture = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
return extractAllowHeader(headersFuture);
}
@Override
public Future<Set<HttpMethod>> optionsForAllow(URI url) throws RestClientException {
public ListenableFuture<Set<HttpMethod>> optionsForAllow(URI url) throws RestClientException {
ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
Future<HttpHeaders> headersFuture = execute(url, HttpMethod.OPTIONS, null, headersExtractor);
ListenableFuture<HttpHeaders> headersFuture = execute(url, HttpMethod.OPTIONS, null, headersExtractor);
return extractAllowHeader(headersFuture);
}
private static Future<Set<HttpMethod>> extractAllowHeader(final Future<HttpHeaders> headersFuture) {
return new Future<Set<HttpMethod>>() {
private static ListenableFuture<Set<HttpMethod>> extractAllowHeader(final ListenableFuture<HttpHeaders> headersFuture) {
return new ListenableFuture<Set<HttpMethod>>() {
@Override
public void addCallback(
final ListenableFutureCallback<? super Set<HttpMethod>> callback) {
headersFuture.addCallback(new ListenableFutureCallback<HttpHeaders>() {
@Override
public void onSuccess(HttpHeaders result) {
callback.onSuccess(result.getAllow());
}
@Override
public void onFailure(Throwable t) {
callback.onFailure(t);
}
});
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return headersFuture.cancel(mayInterruptIfRunning);
@@ -397,7 +436,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// exchange
@Override
public <T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
throws RestClientException {
AsyncRequestCallback requestCallback =
@@ -408,7 +447,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException {
AsyncRequestCallback requestCallback =
@@ -419,7 +458,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> exchange(URI url, HttpMethod method,
public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType)
throws RestClientException {
AsyncRequestCallback requestCallback =
@@ -430,7 +469,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Object... uriVariables) throws RestClientException {
Type type = responseType.getType();
@@ -441,7 +480,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> exchange(String url, HttpMethod method,
public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
Map<String, ?> uriVariables) throws RestClientException {
Type type = responseType.getType();
@@ -452,7 +491,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<ResponseEntity<T>> exchange(URI url, HttpMethod method,
public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
throws RestClientException {
Type type = responseType.getType();
@@ -466,7 +505,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
// general execution
@Override
public <T> Future<T> execute(String url, HttpMethod method,
public <T> ListenableFuture<T> execute(String url, HttpMethod method,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor,
Object... urlVariables) throws RestClientException {
@@ -475,7 +514,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<T> execute(String url, HttpMethod method,
public <T> ListenableFuture<T> execute(String url, HttpMethod method,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor,
Map<String, ?> urlVariables) throws RestClientException {
@@ -484,7 +523,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
@Override
public <T> Future<T> execute(URI url, HttpMethod method,
public <T> ListenableFuture<T> execute(URI url, HttpMethod method,
AsyncRequestCallback requestCallback, ResponseExtractor<T> responseExtractor)
throws RestClientException {
@@ -504,7 +543,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
@SuppressWarnings("unchecked")
protected <T> Future<T> doExecute(URI url, HttpMethod method, AsyncRequestCallback requestCallback,
protected <T> ListenableFuture<T> doExecute(URI url, HttpMethod method, AsyncRequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
@@ -514,13 +553,9 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
Future<ClientHttpResponse> responseFuture = request.executeAsync();
if (responseExtractor != null) {
return new ResponseExtractorFuture<T>(method, url, responseFuture, responseExtractor);
}
else {
return (Future<T>) new VoidResponseFuture(method, url, responseFuture);
}
ListenableFuture<ClientHttpResponse> responseFuture = request.executeAsync();
return new ResponseExtractorFuture<T>(method, url, responseFuture,
responseExtractor);
}
catch (IOException ex) {
throw new ResourceAccessException("I/O error on " + method.name() +
@@ -594,47 +629,30 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return this.syncTemplate.headersExtractor();
}
private abstract class ResponseFuture<T> implements Future<T> {
/**
* Future returned from
* {@link #doExecute(URI, HttpMethod, AsyncRequestCallback, ResponseExtractor)}
*/
private class ResponseExtractorFuture<T>
extends ListenableFutureAdapter<T, ClientHttpResponse> {
private final HttpMethod method;
private final URI url;
private final Future<ClientHttpResponse> responseFuture;
private final ResponseExtractor<T> responseExtractor;
public ResponseFuture(HttpMethod method, URI url, Future<ClientHttpResponse> responseFuture) {
public ResponseExtractorFuture(HttpMethod method, URI url,
ListenableFuture<ClientHttpResponse> clientHttpResponseFuture,
ResponseExtractor<T> responseExtractor) {
super(clientHttpResponseFuture);
this.method = method;
this.url = url;
this.responseFuture = responseFuture;
this.responseExtractor = responseExtractor;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return this.responseFuture.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return this.responseFuture.isCancelled();
}
@Override
public boolean isDone() {
return this.responseFuture.isDone();
}
@Override
public T get() throws InterruptedException, ExecutionException {
return getInternal(this.responseFuture.get());
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return getInternal(this.responseFuture.get(timeout, unit));
}
private T getInternal(ClientHttpResponse response) throws ExecutionException {
protected final T adapt(ClientHttpResponse response) throws ExecutionException {
try {
if (!getErrorHandler().hasError(response)) {
logResponseStatus(this.method, this.url, response);
@@ -642,7 +660,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
else {
handleResponseError(this.method, this.url, response);
}
return extractData(response);
return convertResponse(response);
}
catch (IOException ex) {
throw new ExecutionException(ex);
@@ -654,42 +672,13 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
}
}
protected abstract T extractData(ClientHttpResponse response)
throws IOException;
protected T convertResponse(ClientHttpResponse response) throws IOException {
return responseExtractor != null ? responseExtractor.extractData(response) :
null;
}
}
private class ResponseExtractorFuture<T> extends ResponseFuture<T> {
private final ResponseExtractor<T> responseExtractor;
public ResponseExtractorFuture(HttpMethod method, URI url, Future<ClientHttpResponse> responseFuture,
ResponseExtractor<T> responseExtractor) {
super(method, url, responseFuture);
this.responseExtractor = responseExtractor;
}
@Override
protected T extractData(ClientHttpResponse response) throws IOException {
return responseExtractor.extractData(response);
}
}
private class VoidResponseFuture extends ResponseFuture<Void> {
public VoidResponseFuture(HttpMethod method, URI url, Future<ClientHttpResponse> responseFuture) {
super(method, url, responseFuture);
}
@Override
protected Void extractData(ClientHttpResponse response) throws IOException {
return null;
}
}
/**
* Adapts a {@link RequestCallback} to the {@link AsyncRequestCallback} interface.
*/