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.Collections;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.stream.Collectors;
028import java.util.stream.Stream;
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.HttpStatus;
035import org.springframework.http.MediaType;
036import org.springframework.http.RequestEntity;
037import org.springframework.http.ResponseEntity;
038import org.springframework.http.client.ClientHttpRequest;
039import org.springframework.http.client.ClientHttpRequestFactory;
040import org.springframework.http.client.ClientHttpResponse;
041import org.springframework.http.client.support.InterceptingHttpAccessor;
042import org.springframework.http.converter.ByteArrayHttpMessageConverter;
043import org.springframework.http.converter.GenericHttpMessageConverter;
044import org.springframework.http.converter.HttpMessageConverter;
045import org.springframework.http.converter.ResourceHttpMessageConverter;
046import org.springframework.http.converter.StringHttpMessageConverter;
047import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
048import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
049import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
050import org.springframework.http.converter.json.GsonHttpMessageConverter;
051import org.springframework.http.converter.json.JsonbHttpMessageConverter;
052import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
053import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
054import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
055import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
056import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
057import org.springframework.http.converter.xml.SourceHttpMessageConverter;
058import org.springframework.lang.Nullable;
059import org.springframework.util.Assert;
060import org.springframework.util.ClassUtils;
061import org.springframework.web.util.DefaultUriBuilderFactory;
062import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
063import org.springframework.web.util.UriTemplateHandler;
064
065/**
066 * Synchronous client to perform HTTP requests, exposing a simple, template
067 * method API over underlying HTTP client libraries such as the JDK
068 * {@code HttpURLConnection}, Apache HttpComponents, and others.
069 *
070 * <p>The RestTemplate offers templates for common scenarios by HTTP method, in
071 * addition to the generalized {@code exchange} and {@code execute} methods that
072 * support of less frequent cases.
073 *
074 * <p><strong>NOTE:</strong> As of 5.0 this class is in maintenance mode, with
075 * only minor requests for changes and bugs to be accepted going forward. Please,
076 * consider using the {@code org.springframework.web.reactive.client.WebClient}
077 * which has a more modern API and supports sync, async, and streaming scenarios.
078 *
079 * @author Arjen Poutsma
080 * @author Brian Clozel
081 * @author Roy Clarkson
082 * @author Juergen Hoeller
083 * @author Sam Brannen
084 * @since 3.0
085 * @see HttpMessageConverter
086 * @see RequestCallback
087 * @see ResponseExtractor
088 * @see ResponseErrorHandler
089 */
090public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
091
092        private static final boolean romePresent;
093
094        private static final boolean jaxb2Present;
095
096        private static final boolean jackson2Present;
097
098        private static final boolean jackson2XmlPresent;
099
100        private static final boolean jackson2SmilePresent;
101
102        private static final boolean jackson2CborPresent;
103
104        private static final boolean gsonPresent;
105
106        private static final boolean jsonbPresent;
107
108        static {
109                ClassLoader classLoader = RestTemplate.class.getClassLoader();
110                romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
111                jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
112                jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
113                                ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
114                jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
115                jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
116                jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
117                gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
118                jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
119        }
120
121
122        private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
123
124        private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
125
126        private UriTemplateHandler uriTemplateHandler;
127
128        private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
129
130
131        /**
132         * Create a new instance of the {@link RestTemplate} using default settings.
133         * Default {@link HttpMessageConverter HttpMessageConverters} are initialized.
134         */
135        public RestTemplate() {
136                this.messageConverters.add(new ByteArrayHttpMessageConverter());
137                this.messageConverters.add(new StringHttpMessageConverter());
138                this.messageConverters.add(new ResourceHttpMessageConverter(false));
139                try {
140                        this.messageConverters.add(new SourceHttpMessageConverter<>());
141                }
142                catch (Error err) {
143                        // Ignore when no TransformerFactory implementation is available
144                }
145                this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
146
147                if (romePresent) {
148                        this.messageConverters.add(new AtomFeedHttpMessageConverter());
149                        this.messageConverters.add(new RssChannelHttpMessageConverter());
150                }
151
152                if (jackson2XmlPresent) {
153                        this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
154                }
155                else if (jaxb2Present) {
156                        this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
157                }
158
159                if (jackson2Present) {
160                        this.messageConverters.add(new MappingJackson2HttpMessageConverter());
161                }
162                else if (gsonPresent) {
163                        this.messageConverters.add(new GsonHttpMessageConverter());
164                }
165                else if (jsonbPresent) {
166                        this.messageConverters.add(new JsonbHttpMessageConverter());
167                }
168
169                if (jackson2SmilePresent) {
170                        this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
171                }
172                if (jackson2CborPresent) {
173                        this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
174                }
175
176                this.uriTemplateHandler = initUriTemplateHandler();
177        }
178
179        /**
180         * Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}.
181         * @param requestFactory the HTTP request factory to use
182         * @see org.springframework.http.client.SimpleClientHttpRequestFactory
183         * @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
184         */
185        public RestTemplate(ClientHttpRequestFactory requestFactory) {
186                this();
187                setRequestFactory(requestFactory);
188        }
189
190        /**
191         * Create a new instance of the {@link RestTemplate} using the given list of
192         * {@link HttpMessageConverter} to use.
193         * @param messageConverters the list of {@link HttpMessageConverter} to use
194         * @since 3.2.7
195         */
196        public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
197                validateConverters(messageConverters);
198                this.messageConverters.addAll(messageConverters);
199                this.uriTemplateHandler = initUriTemplateHandler();
200        }
201
202
203        private static DefaultUriBuilderFactory initUriTemplateHandler() {
204                DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
205                uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT);  // for backwards compatibility..
206                return uriFactory;
207        }
208
209
210        /**
211         * Set the message body converters to use.
212         * <p>These converters are used to convert from and to HTTP requests and responses.
213         */
214        public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
215                validateConverters(messageConverters);
216                // Take getMessageConverters() List as-is when passed in here
217                if (this.messageConverters != messageConverters) {
218                        this.messageConverters.clear();
219                        this.messageConverters.addAll(messageConverters);
220                }
221        }
222
223        private void validateConverters(List<HttpMessageConverter<?>> messageConverters) {
224                Assert.notEmpty(messageConverters, "At least one HttpMessageConverter is required");
225                Assert.noNullElements(messageConverters, "The HttpMessageConverter list must not contain null elements");
226        }
227
228        /**
229         * Return the list of message body converters.
230         * <p>The returned {@link List} is active and may get appended to.
231         */
232        public List<HttpMessageConverter<?>> getMessageConverters() {
233                return this.messageConverters;
234        }
235
236        /**
237         * Set the error handler.
238         * <p>By default, RestTemplate uses a {@link DefaultResponseErrorHandler}.
239         */
240        public void setErrorHandler(ResponseErrorHandler errorHandler) {
241                Assert.notNull(errorHandler, "ResponseErrorHandler must not be null");
242                this.errorHandler = errorHandler;
243        }
244
245        /**
246         * Return the error handler.
247         */
248        public ResponseErrorHandler getErrorHandler() {
249                return this.errorHandler;
250        }
251
252        /**
253         * Configure default URI variable values. This is a shortcut for:
254         * <pre class="code">
255         * DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
256         * handler.setDefaultUriVariables(...);
257         *
258         * RestTemplate restTemplate = new RestTemplate();
259         * restTemplate.setUriTemplateHandler(handler);
260         * </pre>
261         * @param uriVars the default URI variable values
262         * @since 4.3
263         */
264        @SuppressWarnings("deprecation")
265        public void setDefaultUriVariables(Map<String, ?> uriVars) {
266                if (this.uriTemplateHandler instanceof DefaultUriBuilderFactory) {
267                        ((DefaultUriBuilderFactory) this.uriTemplateHandler).setDefaultUriVariables(uriVars);
268                }
269                else if (this.uriTemplateHandler instanceof org.springframework.web.util.AbstractUriTemplateHandler) {
270                        ((org.springframework.web.util.AbstractUriTemplateHandler) this.uriTemplateHandler)
271                                        .setDefaultUriVariables(uriVars);
272                }
273                else {
274                        throw new IllegalArgumentException(
275                                        "This property is not supported with the configured UriTemplateHandler.");
276                }
277        }
278
279        /**
280         * Configure a strategy for expanding URI templates.
281         * <p>By default, {@link DefaultUriBuilderFactory} is used and for
282         * backwards compatibility, the encoding mode is set to
283         * {@link EncodingMode#URI_COMPONENT URI_COMPONENT}. As of 5.0.8, prefer
284         * using {@link EncodingMode#TEMPLATE_AND_VALUES TEMPLATE_AND_VALUES}.
285         * <p><strong>Note:</strong> in 5.0 the switch from
286         * {@link org.springframework.web.util.DefaultUriTemplateHandler
287         * DefaultUriTemplateHandler} (deprecated in 4.3), as the default to use, to
288         * {@link DefaultUriBuilderFactory} brings in a different default for the
289         * {@code parsePath} property (switching from false to true).
290         * @param handler the URI template handler to use
291         */
292        public void setUriTemplateHandler(UriTemplateHandler handler) {
293                Assert.notNull(handler, "UriTemplateHandler must not be null");
294                this.uriTemplateHandler = handler;
295        }
296
297        /**
298         * Return the configured URI template handler.
299         */
300        public UriTemplateHandler getUriTemplateHandler() {
301                return this.uriTemplateHandler;
302        }
303
304
305        // GET
306
307        @Override
308        @Nullable
309        public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
310                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
311                HttpMessageConverterExtractor<T> responseExtractor =
312                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
313                return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
314        }
315
316        @Override
317        @Nullable
318        public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
319                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
320                HttpMessageConverterExtractor<T> responseExtractor =
321                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
322                return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
323        }
324
325        @Override
326        @Nullable
327        public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
328                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
329                HttpMessageConverterExtractor<T> responseExtractor =
330                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
331                return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
332        }
333
334        @Override
335        public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
336                        throws RestClientException {
337
338                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
339                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
340                return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
341        }
342
343        @Override
344        public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
345                        throws RestClientException {
346
347                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
348                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
349                return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
350        }
351
352        @Override
353        public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
354                RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
355                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
356                return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor));
357        }
358
359
360        // HEAD
361
362        @Override
363        public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException {
364                return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables));
365        }
366
367        @Override
368        public HttpHeaders headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException {
369                return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables));
370        }
371
372        @Override
373        public HttpHeaders headForHeaders(URI url) throws RestClientException {
374                return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor()));
375        }
376
377
378        // POST
379
380        @Override
381        @Nullable
382        public URI postForLocation(String url, @Nullable Object request, Object... uriVariables)
383                        throws RestClientException {
384
385                RequestCallback requestCallback = httpEntityCallback(request);
386                HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
387                return (headers != null ? headers.getLocation() : null);
388        }
389
390        @Override
391        @Nullable
392        public URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVariables)
393                        throws RestClientException {
394
395                RequestCallback requestCallback = httpEntityCallback(request);
396                HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
397                return (headers != null ? headers.getLocation() : null);
398        }
399
400        @Override
401        @Nullable
402        public URI postForLocation(URI url, @Nullable Object request) throws RestClientException {
403                RequestCallback requestCallback = httpEntityCallback(request);
404                HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor());
405                return (headers != null ? headers.getLocation() : null);
406        }
407
408        @Override
409        @Nullable
410        public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
411                        Object... uriVariables) throws RestClientException {
412
413                RequestCallback requestCallback = httpEntityCallback(request, responseType);
414                HttpMessageConverterExtractor<T> responseExtractor =
415                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
416                return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
417        }
418
419        @Override
420        @Nullable
421        public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
422                        Map<String, ?> uriVariables) throws RestClientException {
423
424                RequestCallback requestCallback = httpEntityCallback(request, responseType);
425                HttpMessageConverterExtractor<T> responseExtractor =
426                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
427                return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
428        }
429
430        @Override
431        @Nullable
432        public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType)
433                        throws RestClientException {
434
435                RequestCallback requestCallback = httpEntityCallback(request, responseType);
436                HttpMessageConverterExtractor<T> responseExtractor =
437                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters());
438                return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
439        }
440
441        @Override
442        public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,
443                        Class<T> responseType, Object... uriVariables) throws RestClientException {
444
445                RequestCallback requestCallback = httpEntityCallback(request, responseType);
446                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
447                return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));
448        }
449
450        @Override
451        public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,
452                        Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
453
454                RequestCallback requestCallback = httpEntityCallback(request, responseType);
455                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
456                return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));
457        }
458
459        @Override
460        public <T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType)
461                        throws RestClientException {
462
463                RequestCallback requestCallback = httpEntityCallback(request, responseType);
464                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
465                return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor));
466        }
467
468
469        // PUT
470
471        @Override
472        public void put(String url, @Nullable Object request, Object... uriVariables)
473                        throws RestClientException {
474
475                RequestCallback requestCallback = httpEntityCallback(request);
476                execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
477        }
478
479        @Override
480        public void put(String url, @Nullable Object request, Map<String, ?> uriVariables)
481                        throws RestClientException {
482
483                RequestCallback requestCallback = httpEntityCallback(request);
484                execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
485        }
486
487        @Override
488        public void put(URI url, @Nullable Object request) throws RestClientException {
489                RequestCallback requestCallback = httpEntityCallback(request);
490                execute(url, HttpMethod.PUT, requestCallback, null);
491        }
492
493
494        // PATCH
495
496        @Override
497        @Nullable
498        public <T> T patchForObject(String url, @Nullable Object request, Class<T> responseType,
499                        Object... uriVariables) throws RestClientException {
500
501                RequestCallback requestCallback = httpEntityCallback(request, responseType);
502                HttpMessageConverterExtractor<T> responseExtractor =
503                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
504                return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables);
505        }
506
507        @Override
508        @Nullable
509        public <T> T patchForObject(String url, @Nullable Object request, Class<T> responseType,
510                        Map<String, ?> uriVariables) throws RestClientException {
511
512                RequestCallback requestCallback = httpEntityCallback(request, responseType);
513                HttpMessageConverterExtractor<T> responseExtractor =
514                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
515                return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables);
516        }
517
518        @Override
519        @Nullable
520        public <T> T patchForObject(URI url, @Nullable Object request, Class<T> responseType)
521                        throws RestClientException {
522
523                RequestCallback requestCallback = httpEntityCallback(request, responseType);
524                HttpMessageConverterExtractor<T> responseExtractor =
525                                new HttpMessageConverterExtractor<>(responseType, getMessageConverters());
526                return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor);
527        }
528
529
530        // DELETE
531
532        @Override
533        public void delete(String url, Object... uriVariables) throws RestClientException {
534                execute(url, HttpMethod.DELETE, null, null, uriVariables);
535        }
536
537        @Override
538        public void delete(String url, Map<String, ?> uriVariables) throws RestClientException {
539                execute(url, HttpMethod.DELETE, null, null, uriVariables);
540        }
541
542        @Override
543        public void delete(URI url) throws RestClientException {
544                execute(url, HttpMethod.DELETE, null, null);
545        }
546
547
548        // OPTIONS
549
550        @Override
551        public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException {
552                ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
553                HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
554                return (headers != null ? headers.getAllow() : Collections.emptySet());
555        }
556
557        @Override
558        public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables) throws RestClientException {
559                ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
560                HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
561                return (headers != null ? headers.getAllow() : Collections.emptySet());
562        }
563
564        @Override
565        public Set<HttpMethod> optionsForAllow(URI url) throws RestClientException {
566                ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
567                HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor);
568                return (headers != null ? headers.getAllow() : Collections.emptySet());
569        }
570
571
572        // exchange
573
574        @Override
575        public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
576                        @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
577                        throws RestClientException {
578
579                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
580                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
581                return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
582        }
583
584        @Override
585        public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
586                        @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables)
587                        throws RestClientException {
588
589                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
590                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
591                return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
592        }
593
594        @Override
595        public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
596                        Class<T> responseType) throws RestClientException {
597
598                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
599                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
600                return nonNull(execute(url, method, requestCallback, responseExtractor));
601        }
602
603        @Override
604        public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
605                        ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
606
607                Type type = responseType.getType();
608                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
609                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
610                return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
611        }
612
613        @Override
614        public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
615                        ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
616
617                Type type = responseType.getType();
618                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
619                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
620                return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
621        }
622
623        @Override
624        public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
625                        ParameterizedTypeReference<T> responseType) throws RestClientException {
626
627                Type type = responseType.getType();
628                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
629                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
630                return nonNull(execute(url, method, requestCallback, responseExtractor));
631        }
632
633        @Override
634        public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType)
635                        throws RestClientException {
636
637                RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
638                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
639                return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor));
640        }
641
642        @Override
643        public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
644                        throws RestClientException {
645
646                Type type = responseType.getType();
647                RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
648                ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
649                return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor));
650        }
651
652
653        // General execution
654
655        /**
656         * {@inheritDoc}
657         * <p>To provide a {@code RequestCallback} or {@code ResponseExtractor} only,
658         * but not both, consider using:
659         * <ul>
660         * <li>{@link #acceptHeaderRequestCallback(Class)}
661         * <li>{@link #httpEntityCallback(Object)}
662         * <li>{@link #httpEntityCallback(Object, Type)}
663         * <li>{@link #responseEntityExtractor(Type)}
664         * </ul>
665         */
666        @Override
667        @Nullable
668        public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
669                        @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
670
671                URI expanded = getUriTemplateHandler().expand(url, uriVariables);
672                return doExecute(expanded, method, requestCallback, responseExtractor);
673        }
674
675        /**
676         * {@inheritDoc}
677         * <p>To provide a {@code RequestCallback} or {@code ResponseExtractor} only,
678         * but not both, consider using:
679         * <ul>
680         * <li>{@link #acceptHeaderRequestCallback(Class)}
681         * <li>{@link #httpEntityCallback(Object)}
682         * <li>{@link #httpEntityCallback(Object, Type)}
683         * <li>{@link #responseEntityExtractor(Type)}
684         * </ul>
685         */
686        @Override
687        @Nullable
688        public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
689                        @Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables)
690                        throws RestClientException {
691
692                URI expanded = getUriTemplateHandler().expand(url, uriVariables);
693                return doExecute(expanded, method, requestCallback, responseExtractor);
694        }
695
696        /**
697         * {@inheritDoc}
698         * <p>To provide a {@code RequestCallback} or {@code ResponseExtractor} only,
699         * but not both, consider using:
700         * <ul>
701         * <li>{@link #acceptHeaderRequestCallback(Class)}
702         * <li>{@link #httpEntityCallback(Object)}
703         * <li>{@link #httpEntityCallback(Object, Type)}
704         * <li>{@link #responseEntityExtractor(Type)}
705         * </ul>
706         */
707        @Override
708        @Nullable
709        public <T> T execute(URI url, HttpMethod method, @Nullable RequestCallback requestCallback,
710                        @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
711
712                return doExecute(url, method, requestCallback, responseExtractor);
713        }
714
715        /**
716         * Execute the given method on the provided URI.
717         * <p>The {@link ClientHttpRequest} is processed using the {@link RequestCallback};
718         * the response with the {@link ResponseExtractor}.
719         * @param url the fully-expanded URL to connect to
720         * @param method the HTTP method to execute (GET, POST, etc.)
721         * @param requestCallback object that prepares the request (can be {@code null})
722         * @param responseExtractor object that extracts the return value from the response (can be {@code null})
723         * @return an arbitrary object, as returned by the {@link ResponseExtractor}
724         */
725        @Nullable
726        protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
727                        @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
728
729                Assert.notNull(url, "URI is required");
730                Assert.notNull(method, "HttpMethod is required");
731                ClientHttpResponse response = null;
732                try {
733                        ClientHttpRequest request = createRequest(url, method);
734                        if (requestCallback != null) {
735                                requestCallback.doWithRequest(request);
736                        }
737                        response = request.execute();
738                        handleResponse(url, method, response);
739                        return (responseExtractor != null ? responseExtractor.extractData(response) : null);
740                }
741                catch (IOException ex) {
742                        String resource = url.toString();
743                        String query = url.getRawQuery();
744                        resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
745                        throw new ResourceAccessException("I/O error on " + method.name() +
746                                        " request for \"" + resource + "\": " + ex.getMessage(), ex);
747                }
748                finally {
749                        if (response != null) {
750                                response.close();
751                        }
752                }
753        }
754
755        /**
756         * Handle the given response, performing appropriate logging and
757         * invoking the {@link ResponseErrorHandler} if necessary.
758         * <p>Can be overridden in subclasses.
759         * @param url the fully-expanded URL to connect to
760         * @param method the HTTP method to execute (GET, POST, etc.)
761         * @param response the resulting {@link ClientHttpResponse}
762         * @throws IOException if propagated from {@link ResponseErrorHandler}
763         * @since 4.1.6
764         * @see #setErrorHandler
765         */
766        protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
767                ResponseErrorHandler errorHandler = getErrorHandler();
768                boolean hasError = errorHandler.hasError(response);
769                if (logger.isDebugEnabled()) {
770                        try {
771                                int code = response.getRawStatusCode();
772                                HttpStatus status = HttpStatus.resolve(code);
773                                logger.debug("Response " + (status != null ? status : code));
774                        }
775                        catch (IOException ex) {
776                                // ignore
777                        }
778                }
779                if (hasError) {
780                        errorHandler.handleError(url, method, response);
781                }
782        }
783
784        /**
785         * Return a {@code RequestCallback} that sets the request {@code Accept}
786         * header based on the given response type, cross-checked against the
787         * configured message converters.
788         */
789        public <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) {
790                return new AcceptHeaderRequestCallback(responseType);
791        }
792
793        /**
794         * Return a {@code RequestCallback} implementation that writes the given
795         * object to the request stream.
796         */
797        public <T> RequestCallback httpEntityCallback(@Nullable Object requestBody) {
798                return new HttpEntityRequestCallback(requestBody);
799        }
800
801        /**
802         * Return a {@code RequestCallback} implementation that:
803         * <ol>
804         * <li>Sets the request {@code Accept} header based on the given response
805         * type, cross-checked against the configured message converters.
806         * <li>Writes the given object to the request stream.
807         * </ol>
808         */
809        public <T> RequestCallback httpEntityCallback(@Nullable Object requestBody, Type responseType) {
810                return new HttpEntityRequestCallback(requestBody, responseType);
811        }
812
813        /**
814         * Return a {@code ResponseExtractor} that prepares a {@link ResponseEntity}.
815         */
816        public <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) {
817                return new ResponseEntityResponseExtractor<>(responseType);
818        }
819
820        /**
821         * Return a response extractor for {@link HttpHeaders}.
822         */
823        protected ResponseExtractor<HttpHeaders> headersExtractor() {
824                return this.headersExtractor;
825        }
826
827        private static <T> T nonNull(@Nullable T result) {
828                Assert.state(result != null, "No result");
829                return result;
830        }
831
832
833        /**
834         * Request callback implementation that prepares the request's accept headers.
835         */
836        private class AcceptHeaderRequestCallback implements RequestCallback {
837
838                @Nullable
839                private final Type responseType;
840
841                public AcceptHeaderRequestCallback(@Nullable Type responseType) {
842                        this.responseType = responseType;
843                }
844
845                @Override
846                public void doWithRequest(ClientHttpRequest request) throws IOException {
847                        if (this.responseType != null) {
848                                List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
849                                                .filter(converter -> canReadResponse(this.responseType, converter))
850                                                .flatMap(this::getSupportedMediaTypes)
851                                                .distinct()
852                                                .sorted(MediaType.SPECIFICITY_COMPARATOR)
853                                                .collect(Collectors.toList());
854                                if (logger.isDebugEnabled()) {
855                                        logger.debug("Accept=" + allSupportedMediaTypes);
856                                }
857                                request.getHeaders().setAccept(allSupportedMediaTypes);
858                        }
859                }
860
861                private boolean canReadResponse(Type responseType, HttpMessageConverter<?> converter) {
862                        Class<?> responseClass = (responseType instanceof Class ? (Class<?>) responseType : null);
863                        if (responseClass != null) {
864                                return converter.canRead(responseClass, null);
865                        }
866                        else if (converter instanceof GenericHttpMessageConverter) {
867                                GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
868                                return genericConverter.canRead(responseType, null, null);
869                        }
870                        return false;
871                }
872
873                private Stream<MediaType> getSupportedMediaTypes(HttpMessageConverter<?> messageConverter) {
874                        return messageConverter.getSupportedMediaTypes()
875                                        .stream()
876                                        .map(mediaType -> {
877                                                if (mediaType.getCharset() != null) {
878                                                        return new MediaType(mediaType.getType(), mediaType.getSubtype());
879                                                }
880                                                return mediaType;
881                                        });
882                }
883        }
884
885
886        /**
887         * Request callback implementation that writes the given object to the request stream.
888         */
889        private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback {
890
891                private final HttpEntity<?> requestEntity;
892
893                public HttpEntityRequestCallback(@Nullable Object requestBody) {
894                        this(requestBody, null);
895                }
896
897                public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable Type responseType) {
898                        super(responseType);
899                        if (requestBody instanceof HttpEntity) {
900                                this.requestEntity = (HttpEntity<?>) requestBody;
901                        }
902                        else if (requestBody != null) {
903                                this.requestEntity = new HttpEntity<>(requestBody);
904                        }
905                        else {
906                                this.requestEntity = HttpEntity.EMPTY;
907                        }
908                }
909
910                @Override
911                @SuppressWarnings("unchecked")
912                public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
913                        super.doWithRequest(httpRequest);
914                        Object requestBody = this.requestEntity.getBody();
915                        if (requestBody == null) {
916                                HttpHeaders httpHeaders = httpRequest.getHeaders();
917                                HttpHeaders requestHeaders = this.requestEntity.getHeaders();
918                                if (!requestHeaders.isEmpty()) {
919                                        requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
920                                }
921                                if (httpHeaders.getContentLength() < 0) {
922                                        httpHeaders.setContentLength(0L);
923                                }
924                        }
925                        else {
926                                Class<?> requestBodyClass = requestBody.getClass();
927                                Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
928                                                ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
929                                HttpHeaders httpHeaders = httpRequest.getHeaders();
930                                HttpHeaders requestHeaders = this.requestEntity.getHeaders();
931                                MediaType requestContentType = requestHeaders.getContentType();
932                                for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
933                                        if (messageConverter instanceof GenericHttpMessageConverter) {
934                                                GenericHttpMessageConverter<Object> genericConverter =
935                                                                (GenericHttpMessageConverter<Object>) messageConverter;
936                                                if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
937                                                        if (!requestHeaders.isEmpty()) {
938                                                                requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
939                                                        }
940                                                        logBody(requestBody, requestContentType, genericConverter);
941                                                        genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);
942                                                        return;
943                                                }
944                                        }
945                                        else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
946                                                if (!requestHeaders.isEmpty()) {
947                                                        requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
948                                                }
949                                                logBody(requestBody, requestContentType, messageConverter);
950                                                ((HttpMessageConverter<Object>) messageConverter).write(
951                                                                requestBody, requestContentType, httpRequest);
952                                                return;
953                                        }
954                                }
955                                String message = "No HttpMessageConverter for " + requestBodyClass.getName();
956                                if (requestContentType != null) {
957                                        message += " and content type \"" + requestContentType + "\"";
958                                }
959                                throw new RestClientException(message);
960                        }
961                }
962
963                private void logBody(Object body, @Nullable MediaType mediaType, HttpMessageConverter<?> converter) {
964                        if (logger.isDebugEnabled()) {
965                                if (mediaType != null) {
966                                        logger.debug("Writing [" + body + "] as \"" + mediaType + "\"");
967                                }
968                                else {
969                                        logger.debug("Writing [" + body + "] with " + converter.getClass().getName());
970                                }
971                        }
972                }
973        }
974
975
976        /**
977         * Response extractor for {@link HttpEntity}.
978         */
979        private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> {
980
981                @Nullable
982                private final HttpMessageConverterExtractor<T> delegate;
983
984                public ResponseEntityResponseExtractor(@Nullable Type responseType) {
985                        if (responseType != null && Void.class != responseType) {
986                                this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
987                        }
988                        else {
989                                this.delegate = null;
990                        }
991                }
992
993                @Override
994                public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
995                        if (this.delegate != null) {
996                                T body = this.delegate.extractData(response);
997                                return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
998                        }
999                        else {
1000                                return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
1001                        }
1002                }
1003        }
1004
1005
1006        /**
1007         * Response extractor that extracts the response {@link HttpHeaders}.
1008         */
1009        private static class HeadersExtractor implements ResponseExtractor<HttpHeaders> {
1010
1011                @Override
1012                public HttpHeaders extractData(ClientHttpResponse response) {
1013                        return response.getHeaders();
1014                }
1015        }
1016
1017}