001/*
002 * Copyright 2002-2020 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.lang.reflect.Type;
021import java.net.URI;
022import java.util.ArrayList;
023import java.util.LinkedHashSet;
024import java.util.LinkedList;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028import javax.xml.transform.Source;
029
030import org.springframework.core.ParameterizedTypeReference;
031import org.springframework.http.HttpEntity;
032import org.springframework.http.HttpHeaders;
033import org.springframework.http.HttpMethod;
034import org.springframework.http.MediaType;
035import org.springframework.http.RequestEntity;
036import org.springframework.http.ResponseEntity;
037import org.springframework.http.client.ClientHttpRequest;
038import org.springframework.http.client.ClientHttpRequestFactory;
039import org.springframework.http.client.ClientHttpResponse;
040import org.springframework.http.client.support.InterceptingHttpAccessor;
041import org.springframework.http.converter.ByteArrayHttpMessageConverter;
042import org.springframework.http.converter.GenericHttpMessageConverter;
043import org.springframework.http.converter.HttpMessageConverter;
044import org.springframework.http.converter.ResourceHttpMessageConverter;
045import org.springframework.http.converter.StringHttpMessageConverter;
046import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
047import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
048import org.springframework.http.converter.json.GsonHttpMessageConverter;
049import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
050import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
051import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
052import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
053import org.springframework.http.converter.xml.SourceHttpMessageConverter;
054import org.springframework.util.Assert;
055import org.springframework.util.ClassUtils;
056import org.springframework.web.util.AbstractUriTemplateHandler;
057import org.springframework.web.util.DefaultUriTemplateHandler;
058import org.springframework.web.util.UriTemplateHandler;
059
060/**
061 * <strong>Spring's central class for synchronous client-side HTTP access.</strong>
062 * It simplifies communication with HTTP servers, and enforces RESTful principles.
063 * It handles HTTP connections, leaving application code to provide URLs
064 * (with possible template variables) and extract results.
065 *
066 * <p><strong>Note:</strong> by default the RestTemplate relies on standard JDK
067 * facilities to establish HTTP connections. You can switch to use a different
068 * HTTP library such as Apache HttpComponents, Netty, and OkHttp through the
069 * {@link #setRequestFactory} property.
070 *
071 * <p>The main entry points of this template are the methods named after the six main HTTP methods:
072 * <table>
073 * <tr><th>HTTP method</th><th>RestTemplate methods</th></tr>
074 * <tr><td>DELETE</td><td>{@link #delete}</td></tr>
075 * <tr><td>GET</td><td>{@link #getForObject}</td></tr>
076 * <tr><td></td><td>{@link #getForEntity}</td></tr>
077 * <tr><td>HEAD</td><td>{@link #headForHeaders}</td></tr>
078 * <tr><td>OPTIONS</td><td>{@link #optionsForAllow}</td></tr>
079 * <tr><td>POST</td><td>{@link #postForLocation}</td></tr>
080 * <tr><td></td><td>{@link #postForObject}</td></tr>
081 * <tr><td>PUT</td><td>{@link #put}</td></tr>
082 * <tr><td>any</td><td>{@link #exchange}</td></tr>
083 * <tr><td></td><td>{@link #execute}</td></tr> </table>
084 *
085 * <p>In addition the {@code exchange} and {@code execute} methods are generalized versions of
086 * the above methods and can be used to support additional, less frequent combinations (e.g.
087 * HTTP PATCH, HTTP PUT with response body, etc.). Note however that the underlying HTTP
088 * library used must also support the desired combination.
089 *
090 * <p>For each HTTP method there are three variants: two accept a URI template string
091 * and URI variables (array or map) while a third accepts a {@link URI}.
092 * Note that for URI templates it is assumed encoding is necessary, e.g.
093 * {@code restTemplate.getForObject("https://example.com/hotel list")} becomes
094 * {@code "https://example.com/hotel%20list"}. This also means if the URI template
095 * or URI variables are already encoded, double encoding will occur, e.g.
096 * {@code https://example.com/hotel%20list} becomes
097 * {@code https://example.com/hotel%2520list}). To avoid that use a {@code URI} method
098 * variant to provide (or re-use) a previously encoded URI. To prepare such an URI
099 * with full control over encoding, consider using
100 * {@link org.springframework.web.util.UriComponentsBuilder}.
101 *
102 * <p>Internally the template uses {@link HttpMessageConverter} instances to
103 * convert HTTP messages to and from POJOs. Converters for the main mime types
104 * are registered by default but you can also register additional converters
105 * via {@link #setMessageConverters}.
106 *
107 * <p>This template uses a
108 * {@link org.springframework.http.client.SimpleClientHttpRequestFactory} and a
109 * {@link DefaultResponseErrorHandler} as default strategies for creating HTTP
110 * connections or handling HTTP errors, respectively. These defaults can be overridden
111 * through {@link #setRequestFactory} and {@link #setErrorHandler} respectively.
112 *
113 * @author Arjen Poutsma
114 * @author Brian Clozel
115 * @author Roy Clarkson
116 * @author Juergen Hoeller
117 * @since 3.0
118 * @see HttpMessageConverter
119 * @see RequestCallback
120 * @see ResponseExtractor
121 * @see ResponseErrorHandler
122 * @see AsyncRestTemplate
123 */
124public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
125
126        private static final boolean romePresent =
127                        ClassUtils.isPresent("com.rometools.rome.feed.WireFeed",
128                                        RestTemplate.class.getClassLoader());
129
130        private static final boolean jaxb2Present =
131                        ClassUtils.isPresent("javax.xml.bind.Binder",
132                                        RestTemplate.class.getClassLoader());
133
134        private static final boolean jackson2Present =
135                        ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
136                                        RestTemplate.class.getClassLoader()) &&
137                        ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
138                                        RestTemplate.class.getClassLoader());
139
140        private static final boolean jackson2XmlPresent =
141                        ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper",
142                                        RestTemplate.class.getClassLoader());
143
144        private static final boolean gsonPresent =
145                        ClassUtils.isPresent("com.google.gson.Gson",
146                                        RestTemplate.class.getClassLoader());
147
148
149        private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
150
151        private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
152
153        private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler();
154
155        private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
156
157
158        /**
159         * Create a new instance of the {@link RestTemplate} using default settings.
160         * Default {@link HttpMessageConverter}s are initialized.
161         */
162        public RestTemplate() {
163                this.messageConverters.add(new ByteArrayHttpMessageConverter());
164                this.messageConverters.add(new StringHttpMessageConverter());
165                this.messageConverters.add(new ResourceHttpMessageConverter());
166                this.messageConverters.add(new SourceHttpMessageConverter<Source>());
167                this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
168
169                if (romePresent) {
170                        this.messageConverters.add(new AtomFeedHttpMessageConverter());
171                        this.messageConverters.add(new RssChannelHttpMessageConverter());
172                }
173
174                if (jackson2XmlPresent) {
175                        this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
176                }
177                else if (jaxb2Present) {
178                        this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
179                }
180
181                if (jackson2Present) {
182                        this.messageConverters.add(new MappingJackson2HttpMessageConverter());
183                }
184                else if (gsonPresent) {
185                        this.messageConverters.add(new GsonHttpMessageConverter());
186                }
187        }
188
189        /**
190         * Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}.
191         * @param requestFactory HTTP request factory to use
192         * @see org.springframework.http.client.SimpleClientHttpRequestFactory
193         * @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
194         */
195        public RestTemplate(ClientHttpRequestFactory requestFactory) {
196                this();
197                setRequestFactory(requestFactory);
198        }
199
200        /**
201         * Create a new instance of the {@link RestTemplate} using the given list of
202         * {@link HttpMessageConverter} to use
203         * @param messageConverters the list of {@link HttpMessageConverter} to use
204         * @since 3.2.7
205         */
206        public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
207                Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
208                this.messageConverters.addAll(messageConverters);
209        }
210
211
212        /**
213         * Set the message body converters to use.
214         * <p>These converters are used to convert from and to HTTP requests and responses.
215         */
216        public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
217                Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
218                // Take getMessageConverters() List as-is when passed in here
219                if (this.messageConverters != messageConverters) {
220                        this.messageConverters.clear();
221                        this.messageConverters.addAll(messageConverters);
222                }
223        }
224
225        /**
226         * Return the list of message body converters.
227         * <p>The returned {@link List} is active and may get appended to.
228         */
229        public List<HttpMessageConverter<?>> getMessageConverters() {
230                return this.messageConverters;
231        }
232
233        /**
234         * Set the error handler.
235         * <p>By default, RestTemplate uses a {@link DefaultResponseErrorHandler}.
236         */
237        public void setErrorHandler(ResponseErrorHandler errorHandler) {
238                Assert.notNull(errorHandler, "ResponseErrorHandler must not be null");
239                this.errorHandler = errorHandler;
240        }
241
242        /**
243         * Return the error handler.
244         */
245        public ResponseErrorHandler getErrorHandler() {
246                return this.errorHandler;
247        }
248
249        /**
250         * Configure default URI variable values. This is a shortcut for:
251         * <pre class="code">
252         * DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler();
253         * handler.setDefaultUriVariables(...);
254         *
255         * RestTemplate restTemplate = new RestTemplate();
256         * restTemplate.setUriTemplateHandler(handler);
257         * </pre>
258         * @param defaultUriVariables the default URI variable values
259         * @since 4.3
260         */
261        public void setDefaultUriVariables(Map<String, ?> defaultUriVariables) {
262                Assert.isInstanceOf(AbstractUriTemplateHandler.class, this.uriTemplateHandler,
263                                "Can only use this property in conjunction with an AbstractUriTemplateHandler");
264                ((AbstractUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(defaultUriVariables);
265        }
266
267        /**
268         * Configure the {@link UriTemplateHandler} to use to expand URI templates.
269         * By default the {@link DefaultUriTemplateHandler} is used which relies on
270         * Spring's URI template support and exposes several useful properties that
271         * customize its behavior for encoding and for prepending a common base URL.
272         * An alternative implementation may be used to plug an external URI
273         * template library.
274         * @param handler the URI template handler to use
275         */
276        public void setUriTemplateHandler(UriTemplateHandler handler) {
277                Assert.notNull(handler, "UriTemplateHandler must not be null");
278                this.uriTemplateHandler = handler;
279        }
280
281        /**
282         * Return the configured URI template handler.
283         */
284        public UriTemplateHandler getUriTemplateHandler() {
285                return this.uriTemplateHandler;
286        }
287
288
289        // GET
290
291        @Override
292        public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
293                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
294                HttpMessageConverterExtractor<T> responseExtractor =
295                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
296                return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
297        }
298
299        @Override
300        public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
301                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
302                HttpMessageConverterExtractor<T> responseExtractor =
303                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
304                return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
305        }
306
307        @Override
308        public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
309                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
310                HttpMessageConverterExtractor<T> responseExtractor =
311                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
312                return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
313        }
314
315        @Override
316        public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
317                        throws RestClientException {
318
319                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
320                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
321                return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
322        }
323
324        @Override
325        public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
326                        throws RestClientException {
327
328                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
329                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
330                return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
331        }
332
333        @Override
334        public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
335                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
336                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
337                return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
338        }
339
340
341        // HEAD
342
343        @Override
344        public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException {
345                return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables);
346        }
347
348        @Override
349        public HttpHeaders headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException {
350                return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables);
351        }
352
353        @Override
354        public HttpHeaders headForHeaders(URI url) throws RestClientException {
355                return execute(url, HttpMethod.HEAD, null, headersExtractor());
356        }
357
358
359        // POST
360
361        @Override
362        public URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException {
363                RequestCallback requestCallback = httpEntityCallback(request);
364                HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
365                return headers.getLocation();
366        }
367
368        @Override
369        public URI postForLocation(String url, Object request, Map<String, ?> uriVariables) throws RestClientException {
370                RequestCallback requestCallback = httpEntityCallback(request);
371                HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
372                return headers.getLocation();
373        }
374
375        @Override
376        public URI postForLocation(URI url, Object request) throws RestClientException {
377                RequestCallback requestCallback = httpEntityCallback(request);
378                HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor());
379                return headers.getLocation();
380        }
381
382        @Override
383        public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
384                        throws RestClientException {
385
386                RequestCallback requestCallback = httpEntityCallback(request, responseType);
387                HttpMessageConverterExtractor<T> responseExtractor =
388                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
389                return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
390        }
391
392        @Override
393        public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
394                        throws RestClientException {
395
396                RequestCallback requestCallback = httpEntityCallback(request, responseType);
397                HttpMessageConverterExtractor<T> responseExtractor =
398                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
399                return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
400        }
401
402        @Override
403        public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {
404                RequestCallback requestCallback = httpEntityCallback(request, responseType);
405                HttpMessageConverterExtractor<T> responseExtractor =
406                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
407                return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
408        }
409
410        @Override
411        public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
412                        throws RestClientException {
413
414                RequestCallback requestCallback = httpEntityCallback(request, responseType);
415                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
416                return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
417        }
418
419        @Override
420        public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
421                        throws RestClientException {
422
423                RequestCallback requestCallback = httpEntityCallback(request, responseType);
424                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
425                return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
426        }
427
428        @Override
429        public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
430                RequestCallback requestCallback = httpEntityCallback(request, responseType);
431                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
432                return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
433        }
434
435
436        // PUT
437
438        @Override
439        public void put(String url, Object request, Object... uriVariables) throws RestClientException {
440                RequestCallback requestCallback = httpEntityCallback(request);
441                execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
442        }
443
444        @Override
445        public void put(String url, Object request, Map<String, ?> uriVariables) throws RestClientException {
446                RequestCallback requestCallback = httpEntityCallback(request);
447                execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
448        }
449
450        @Override
451        public void put(URI url, Object request) throws RestClientException {
452                RequestCallback requestCallback = httpEntityCallback(request);
453                execute(url, HttpMethod.PUT, requestCallback, null);
454        }
455
456
457        // PATCH
458
459        @Override
460        public <T> T patchForObject(String url, Object request, Class<T> responseType,
461                        Object... uriVariables) throws RestClientException {
462
463                RequestCallback requestCallback = httpEntityCallback(request, responseType);
464                HttpMessageConverterExtractor<T> responseExtractor =
465                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
466                return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables);
467        }
468
469        @Override
470        public <T> T patchForObject(String url, Object request, Class<T> responseType,
471                        Map<String, ?> uriVariables) throws RestClientException {
472
473                RequestCallback requestCallback = httpEntityCallback(request, responseType);
474                HttpMessageConverterExtractor<T> responseExtractor =
475                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
476                return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables);
477        }
478
479        @Override
480        public <T> T patchForObject(URI url, Object request, Class<T> responseType)
481                        throws RestClientException {
482
483                RequestCallback requestCallback = httpEntityCallback(request, responseType);
484                HttpMessageConverterExtractor<T> responseExtractor =
485                                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
486                return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor);
487        }
488
489
490        // DELETE
491
492        @Override
493        public void delete(String url, Object... uriVariables) throws RestClientException {
494                execute(url, HttpMethod.DELETE, null, null, uriVariables);
495        }
496
497        @Override
498        public void delete(String url, Map<String, ?> uriVariables) throws RestClientException {
499                execute(url, HttpMethod.DELETE, null, null, uriVariables);
500        }
501
502        @Override
503        public void delete(URI url) throws RestClientException {
504                execute(url, HttpMethod.DELETE, null, null);
505        }
506
507
508        // OPTIONS
509
510        @Override
511        public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException {
512                ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
513                HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
514                return headers.getAllow();
515        }
516
517        @Override
518        public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables) throws RestClientException {
519                ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
520                HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
521                return headers.getAllow();
522        }
523
524        @Override
525        public Set<HttpMethod> optionsForAllow(URI url) throws RestClientException {
526                ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
527                HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor);
528                return headers.getAllow();
529        }
530
531
532        // exchange
533
534        @Override
535        public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
536                        HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException {
537
538                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
539                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
540                return execute(url, method, requestCallback, responseExtractor, uriVariables);
541        }
542
543        @Override
544        public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
545                        HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
546
547                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
548                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
549                return execute(url, method, requestCallback, responseExtractor, uriVariables);
550        }
551
552        @Override
553        public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
554                        Class<T> responseType) throws RestClientException {
555
556                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
557                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
558                return execute(url, method, requestCallback, responseExtractor);
559        }
560
561        @Override
562        public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
563                        ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
564
565                Type type = responseType.getType();
566                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
567                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
568                return execute(url, method, requestCallback, responseExtractor, uriVariables);
569        }
570
571        @Override
572        public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
573                        ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
574
575                Type type = responseType.getType();
576                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
577                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
578                return execute(url, method, requestCallback, responseExtractor, uriVariables);
579        }
580
581        @Override
582        public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
583                        ParameterizedTypeReference<T> responseType) throws RestClientException {
584
585                Type type = responseType.getType();
586                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
587                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
588                return execute(url, method, requestCallback, responseExtractor);
589        }
590
591        @Override
592        public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType)
593                        throws RestClientException {
594
595                Assert.notNull(requestEntity, "RequestEntity must not be null");
596
597                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
598                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
599                return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor);
600        }
601
602        @Override
603        public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
604                        throws RestClientException {
605
606                Assert.notNull(requestEntity, "RequestEntity must not be null");
607
608                Type type = responseType.getType();
609                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
610                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
611                return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor);
612        }
613
614
615        // general execution
616
617        @Override
618        public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
619                        ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
620
621                URI expanded = getUriTemplateHandler().expand(url, uriVariables);
622                return doExecute(expanded, method, requestCallback, responseExtractor);
623        }
624
625        @Override
626        public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
627                        ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException {
628
629                URI expanded = getUriTemplateHandler().expand(url, uriVariables);
630                return doExecute(expanded, method, requestCallback, responseExtractor);
631        }
632
633        @Override
634        public <T> T execute(URI url, HttpMethod method, RequestCallback requestCallback,
635                        ResponseExtractor<T> responseExtractor) throws RestClientException {
636
637                return doExecute(url, method, requestCallback, responseExtractor);
638        }
639
640        /**
641         * Execute the given method on the provided URI.
642         * <p>The {@link ClientHttpRequest} is processed using the {@link RequestCallback};
643         * the response with the {@link ResponseExtractor}.
644         * @param url the fully-expanded URL to connect to
645         * @param method the HTTP method to execute (GET, POST, etc.)
646         * @param requestCallback object that prepares the request (can be {@code null})
647         * @param responseExtractor object that extracts the return value from the response (can be {@code null})
648         * @return an arbitrary object, as returned by the {@link ResponseExtractor}
649         */
650        protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
651                        ResponseExtractor<T> responseExtractor) throws RestClientException {
652
653                Assert.notNull(url, "'url' must not be null");
654                Assert.notNull(method, "'method' must not be null");
655                ClientHttpResponse response = null;
656                try {
657                        ClientHttpRequest request = createRequest(url, method);
658                        if (requestCallback != null) {
659                                requestCallback.doWithRequest(request);
660                        }
661                        response = request.execute();
662                        handleResponse(url, method, response);
663                        if (responseExtractor != null) {
664                                return responseExtractor.extractData(response);
665                        }
666                        else {
667                                return null;
668                        }
669                }
670                catch (IOException ex) {
671                        String resource = url.toString();
672                        String query = url.getRawQuery();
673                        resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
674                        throw new ResourceAccessException("I/O error on " + method.name() +
675                                        " request for \"" + resource + "\": " + ex.getMessage(), ex);
676                }
677                finally {
678                        if (response != null) {
679                                response.close();
680                        }
681                }
682        }
683
684        /**
685         * Handle the given response, performing appropriate logging and
686         * invoking the {@link ResponseErrorHandler} if necessary.
687         * <p>Can be overridden in subclasses.
688         * @param url the fully-expanded URL to connect to
689         * @param method the HTTP method to execute (GET, POST, etc.)
690         * @param response the resulting {@link ClientHttpResponse}
691         * @throws IOException if propagated from {@link ResponseErrorHandler}
692         * @since 4.1.6
693         * @see #setErrorHandler
694         */
695        protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
696                ResponseErrorHandler errorHandler = getErrorHandler();
697                boolean hasError = errorHandler.hasError(response);
698                if (logger.isDebugEnabled()) {
699                        try {
700                                logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
701                                                response.getRawStatusCode() + " (" + response.getStatusText() + ")" +
702                                                (hasError ? "; invoking error handler" : ""));
703                        }
704                        catch (IOException ex) {
705                                // ignore
706                        }
707                }
708                if (hasError) {
709                        errorHandler.handleError(response);
710                }
711        }
712
713        /**
714         * Returns a request callback implementation that prepares the request {@code Accept}
715         * headers based on the given response type and configured
716         * {@linkplain #getMessageConverters() message converters}.
717         */
718        protected <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) {
719                return new AcceptHeaderRequestCallback(responseType);
720        }
721
722        /**
723         * Returns a request callback implementation that writes the given object to the
724         * request stream.
725         */
726        protected <T> RequestCallback httpEntityCallback(Object requestBody) {
727                return new HttpEntityRequestCallback(requestBody);
728        }
729
730        /**
731         * Returns a request callback implementation that writes the given object to the
732         * request stream.
733         */
734        protected <T> RequestCallback httpEntityCallback(Object requestBody, Type responseType) {
735                return new HttpEntityRequestCallback(requestBody, responseType);
736        }
737
738        /**
739         * Returns a response extractor for {@link ResponseEntity}.
740         */
741        protected <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) {
742                return new ResponseEntityResponseExtractor<T>(responseType);
743        }
744
745        /**
746         * Returns a response extractor for {@link HttpHeaders}.
747         */
748        protected ResponseExtractor<HttpHeaders> headersExtractor() {
749                return this.headersExtractor;
750        }
751
752
753        /**
754         * Request callback implementation that prepares the request's accept headers.
755         */
756        private class AcceptHeaderRequestCallback implements RequestCallback {
757
758                private final Type responseType;
759
760                private AcceptHeaderRequestCallback(Type responseType) {
761                        this.responseType = responseType;
762                }
763
764                @Override
765                public void doWithRequest(ClientHttpRequest request) throws IOException {
766                        if (this.responseType != null) {
767                                Class<?> responseClass = null;
768                                if (this.responseType instanceof Class) {
769                                        responseClass = (Class<?>) this.responseType;
770                                }
771                                Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>();
772                                for (HttpMessageConverter<?> converter : getMessageConverters()) {
773                                        if (responseClass != null) {
774                                                if (converter.canRead(responseClass, null)) {
775                                                        allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter));
776                                                }
777                                        }
778                                        else if (converter instanceof GenericHttpMessageConverter) {
779                                                GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
780                                                if (genericConverter.canRead(this.responseType, null, null)) {
781                                                        allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter));
782                                                }
783                                        }
784                                }
785                                if (!allSupportedMediaTypes.isEmpty()) {
786                                        List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes);
787                                        MediaType.sortBySpecificity(result);
788                                        if (logger.isDebugEnabled()) {
789                                                logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
790                                        }
791                                        request.getHeaders().setAccept(result);
792                                }
793                        }
794                }
795
796                private List<MediaType> getSupportedMediaTypes(HttpMessageConverter<?> messageConverter) {
797                        List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
798                        List<MediaType> result = new ArrayList<MediaType>(supportedMediaTypes.size());
799                        for (MediaType supportedMediaType : supportedMediaTypes) {
800                                if (supportedMediaType.getCharset() != null) {
801                                        supportedMediaType =
802                                                        new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
803                                }
804                                result.add(supportedMediaType);
805                        }
806                        return result;
807                }
808        }
809
810
811        /**
812         * Request callback implementation that writes the given object to the request stream.
813         */
814        private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback {
815
816                private final HttpEntity<?> requestEntity;
817
818                private HttpEntityRequestCallback(Object requestBody) {
819                        this(requestBody, null);
820                }
821
822                private HttpEntityRequestCallback(Object requestBody, Type responseType) {
823                        super(responseType);
824                        if (requestBody instanceof HttpEntity) {
825                                this.requestEntity = (HttpEntity<?>) requestBody;
826                        }
827                        else if (requestBody != null) {
828                                this.requestEntity = new HttpEntity<Object>(requestBody);
829                        }
830                        else {
831                                this.requestEntity = HttpEntity.EMPTY;
832                        }
833                }
834
835                @Override
836                @SuppressWarnings("unchecked")
837                public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
838                        super.doWithRequest(httpRequest);
839                        if (!this.requestEntity.hasBody()) {
840                                HttpHeaders httpHeaders = httpRequest.getHeaders();
841                                HttpHeaders requestHeaders = this.requestEntity.getHeaders();
842                                if (!requestHeaders.isEmpty()) {
843                                        for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
844                                                httpHeaders.put(entry.getKey(), new LinkedList<String>(entry.getValue()));
845                                        }
846                                }
847                                if (httpHeaders.getContentLength() < 0) {
848                                        httpHeaders.setContentLength(0L);
849                                }
850                        }
851                        else {
852                                Object requestBody = this.requestEntity.getBody();
853                                Class<?> requestBodyClass = requestBody.getClass();
854                                Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
855                                                ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
856                                HttpHeaders httpHeaders = httpRequest.getHeaders();
857                                HttpHeaders requestHeaders = this.requestEntity.getHeaders();
858                                MediaType requestContentType = requestHeaders.getContentType();
859                                for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
860                                        if (messageConverter instanceof GenericHttpMessageConverter) {
861                                                GenericHttpMessageConverter<Object> genericMessageConverter = (GenericHttpMessageConverter<Object>) messageConverter;
862                                                if (genericMessageConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
863                                                        if (!requestHeaders.isEmpty()) {
864                                                                for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
865                                                                        httpHeaders.put(entry.getKey(), new LinkedList<String>(entry.getValue()));
866                                                                }
867                                                        }
868                                                        if (logger.isDebugEnabled()) {
869                                                                if (requestContentType != null) {
870                                                                        logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
871                                                                                        "\" using [" + messageConverter + "]");
872                                                                }
873                                                                else {
874                                                                        logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
875                                                                }
876
877                                                        }
878                                                        genericMessageConverter.write(
879                                                                        requestBody, requestBodyType, requestContentType, httpRequest);
880                                                        return;
881                                                }
882                                        }
883                                        else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
884                                                if (!requestHeaders.isEmpty()) {
885                                                        for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) {
886                                                                httpHeaders.put(entry.getKey(), new LinkedList<String>(entry.getValue()));
887                                                        }
888                                                }
889                                                if (logger.isDebugEnabled()) {
890                                                        if (requestContentType != null) {
891                                                                logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
892                                                                                "\" using [" + messageConverter + "]");
893                                                        }
894                                                        else {
895                                                                logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
896                                                        }
897
898                                                }
899                                                ((HttpMessageConverter<Object>) messageConverter).write(
900                                                                requestBody, requestContentType, httpRequest);
901                                                return;
902                                        }
903                                }
904                                String message = "Could not write request: no suitable HttpMessageConverter found for request type [" +
905                                                requestBodyClass.getName() + "]";
906                                if (requestContentType != null) {
907                                        message += " and content type [" + requestContentType + "]";
908                                }
909                                throw new RestClientException(message);
910                        }
911                }
912        }
913
914
915        /**
916         * Response extractor for {@link HttpEntity}.
917         */
918        private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> {
919
920                private final HttpMessageConverterExtractor<T> delegate;
921
922                public ResponseEntityResponseExtractor(Type responseType) {
923                        if (responseType != null && Void.class != responseType) {
924                                this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
925                        }
926                        else {
927                                this.delegate = null;
928                        }
929                }
930
931                @Override
932                public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
933                        if (this.delegate != null) {
934                                T body = this.delegate.extractData(response);
935                                return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
936                        }
937                        else {
938                                return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
939                        }
940                }
941        }
942
943
944        /**
945         * Response extractor that extracts the response {@link HttpHeaders}.
946         */
947        private static class HeadersExtractor implements ResponseExtractor<HttpHeaders> {
948
949                @Override
950                public HttpHeaders extractData(ClientHttpResponse response) throws IOException {
951                        return response.getHeaders();
952                }
953        }
954
955}