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