001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.client; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.lang.reflect.Type; 022import java.net.URI; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.concurrent.ExecutionException; 027 028import org.springframework.core.ParameterizedTypeReference; 029import org.springframework.core.task.AsyncListenableTaskExecutor; 030import org.springframework.core.task.AsyncTaskExecutor; 031import org.springframework.core.task.SimpleAsyncTaskExecutor; 032import org.springframework.http.HttpEntity; 033import org.springframework.http.HttpHeaders; 034import org.springframework.http.HttpMethod; 035import org.springframework.http.ResponseEntity; 036import org.springframework.http.client.AsyncClientHttpRequest; 037import org.springframework.http.client.AsyncClientHttpRequestFactory; 038import org.springframework.http.client.ClientHttpRequest; 039import org.springframework.http.client.ClientHttpRequestFactory; 040import org.springframework.http.client.ClientHttpResponse; 041import org.springframework.http.client.SimpleClientHttpRequestFactory; 042import org.springframework.http.client.support.InterceptingAsyncHttpAccessor; 043import org.springframework.http.converter.HttpMessageConverter; 044import org.springframework.util.Assert; 045import org.springframework.util.concurrent.ListenableFuture; 046import org.springframework.util.concurrent.ListenableFutureAdapter; 047import org.springframework.web.util.AbstractUriTemplateHandler; 048import org.springframework.web.util.UriTemplateHandler; 049 050/** 051 * <strong>Spring's central class for asynchronous client-side HTTP access.</strong> 052 * Exposes similar methods as {@link RestTemplate}, but returns {@link ListenableFuture} 053 * wrappers as opposed to concrete results. 054 * 055 * <p>The {@code AsyncRestTemplate} exposes a synchronous {@link RestTemplate} via the 056 * {@link #getRestOperations()} method and shares its {@linkplain #setErrorHandler error handler} 057 * and {@linkplain #setMessageConverters message converters} with that {@code RestTemplate}. 058 * 059 * <p><strong>Note:</strong> by default {@code AsyncRestTemplate} relies on 060 * standard JDK facilities to establish HTTP connections. You can switch to use 061 * a different HTTP library such as Apache HttpComponents, Netty, and OkHttp by 062 * using a constructor accepting an {@link AsyncClientHttpRequestFactory}. 063 * 064 * <p>For more information, please refer to the {@link RestTemplate} API documentation. 065 * 066 * @author Arjen Poutsma 067 * @since 4.0 068 * @see RestTemplate 069 */ 070public class AsyncRestTemplate extends InterceptingAsyncHttpAccessor implements AsyncRestOperations { 071 072 private final RestTemplate syncTemplate; 073 074 075 /** 076 * Create a new instance of the {@code AsyncRestTemplate} using default settings. 077 * <p>This constructor uses a {@link SimpleClientHttpRequestFactory} in combination 078 * with a {@link SimpleAsyncTaskExecutor} for asynchronous execution. 079 */ 080 public AsyncRestTemplate() { 081 this(new SimpleAsyncTaskExecutor()); 082 } 083 084 /** 085 * Create a new instance of the {@code AsyncRestTemplate} using the given 086 * {@link AsyncTaskExecutor}. 087 * <p>This constructor uses a {@link SimpleClientHttpRequestFactory} in combination 088 * with the given {@code AsyncTaskExecutor} for asynchronous execution. 089 */ 090 public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor) { 091 Assert.notNull(taskExecutor, "AsyncTaskExecutor must not be null"); 092 SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); 093 requestFactory.setTaskExecutor(taskExecutor); 094 this.syncTemplate = new RestTemplate(requestFactory); 095 setAsyncRequestFactory(requestFactory); 096 } 097 098 /** 099 * Create a new instance of the {@code AsyncRestTemplate} using the given 100 * {@link AsyncClientHttpRequestFactory}. 101 * <p>This constructor will cast the given asynchronous 102 * {@code AsyncClientHttpRequestFactory} to a {@link ClientHttpRequestFactory}. Since 103 * all implementations of {@code ClientHttpRequestFactory} provided in Spring also 104 * implement {@code AsyncClientHttpRequestFactory}, this should not result in a 105 * {@code ClassCastException}. 106 */ 107 public AsyncRestTemplate(AsyncClientHttpRequestFactory asyncRequestFactory) { 108 this(asyncRequestFactory, (ClientHttpRequestFactory) asyncRequestFactory); 109 } 110 111 /** 112 * Creates a new instance of the {@code AsyncRestTemplate} using the given 113 * asynchronous and synchronous request factories. 114 * @param asyncRequestFactory the asynchronous request factory 115 * @param syncRequestFactory the synchronous request factory 116 */ 117 public AsyncRestTemplate( 118 AsyncClientHttpRequestFactory asyncRequestFactory, ClientHttpRequestFactory syncRequestFactory) { 119 120 this(asyncRequestFactory, new RestTemplate(syncRequestFactory)); 121 } 122 123 /** 124 * Create a new instance of the {@code AsyncRestTemplate} using the given 125 * {@link AsyncClientHttpRequestFactory} and synchronous {@link RestTemplate}. 126 * @param requestFactory the asynchronous request factory to use 127 * @param restTemplate the synchronous template to use 128 */ 129 public AsyncRestTemplate(AsyncClientHttpRequestFactory requestFactory, RestTemplate restTemplate) { 130 Assert.notNull(restTemplate, "RestTemplate must not be null"); 131 this.syncTemplate = restTemplate; 132 setAsyncRequestFactory(requestFactory); 133 } 134 135 136 /** 137 * Set the error handler. 138 * <p>By default, AsyncRestTemplate uses a 139 * {@link org.springframework.web.client.DefaultResponseErrorHandler}. 140 */ 141 public void setErrorHandler(ResponseErrorHandler errorHandler) { 142 this.syncTemplate.setErrorHandler(errorHandler); 143 } 144 145 /** 146 * Return the error handler. 147 */ 148 public ResponseErrorHandler getErrorHandler() { 149 return this.syncTemplate.getErrorHandler(); 150 } 151 152 /** 153 * Configure default URI variable values. This is a shortcut for: 154 * <pre class="code"> 155 * DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler(); 156 * handler.setDefaultUriVariables(...); 157 * 158 * AsyncRestTemplate restTemplate = new AsyncRestTemplate(); 159 * restTemplate.setUriTemplateHandler(handler); 160 * </pre> 161 * @param defaultUriVariables the default URI variable values 162 * @since 4.3 163 */ 164 public void setDefaultUriVariables(Map<String, ?> defaultUriVariables) { 165 UriTemplateHandler handler = this.syncTemplate.getUriTemplateHandler(); 166 Assert.isInstanceOf(AbstractUriTemplateHandler.class, handler, 167 "Can only use this property in conjunction with a DefaultUriTemplateHandler"); 168 ((AbstractUriTemplateHandler) handler).setDefaultUriVariables(defaultUriVariables); 169 } 170 171 /** 172 * This property has the same purpose as the corresponding property on the 173 * {@code RestTemplate}. For more details see 174 * {@link RestTemplate#setUriTemplateHandler}. 175 * @param handler the URI template handler to use 176 */ 177 public void setUriTemplateHandler(UriTemplateHandler handler) { 178 this.syncTemplate.setUriTemplateHandler(handler); 179 } 180 181 /** 182 * Return the configured URI template handler. 183 */ 184 public UriTemplateHandler getUriTemplateHandler() { 185 return this.syncTemplate.getUriTemplateHandler(); 186 } 187 188 @Override 189 public RestOperations getRestOperations() { 190 return this.syncTemplate; 191 } 192 193 /** 194 * Set the message body converters to use. 195 * <p>These converters are used to convert from and to HTTP requests and responses. 196 */ 197 public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) { 198 this.syncTemplate.setMessageConverters(messageConverters); 199 } 200 201 /** 202 * Return the message body converters. 203 */ 204 public List<HttpMessageConverter<?>> getMessageConverters() { 205 return this.syncTemplate.getMessageConverters(); 206 } 207 208 209 // GET 210 211 @Override 212 public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType, Object... uriVariables) 213 throws RestClientException { 214 215 AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 216 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 217 return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); 218 } 219 220 @Override 221 public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType, 222 Map<String, ?> uriVariables) throws RestClientException { 223 224 AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 225 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 226 return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); 227 } 228 229 @Override 230 public <T> ListenableFuture<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType) throws RestClientException { 231 AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 232 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 233 return execute(url, HttpMethod.GET, requestCallback, responseExtractor); 234 } 235 236 237 // HEAD 238 239 @Override 240 public ListenableFuture<HttpHeaders> headForHeaders(String url, Object... uriVariables) throws RestClientException { 241 ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); 242 return execute(url, HttpMethod.HEAD, null, headersExtractor, uriVariables); 243 } 244 245 @Override 246 public ListenableFuture<HttpHeaders> headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException { 247 ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); 248 return execute(url, HttpMethod.HEAD, null, headersExtractor, uriVariables); 249 } 250 251 @Override 252 public ListenableFuture<HttpHeaders> headForHeaders(URI url) throws RestClientException { 253 ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); 254 return execute(url, HttpMethod.HEAD, null, headersExtractor); 255 } 256 257 258 // POST 259 260 @Override 261 public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, Object... uriVars) 262 throws RestClientException { 263 264 AsyncRequestCallback callback = httpEntityCallback(request); 265 ResponseExtractor<HttpHeaders> extractor = headersExtractor(); 266 ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.POST, callback, extractor, uriVars); 267 return adaptToLocationHeader(future); 268 } 269 270 @Override 271 public ListenableFuture<URI> postForLocation(String url, HttpEntity<?> request, Map<String, ?> uriVars) 272 throws RestClientException { 273 274 AsyncRequestCallback callback = httpEntityCallback(request); 275 ResponseExtractor<HttpHeaders> extractor = headersExtractor(); 276 ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.POST, callback, extractor, uriVars); 277 return adaptToLocationHeader(future); 278 } 279 280 @Override 281 public ListenableFuture<URI> postForLocation(URI url, HttpEntity<?> request) throws RestClientException { 282 AsyncRequestCallback callback = httpEntityCallback(request); 283 ResponseExtractor<HttpHeaders> extractor = headersExtractor(); 284 ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.POST, callback, extractor); 285 return adaptToLocationHeader(future); 286 } 287 288 private static ListenableFuture<URI> adaptToLocationHeader(ListenableFuture<HttpHeaders> future) { 289 return new ListenableFutureAdapter<URI, HttpHeaders>(future) { 290 @Override 291 protected URI adapt(HttpHeaders headers) throws ExecutionException { 292 return headers.getLocation(); 293 } 294 }; 295 } 296 297 @Override 298 public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request, 299 Class<T> responseType, Object... uriVariables) throws RestClientException { 300 301 AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType); 302 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 303 return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); 304 } 305 306 @Override 307 public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, HttpEntity<?> request, 308 Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { 309 310 AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType); 311 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 312 return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); 313 } 314 315 @Override 316 public <T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url, HttpEntity<?> request, Class<T> responseType) 317 throws RestClientException { 318 319 AsyncRequestCallback requestCallback = httpEntityCallback(request, responseType); 320 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 321 return execute(url, HttpMethod.POST, requestCallback, responseExtractor); 322 } 323 324 325 // PUT 326 327 @Override 328 public ListenableFuture<?> put(String url, HttpEntity<?> request, Object... uriVariables) throws RestClientException { 329 AsyncRequestCallback requestCallback = httpEntityCallback(request); 330 return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); 331 } 332 333 @Override 334 public ListenableFuture<?> put(String url, HttpEntity<?> request, Map<String, ?> uriVariables) throws RestClientException { 335 AsyncRequestCallback requestCallback = httpEntityCallback(request); 336 return execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); 337 } 338 339 @Override 340 public ListenableFuture<?> put(URI url, HttpEntity<?> request) throws RestClientException { 341 AsyncRequestCallback requestCallback = httpEntityCallback(request); 342 return execute(url, HttpMethod.PUT, requestCallback, null); 343 } 344 345 346 // DELETE 347 348 @Override 349 public ListenableFuture<?> delete(String url, Object... uriVariables) throws RestClientException { 350 return execute(url, HttpMethod.DELETE, null, null, uriVariables); 351 } 352 353 @Override 354 public ListenableFuture<?> delete(String url, Map<String, ?> uriVariables) throws RestClientException { 355 return execute(url, HttpMethod.DELETE, null, null, uriVariables); 356 } 357 358 @Override 359 public ListenableFuture<?> delete(URI url) throws RestClientException { 360 return execute(url, HttpMethod.DELETE, null, null); 361 } 362 363 364 // OPTIONS 365 366 @Override 367 public ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Object... uriVars) throws RestClientException { 368 ResponseExtractor<HttpHeaders> extractor = headersExtractor(); 369 ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.OPTIONS, null, extractor, uriVars); 370 return adaptToAllowHeader(future); 371 } 372 373 @Override 374 public ListenableFuture<Set<HttpMethod>> optionsForAllow(String url, Map<String, ?> uriVars) throws RestClientException { 375 ResponseExtractor<HttpHeaders> extractor = headersExtractor(); 376 ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.OPTIONS, null, extractor, uriVars); 377 return adaptToAllowHeader(future); 378 } 379 380 @Override 381 public ListenableFuture<Set<HttpMethod>> optionsForAllow(URI url) throws RestClientException { 382 ResponseExtractor<HttpHeaders> extractor = headersExtractor(); 383 ListenableFuture<HttpHeaders> future = execute(url, HttpMethod.OPTIONS, null, extractor); 384 return adaptToAllowHeader(future); 385 } 386 387 private static ListenableFuture<Set<HttpMethod>> adaptToAllowHeader(ListenableFuture<HttpHeaders> future) { 388 return new ListenableFutureAdapter<Set<HttpMethod>, HttpHeaders>(future) { 389 @Override 390 protected Set<HttpMethod> adapt(HttpHeaders headers) throws ExecutionException { 391 return headers.getAllow(); 392 } 393 }; 394 } 395 396 // exchange 397 398 @Override 399 public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, 400 Class<T> responseType, Object... uriVariables) throws RestClientException { 401 402 AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); 403 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 404 return execute(url, method, requestCallback, responseExtractor, uriVariables); 405 } 406 407 @Override 408 public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, 409 Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { 410 411 AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); 412 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 413 return execute(url, method, requestCallback, responseExtractor, uriVariables); 414 } 415 416 @Override 417 public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, 418 Class<T> responseType) throws RestClientException { 419 420 AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); 421 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 422 return execute(url, method, requestCallback, responseExtractor); 423 } 424 425 @Override 426 public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, 427 ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException { 428 429 Type type = responseType.getType(); 430 AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type); 431 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); 432 return execute(url, method, requestCallback, responseExtractor, uriVariables); 433 } 434 435 @Override 436 public <T> ListenableFuture<ResponseEntity<T>> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, 437 ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException { 438 439 Type type = responseType.getType(); 440 AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type); 441 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); 442 return execute(url, method, requestCallback, responseExtractor, uriVariables); 443 } 444 445 @Override 446 public <T> ListenableFuture<ResponseEntity<T>> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, 447 ParameterizedTypeReference<T> responseType) throws RestClientException { 448 449 Type type = responseType.getType(); 450 AsyncRequestCallback requestCallback = httpEntityCallback(requestEntity, type); 451 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); 452 return execute(url, method, requestCallback, responseExtractor); 453 } 454 455 456 // general execution 457 458 @Override 459 public <T> ListenableFuture<T> execute(String url, HttpMethod method, AsyncRequestCallback requestCallback, 460 ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException { 461 462 URI expanded = getUriTemplateHandler().expand(url, uriVariables); 463 return doExecute(expanded, method, requestCallback, responseExtractor); 464 } 465 466 @Override 467 public <T> ListenableFuture<T> execute(String url, HttpMethod method, AsyncRequestCallback requestCallback, 468 ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException { 469 470 URI expanded = getUriTemplateHandler().expand(url, uriVariables); 471 return doExecute(expanded, method, requestCallback, responseExtractor); 472 } 473 474 @Override 475 public <T> ListenableFuture<T> execute(URI url, HttpMethod method, AsyncRequestCallback requestCallback, 476 ResponseExtractor<T> responseExtractor) throws RestClientException { 477 478 return doExecute(url, method, requestCallback, responseExtractor); 479 } 480 481 /** 482 * Execute the given method on the provided URI. The 483 * {@link org.springframework.http.client.ClientHttpRequest} 484 * is processed using the {@link RequestCallback}; the response with 485 * the {@link ResponseExtractor}. 486 * @param url the fully-expanded URL to connect to 487 * @param method the HTTP method to execute (GET, POST, etc.) 488 * @param requestCallback object that prepares the request (can be {@code null}) 489 * @param responseExtractor object that extracts the return value from the response (can 490 * be {@code null}) 491 * @return an arbitrary object, as returned by the {@link ResponseExtractor} 492 */ 493 protected <T> ListenableFuture<T> doExecute(URI url, HttpMethod method, AsyncRequestCallback requestCallback, 494 ResponseExtractor<T> responseExtractor) throws RestClientException { 495 496 Assert.notNull(url, "'url' must not be null"); 497 Assert.notNull(method, "'method' must not be null"); 498 try { 499 AsyncClientHttpRequest request = createAsyncRequest(url, method); 500 if (requestCallback != null) { 501 requestCallback.doWithRequest(request); 502 } 503 ListenableFuture<ClientHttpResponse> responseFuture = request.executeAsync(); 504 return new ResponseExtractorFuture<T>(method, url, responseFuture, responseExtractor); 505 } 506 catch (IOException ex) { 507 throw new ResourceAccessException("I/O error on " + method.name() + 508 " request for \"" + url + "\":" + ex.getMessage(), ex); 509 } 510 } 511 512 private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) { 513 if (logger.isDebugEnabled()) { 514 try { 515 logger.debug("Async " + method.name() + " request for \"" + url + "\" resulted in " + 516 response.getRawStatusCode() + " (" + response.getStatusText() + ")"); 517 } 518 catch (IOException ex) { 519 // ignore 520 } 521 } 522 } 523 524 private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException { 525 if (logger.isWarnEnabled()) { 526 try { 527 logger.warn("Async " + method.name() + " request for \"" + url + "\" resulted in " + 528 response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler"); 529 } 530 catch (IOException ex) { 531 // ignore 532 } 533 } 534 getErrorHandler().handleError(response); 535 } 536 537 /** 538 * Returns a request callback implementation that prepares the request {@code Accept} 539 * headers based on the given response type and configured {@linkplain 540 * #getMessageConverters() message converters}. 541 */ 542 protected <T> AsyncRequestCallback acceptHeaderRequestCallback(Class<T> responseType) { 543 return new AsyncRequestCallbackAdapter(this.syncTemplate.acceptHeaderRequestCallback(responseType)); 544 } 545 546 /** 547 * Returns a request callback implementation that writes the given object to the 548 * request stream. 549 */ 550 protected <T> AsyncRequestCallback httpEntityCallback(HttpEntity<T> requestBody) { 551 return new AsyncRequestCallbackAdapter(this.syncTemplate.httpEntityCallback(requestBody)); 552 } 553 554 /** 555 * Returns a request callback implementation that writes the given object to the 556 * request stream. 557 */ 558 protected <T> AsyncRequestCallback httpEntityCallback(HttpEntity<T> request, Type responseType) { 559 return new AsyncRequestCallbackAdapter(this.syncTemplate.httpEntityCallback(request, responseType)); 560 } 561 562 /** 563 * Returns a response extractor for {@link ResponseEntity}. 564 */ 565 protected <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) { 566 return this.syncTemplate.responseEntityExtractor(responseType); 567 } 568 569 /** 570 * Returns a response extractor for {@link HttpHeaders}. 571 */ 572 protected ResponseExtractor<HttpHeaders> headersExtractor() { 573 return this.syncTemplate.headersExtractor(); 574 } 575 576 577 /** 578 * Future returned from 579 * {@link #doExecute(URI, HttpMethod, AsyncRequestCallback, ResponseExtractor)} 580 */ 581 private class ResponseExtractorFuture<T> extends ListenableFutureAdapter<T, ClientHttpResponse> { 582 583 private final HttpMethod method; 584 585 private final URI url; 586 587 private final ResponseExtractor<T> responseExtractor; 588 589 public ResponseExtractorFuture(HttpMethod method, URI url, 590 ListenableFuture<ClientHttpResponse> clientHttpResponseFuture, ResponseExtractor<T> responseExtractor) { 591 super(clientHttpResponseFuture); 592 this.method = method; 593 this.url = url; 594 this.responseExtractor = responseExtractor; 595 } 596 597 @Override 598 protected final T adapt(ClientHttpResponse response) throws ExecutionException { 599 try { 600 if (!getErrorHandler().hasError(response)) { 601 logResponseStatus(this.method, this.url, response); 602 } 603 else { 604 handleResponseError(this.method, this.url, response); 605 } 606 return convertResponse(response); 607 } 608 catch (Throwable ex) { 609 throw new ExecutionException(ex); 610 } 611 finally { 612 if (response != null) { 613 response.close(); 614 } 615 } 616 } 617 618 protected T convertResponse(ClientHttpResponse response) throws IOException { 619 return (this.responseExtractor != null ? this.responseExtractor.extractData(response) : null); 620 } 621 } 622 623 624 /** 625 * Adapts a {@link RequestCallback} to the {@link AsyncRequestCallback} interface. 626 */ 627 private static class AsyncRequestCallbackAdapter implements AsyncRequestCallback { 628 629 private final RequestCallback adaptee; 630 631 /** 632 * Create a new {@code AsyncRequestCallbackAdapter} from the given 633 * {@link RequestCallback}. 634 * @param requestCallback the callback to base this adapter on 635 */ 636 public AsyncRequestCallbackAdapter(RequestCallback requestCallback) { 637 this.adaptee = requestCallback; 638 } 639 640 @Override 641 public void doWithRequest(final AsyncClientHttpRequest request) throws IOException { 642 if (this.adaptee != null) { 643 this.adaptee.doWithRequest(new ClientHttpRequest() { 644 @Override 645 public ClientHttpResponse execute() throws IOException { 646 throw new UnsupportedOperationException("execute not supported"); 647 } 648 @Override 649 public OutputStream getBody() throws IOException { 650 return request.getBody(); 651 } 652 @Override 653 public HttpMethod getMethod() { 654 return request.getMethod(); 655 } 656 @Override 657 public URI getURI() { 658 return request.getURI(); 659 } 660 @Override 661 public HttpHeaders getHeaders() { 662 return request.getHeaders(); 663 } 664 }); 665 } 666 } 667 } 668 669}