001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://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.boot.web.client;
018
019import java.lang.reflect.Constructor;
020import java.lang.reflect.Field;
021import java.lang.reflect.Method;
022import java.time.Duration;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.LinkedHashSet;
028import java.util.Set;
029import java.util.function.Consumer;
030import java.util.function.Supplier;
031
032import org.springframework.beans.BeanUtils;
033import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper;
034import org.springframework.http.client.ClientHttpRequestFactory;
035import org.springframework.http.client.ClientHttpRequestInterceptor;
036import org.springframework.http.client.support.BasicAuthenticationInterceptor;
037import org.springframework.http.converter.HttpMessageConverter;
038import org.springframework.util.Assert;
039import org.springframework.util.CollectionUtils;
040import org.springframework.util.ReflectionUtils;
041import org.springframework.web.client.ResponseErrorHandler;
042import org.springframework.web.client.RestTemplate;
043import org.springframework.web.util.UriTemplateHandler;
044
045/**
046 * Builder that can be used to configure and create a {@link RestTemplate}. Provides
047 * convenience methods to register {@link #messageConverters(HttpMessageConverter...)
048 * converters}, {@link #errorHandler(ResponseErrorHandler) error handlers} and
049 * {@link #uriTemplateHandler(UriTemplateHandler) UriTemplateHandlers}.
050 * <p>
051 * By default the built {@link RestTemplate} will attempt to use the most suitable
052 * {@link ClientHttpRequestFactory}, call {@link #detectRequestFactory(boolean)
053 * detectRequestFactory(false)} if you prefer to keep the default. In a typical
054 * auto-configured Spring Boot application this builder is available as a bean and can be
055 * injected whenever a {@link RestTemplate} is needed.
056 *
057 * @author Stephane Nicoll
058 * @author Phillip Webb
059 * @author Andy Wilkinson
060 * @author Brian Clozel
061 * @since 1.4.0
062 */
063public class RestTemplateBuilder {
064
065        private final boolean detectRequestFactory;
066
067        private final String rootUri;
068
069        private final Set<HttpMessageConverter<?>> messageConverters;
070
071        private final Supplier<ClientHttpRequestFactory> requestFactorySupplier;
072
073        private final UriTemplateHandler uriTemplateHandler;
074
075        private final ResponseErrorHandler errorHandler;
076
077        private final BasicAuthenticationInterceptor basicAuthentication;
078
079        private final Set<RestTemplateCustomizer> restTemplateCustomizers;
080
081        private final RequestFactoryCustomizer requestFactoryCustomizer;
082
083        private final Set<ClientHttpRequestInterceptor> interceptors;
084
085        /**
086         * Create a new {@link RestTemplateBuilder} instance.
087         * @param customizers any {@link RestTemplateCustomizer RestTemplateCustomizers} that
088         * should be applied when the {@link RestTemplate} is built
089         */
090        public RestTemplateBuilder(RestTemplateCustomizer... customizers) {
091                Assert.notNull(customizers, "Customizers must not be null");
092                this.detectRequestFactory = true;
093                this.rootUri = null;
094                this.messageConverters = null;
095                this.requestFactorySupplier = null;
096                this.uriTemplateHandler = null;
097                this.errorHandler = null;
098                this.basicAuthentication = null;
099                this.restTemplateCustomizers = Collections
100                                .unmodifiableSet(new LinkedHashSet<>(Arrays.asList(customizers)));
101                this.requestFactoryCustomizer = new RequestFactoryCustomizer();
102                this.interceptors = Collections.emptySet();
103        }
104
105        private RestTemplateBuilder(boolean detectRequestFactory, String rootUri,
106                        Set<HttpMessageConverter<?>> messageConverters,
107                        Supplier<ClientHttpRequestFactory> requestFactorySupplier,
108                        UriTemplateHandler uriTemplateHandler, ResponseErrorHandler errorHandler,
109                        BasicAuthenticationInterceptor basicAuthentication,
110                        Set<RestTemplateCustomizer> restTemplateCustomizers,
111                        RequestFactoryCustomizer requestFactoryCustomizer,
112                        Set<ClientHttpRequestInterceptor> interceptors) {
113                this.detectRequestFactory = detectRequestFactory;
114                this.rootUri = rootUri;
115                this.messageConverters = messageConverters;
116                this.requestFactorySupplier = requestFactorySupplier;
117                this.uriTemplateHandler = uriTemplateHandler;
118                this.errorHandler = errorHandler;
119                this.basicAuthentication = basicAuthentication;
120                this.restTemplateCustomizers = restTemplateCustomizers;
121                this.requestFactoryCustomizer = requestFactoryCustomizer;
122                this.interceptors = interceptors;
123        }
124
125        /**
126         * Set if the {@link ClientHttpRequestFactory} should be detected based on the
127         * classpath. Default if {@code true}.
128         * @param detectRequestFactory if the {@link ClientHttpRequestFactory} should be
129         * detected
130         * @return a new builder instance
131         */
132        public RestTemplateBuilder detectRequestFactory(boolean detectRequestFactory) {
133                return new RestTemplateBuilder(detectRequestFactory, this.rootUri,
134                                this.messageConverters, this.requestFactorySupplier,
135                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
136                                this.restTemplateCustomizers, this.requestFactoryCustomizer,
137                                this.interceptors);
138        }
139
140        /**
141         * Set a root URL that should be applied to each request that starts with {@code '/'}.
142         * See {@link RootUriTemplateHandler} for details.
143         * @param rootUri the root URI or {@code null}
144         * @return a new builder instance
145         */
146        public RestTemplateBuilder rootUri(String rootUri) {
147                return new RestTemplateBuilder(this.detectRequestFactory, rootUri,
148                                this.messageConverters, this.requestFactorySupplier,
149                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
150                                this.restTemplateCustomizers, this.requestFactoryCustomizer,
151                                this.interceptors);
152        }
153
154        /**
155         * Set the {@link HttpMessageConverter HttpMessageConverters} that should be used with
156         * the {@link RestTemplate}. Setting this value will replace any previously configured
157         * converters and any converters configured on the builder will replace RestTemplate's
158         * default converters.
159         * @param messageConverters the converters to set
160         * @return a new builder instance
161         * @see #additionalMessageConverters(HttpMessageConverter...)
162         */
163        public RestTemplateBuilder messageConverters(
164                        HttpMessageConverter<?>... messageConverters) {
165                Assert.notNull(messageConverters, "MessageConverters must not be null");
166                return messageConverters(Arrays.asList(messageConverters));
167        }
168
169        /**
170         * Set the {@link HttpMessageConverter HttpMessageConverters} that should be used with
171         * the {@link RestTemplate}. Setting this value will replace any previously configured
172         * converters and any converters configured on the builder will replace RestTemplate's
173         * default converters.
174         * @param messageConverters the converters to set
175         * @return a new builder instance
176         * @see #additionalMessageConverters(HttpMessageConverter...)
177         */
178        public RestTemplateBuilder messageConverters(
179                        Collection<? extends HttpMessageConverter<?>> messageConverters) {
180                Assert.notNull(messageConverters, "MessageConverters must not be null");
181                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
182                                Collections.unmodifiableSet(
183                                                new LinkedHashSet<HttpMessageConverter<?>>(messageConverters)),
184                                this.requestFactorySupplier, this.uriTemplateHandler, this.errorHandler,
185                                this.basicAuthentication, this.restTemplateCustomizers,
186                                this.requestFactoryCustomizer, this.interceptors);
187        }
188
189        /**
190         * Add additional {@link HttpMessageConverter HttpMessageConverters} that should be
191         * used with the {@link RestTemplate}. Any converters configured on the builder will
192         * replace RestTemplate's default converters.
193         * @param messageConverters the converters to add
194         * @return a new builder instance
195         * @see #messageConverters(HttpMessageConverter...)
196         */
197        public RestTemplateBuilder additionalMessageConverters(
198                        HttpMessageConverter<?>... messageConverters) {
199                Assert.notNull(messageConverters, "MessageConverters must not be null");
200                return additionalMessageConverters(Arrays.asList(messageConverters));
201        }
202
203        /**
204         * Add additional {@link HttpMessageConverter HttpMessageConverters} that should be
205         * used with the {@link RestTemplate}. Any converters configured on the builder will
206         * replace RestTemplate's default converters.
207         * @param messageConverters the converters to add
208         * @return a new builder instance
209         * @see #messageConverters(HttpMessageConverter...)
210         */
211        public RestTemplateBuilder additionalMessageConverters(
212                        Collection<? extends HttpMessageConverter<?>> messageConverters) {
213                Assert.notNull(messageConverters, "MessageConverters must not be null");
214                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
215                                append(this.messageConverters, messageConverters),
216                                this.requestFactorySupplier, this.uriTemplateHandler, this.errorHandler,
217                                this.basicAuthentication, this.restTemplateCustomizers,
218                                this.requestFactoryCustomizer, this.interceptors);
219        }
220
221        /**
222         * Set the {@link HttpMessageConverter HttpMessageConverters} that should be used with
223         * the {@link RestTemplate} to the default set. Calling this method will replace any
224         * previously defined converters.
225         * @return a new builder instance
226         * @see #messageConverters(HttpMessageConverter...)
227         */
228        public RestTemplateBuilder defaultMessageConverters() {
229                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
230                                Collections.unmodifiableSet(
231                                                new LinkedHashSet<>(new RestTemplate().getMessageConverters())),
232                                this.requestFactorySupplier, this.uriTemplateHandler, this.errorHandler,
233                                this.basicAuthentication, this.restTemplateCustomizers,
234                                this.requestFactoryCustomizer, this.interceptors);
235        }
236
237        /**
238         * Set the {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors} that
239         * should be used with the {@link RestTemplate}. Setting this value will replace any
240         * previously defined interceptors.
241         * @param interceptors the interceptors to set
242         * @return a new builder instance
243         * @since 1.4.1
244         * @see #additionalInterceptors(ClientHttpRequestInterceptor...)
245         */
246        public RestTemplateBuilder interceptors(
247                        ClientHttpRequestInterceptor... interceptors) {
248                Assert.notNull(interceptors, "interceptors must not be null");
249                return interceptors(Arrays.asList(interceptors));
250        }
251
252        /**
253         * Set the {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors} that
254         * should be used with the {@link RestTemplate}. Setting this value will replace any
255         * previously defined interceptors.
256         * @param interceptors the interceptors to set
257         * @return a new builder instance
258         * @since 1.4.1
259         * @see #additionalInterceptors(ClientHttpRequestInterceptor...)
260         */
261        public RestTemplateBuilder interceptors(
262                        Collection<ClientHttpRequestInterceptor> interceptors) {
263                Assert.notNull(interceptors, "interceptors must not be null");
264                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
265                                this.messageConverters, this.requestFactorySupplier,
266                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
267                                this.restTemplateCustomizers, this.requestFactoryCustomizer,
268                                Collections.unmodifiableSet(new LinkedHashSet<>(interceptors)));
269        }
270
271        /**
272         * Add additional {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors}
273         * that should be used with the {@link RestTemplate}.
274         * @param interceptors the interceptors to add
275         * @return a new builder instance
276         * @since 1.4.1
277         * @see #interceptors(ClientHttpRequestInterceptor...)
278         */
279        public RestTemplateBuilder additionalInterceptors(
280                        ClientHttpRequestInterceptor... interceptors) {
281                Assert.notNull(interceptors, "interceptors must not be null");
282                return additionalInterceptors(Arrays.asList(interceptors));
283        }
284
285        /**
286         * Add additional {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors}
287         * that should be used with the {@link RestTemplate}.
288         * @param interceptors the interceptors to add
289         * @return a new builder instance
290         * @since 1.4.1
291         * @see #interceptors(ClientHttpRequestInterceptor...)
292         */
293        public RestTemplateBuilder additionalInterceptors(
294                        Collection<? extends ClientHttpRequestInterceptor> interceptors) {
295                Assert.notNull(interceptors, "interceptors must not be null");
296                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
297                                this.messageConverters, this.requestFactorySupplier,
298                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
299                                this.restTemplateCustomizers, this.requestFactoryCustomizer,
300                                append(this.interceptors, interceptors));
301        }
302
303        /**
304         * Set the {@link ClientHttpRequestFactory} class that should be used with the
305         * {@link RestTemplate}.
306         * @param requestFactory the request factory to use
307         * @return a new builder instance
308         */
309        public RestTemplateBuilder requestFactory(
310                        Class<? extends ClientHttpRequestFactory> requestFactory) {
311                Assert.notNull(requestFactory, "RequestFactory must not be null");
312                return requestFactory(() -> createRequestFactory(requestFactory));
313        }
314
315        private ClientHttpRequestFactory createRequestFactory(
316                        Class<? extends ClientHttpRequestFactory> requestFactory) {
317                try {
318                        Constructor<?> constructor = requestFactory.getDeclaredConstructor();
319                        constructor.setAccessible(true);
320                        return (ClientHttpRequestFactory) constructor.newInstance();
321                }
322                catch (Exception ex) {
323                        throw new IllegalStateException(ex);
324                }
325        }
326
327        /**
328         * Set the {@code Supplier} of {@link ClientHttpRequestFactory} that should be called
329         * each time we {@link #build()} a new {@link RestTemplate} instance.
330         * @param requestFactorySupplier the supplier for the request factory
331         * @return a new builder instance
332         * @since 2.0.0
333         */
334        public RestTemplateBuilder requestFactory(
335                        Supplier<ClientHttpRequestFactory> requestFactorySupplier) {
336                Assert.notNull(requestFactorySupplier,
337                                "RequestFactory Supplier must not be null");
338                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
339                                this.messageConverters, requestFactorySupplier, this.uriTemplateHandler,
340                                this.errorHandler, this.basicAuthentication, this.restTemplateCustomizers,
341                                this.requestFactoryCustomizer, this.interceptors);
342        }
343
344        /**
345         * Set the {@link UriTemplateHandler} that should be used with the
346         * {@link RestTemplate}.
347         * @param uriTemplateHandler the URI template handler to use
348         * @return a new builder instance
349         */
350        public RestTemplateBuilder uriTemplateHandler(UriTemplateHandler uriTemplateHandler) {
351                Assert.notNull(uriTemplateHandler, "UriTemplateHandler must not be null");
352                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
353                                this.messageConverters, this.requestFactorySupplier, uriTemplateHandler,
354                                this.errorHandler, this.basicAuthentication, this.restTemplateCustomizers,
355                                this.requestFactoryCustomizer, this.interceptors);
356        }
357
358        /**
359         * Set the {@link ResponseErrorHandler} that should be used with the
360         * {@link RestTemplate}.
361         * @param errorHandler the error handler to use
362         * @return a new builder instance
363         */
364        public RestTemplateBuilder errorHandler(ResponseErrorHandler errorHandler) {
365                Assert.notNull(errorHandler, "ErrorHandler must not be null");
366                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
367                                this.messageConverters, this.requestFactorySupplier,
368                                this.uriTemplateHandler, errorHandler, this.basicAuthentication,
369                                this.restTemplateCustomizers, this.requestFactoryCustomizer,
370                                this.interceptors);
371        }
372
373        /**
374         * Add HTTP basic authentication to requests. See
375         * {@link BasicAuthenticationInterceptor} for details.
376         * @param username the user name
377         * @param password the password
378         * @return a new builder instance
379         * @deprecated since 2.1.0 in favor of
380         * {@link #basicAuthentication(String username, String password)}
381         */
382        @Deprecated
383        public RestTemplateBuilder basicAuthorization(String username, String password) {
384                return basicAuthentication(username, password);
385        }
386
387        /**
388         * Add HTTP basic authentication to requests. See
389         * {@link BasicAuthenticationInterceptor} for details.
390         * @param username the user name
391         * @param password the password
392         * @return a new builder instance
393         * @since 2.1.0
394         */
395        public RestTemplateBuilder basicAuthentication(String username, String password) {
396                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
397                                this.messageConverters, this.requestFactorySupplier,
398                                this.uriTemplateHandler, this.errorHandler,
399                                new BasicAuthenticationInterceptor(username, password),
400                                this.restTemplateCustomizers, this.requestFactoryCustomizer,
401                                this.interceptors);
402        }
403
404        /**
405         * Set the {@link RestTemplateCustomizer RestTemplateCustomizers} that should be
406         * applied to the {@link RestTemplate}. Customizers are applied in the order that they
407         * were added after builder configuration has been applied. Setting this value will
408         * replace any previously configured customizers.
409         * @param restTemplateCustomizers the customizers to set
410         * @return a new builder instance
411         * @see #additionalCustomizers(RestTemplateCustomizer...)
412         */
413        public RestTemplateBuilder customizers(
414                        RestTemplateCustomizer... restTemplateCustomizers) {
415                Assert.notNull(restTemplateCustomizers,
416                                "RestTemplateCustomizers must not be null");
417                return customizers(Arrays.asList(restTemplateCustomizers));
418        }
419
420        /**
421         * Set the {@link RestTemplateCustomizer RestTemplateCustomizers} that should be
422         * applied to the {@link RestTemplate}. Customizers are applied in the order that they
423         * were added after builder configuration has been applied. Setting this value will
424         * replace any previously configured customizers.
425         * @param restTemplateCustomizers the customizers to set
426         * @return a new builder instance
427         * @see #additionalCustomizers(RestTemplateCustomizer...)
428         */
429        public RestTemplateBuilder customizers(
430                        Collection<? extends RestTemplateCustomizer> restTemplateCustomizers) {
431                Assert.notNull(restTemplateCustomizers,
432                                "RestTemplateCustomizers must not be null");
433                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
434                                this.messageConverters, this.requestFactorySupplier,
435                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
436                                Collections.unmodifiableSet(new LinkedHashSet<RestTemplateCustomizer>(
437                                                restTemplateCustomizers)),
438                                this.requestFactoryCustomizer, this.interceptors);
439        }
440
441        /**
442         * Add {@link RestTemplateCustomizer RestTemplateCustomizers} that should be applied
443         * to the {@link RestTemplate}. Customizers are applied in the order that they were
444         * added after builder configuration has been applied.
445         * @param restTemplateCustomizers the customizers to add
446         * @return a new builder instance
447         * @see #customizers(RestTemplateCustomizer...)
448         */
449        public RestTemplateBuilder additionalCustomizers(
450                        RestTemplateCustomizer... restTemplateCustomizers) {
451                Assert.notNull(restTemplateCustomizers,
452                                "RestTemplateCustomizers must not be null");
453                return additionalCustomizers(Arrays.asList(restTemplateCustomizers));
454        }
455
456        /**
457         * Add {@link RestTemplateCustomizer RestTemplateCustomizers} that should be applied
458         * to the {@link RestTemplate}. Customizers are applied in the order that they were
459         * added after builder configuration has been applied.
460         * @param customizers the customizers to add
461         * @return a new builder instance
462         * @see #customizers(RestTemplateCustomizer...)
463         */
464        public RestTemplateBuilder additionalCustomizers(
465                        Collection<? extends RestTemplateCustomizer> customizers) {
466                Assert.notNull(customizers, "RestTemplateCustomizers must not be null");
467                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
468                                this.messageConverters, this.requestFactorySupplier,
469                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
470                                append(this.restTemplateCustomizers, customizers),
471                                this.requestFactoryCustomizer, this.interceptors);
472        }
473
474        /**
475         * Sets the connection timeout on the underlying {@link ClientHttpRequestFactory}.
476         * @param connectTimeout the connection timeout
477         * @return a new builder instance.
478         * @since 2.1.0
479         */
480        public RestTemplateBuilder setConnectTimeout(Duration connectTimeout) {
481                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
482                                this.messageConverters, this.requestFactorySupplier,
483                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
484                                this.restTemplateCustomizers,
485                                this.requestFactoryCustomizer.connectTimeout(connectTimeout),
486                                this.interceptors);
487        }
488
489        /**
490         * Sets the connection timeout in milliseconds on the underlying
491         * {@link ClientHttpRequestFactory}.
492         * @param connectTimeout the connection timeout in milliseconds
493         * @return a new builder instance.
494         * @deprecated since 2.1.0 in favor of {@link #setConnectTimeout(Duration)}
495         */
496        @Deprecated
497        public RestTemplateBuilder setConnectTimeout(int connectTimeout) {
498                return setConnectTimeout(Duration.ofMillis(connectTimeout));
499        }
500
501        /**
502         * Sets the read timeout on the underlying {@link ClientHttpRequestFactory}.
503         * @param readTimeout the read timeout
504         * @return a new builder instance.
505         * @since 2.1.0
506         */
507        public RestTemplateBuilder setReadTimeout(Duration readTimeout) {
508                return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
509                                this.messageConverters, this.requestFactorySupplier,
510                                this.uriTemplateHandler, this.errorHandler, this.basicAuthentication,
511                                this.restTemplateCustomizers,
512                                this.requestFactoryCustomizer.readTimeout(readTimeout),
513                                this.interceptors);
514        }
515
516        /**
517         * Sets the read timeout in milliseconds on the underlying
518         * {@link ClientHttpRequestFactory}.
519         * @param readTimeout the read timeout in milliseconds
520         * @return a new builder instance.
521         * @deprecated since 2.1.0 in favor of {@link #setReadTimeout(Duration)}
522         */
523        @Deprecated
524        public RestTemplateBuilder setReadTimeout(int readTimeout) {
525                return setReadTimeout(Duration.ofMillis(readTimeout));
526        }
527
528        /**
529         * Build a new {@link RestTemplate} instance and configure it using this builder.
530         * @return a configured {@link RestTemplate} instance.
531         * @see #build(Class)
532         * @see #configure(RestTemplate)
533         */
534        public RestTemplate build() {
535                return build(RestTemplate.class);
536        }
537
538        /**
539         * Build a new {@link RestTemplate} instance of the specified type and configure it
540         * using this builder.
541         * @param <T> the type of rest template
542         * @param restTemplateClass the template type to create
543         * @return a configured {@link RestTemplate} instance.
544         * @see RestTemplateBuilder#build()
545         * @see #configure(RestTemplate)
546         */
547
548        public <T extends RestTemplate> T build(Class<T> restTemplateClass) {
549                return configure(BeanUtils.instantiateClass(restTemplateClass));
550        }
551
552        /**
553         * Configure the provided {@link RestTemplate} instance using this builder.
554         * @param <T> the type of rest template
555         * @param restTemplate the {@link RestTemplate} to configure
556         * @return the rest template instance
557         * @see RestTemplateBuilder#build()
558         * @see RestTemplateBuilder#build(Class)
559         */
560        public <T extends RestTemplate> T configure(T restTemplate) {
561                configureRequestFactory(restTemplate);
562                if (!CollectionUtils.isEmpty(this.messageConverters)) {
563                        restTemplate.setMessageConverters(new ArrayList<>(this.messageConverters));
564                }
565                if (this.uriTemplateHandler != null) {
566                        restTemplate.setUriTemplateHandler(this.uriTemplateHandler);
567                }
568                if (this.errorHandler != null) {
569                        restTemplate.setErrorHandler(this.errorHandler);
570                }
571                if (this.rootUri != null) {
572                        RootUriTemplateHandler.addTo(restTemplate, this.rootUri);
573                }
574                if (this.basicAuthentication != null) {
575                        restTemplate.getInterceptors().add(this.basicAuthentication);
576                }
577                restTemplate.getInterceptors().addAll(this.interceptors);
578                if (!CollectionUtils.isEmpty(this.restTemplateCustomizers)) {
579                        for (RestTemplateCustomizer customizer : this.restTemplateCustomizers) {
580                                customizer.customize(restTemplate);
581                        }
582                }
583                return restTemplate;
584        }
585
586        private void configureRequestFactory(RestTemplate restTemplate) {
587                ClientHttpRequestFactory requestFactory = null;
588                if (this.requestFactorySupplier != null) {
589                        requestFactory = this.requestFactorySupplier.get();
590                }
591                else if (this.detectRequestFactory) {
592                        requestFactory = new ClientHttpRequestFactorySupplier().get();
593                }
594                if (requestFactory != null) {
595                        if (this.requestFactoryCustomizer != null) {
596                                this.requestFactoryCustomizer.accept(requestFactory);
597                        }
598                        restTemplate.setRequestFactory(requestFactory);
599                }
600        }
601
602        private <T> Set<T> append(Set<T> set, Collection<? extends T> additions) {
603                Set<T> result = new LinkedHashSet<>((set != null) ? set : Collections.emptySet());
604                result.addAll(additions);
605                return Collections.unmodifiableSet(result);
606        }
607
608        private static class RequestFactoryCustomizer
609                        implements Consumer<ClientHttpRequestFactory> {
610
611                private final Duration connectTimeout;
612
613                private final Duration readTimeout;
614
615                RequestFactoryCustomizer() {
616                        this(null, null);
617                }
618
619                private RequestFactoryCustomizer(Duration connectTimeout, Duration readTimeout) {
620                        this.connectTimeout = connectTimeout;
621                        this.readTimeout = readTimeout;
622                }
623
624                public RequestFactoryCustomizer connectTimeout(Duration connectTimeout) {
625                        return new RequestFactoryCustomizer(connectTimeout, this.readTimeout);
626                }
627
628                public RequestFactoryCustomizer readTimeout(Duration readTimeout) {
629                        return new RequestFactoryCustomizer(this.connectTimeout, readTimeout);
630                }
631
632                @Override
633                public void accept(ClientHttpRequestFactory requestFactory) {
634                        ClientHttpRequestFactory unwrappedRequestFactory = unwrapRequestFactoryIfNecessary(
635                                        requestFactory);
636                        if (this.connectTimeout != null) {
637                                new TimeoutRequestFactoryCustomizer(this.connectTimeout,
638                                                "setConnectTimeout").customize(unwrappedRequestFactory);
639                        }
640                        if (this.readTimeout != null) {
641                                new TimeoutRequestFactoryCustomizer(this.readTimeout, "setReadTimeout")
642                                                .customize(unwrappedRequestFactory);
643                        }
644                }
645
646                private ClientHttpRequestFactory unwrapRequestFactoryIfNecessary(
647                                ClientHttpRequestFactory requestFactory) {
648                        if (!(requestFactory instanceof AbstractClientHttpRequestFactoryWrapper)) {
649                                return requestFactory;
650                        }
651                        ClientHttpRequestFactory unwrappedRequestFactory = requestFactory;
652                        Field field = ReflectionUtils.findField(
653                                        AbstractClientHttpRequestFactoryWrapper.class, "requestFactory");
654                        ReflectionUtils.makeAccessible(field);
655                        do {
656                                unwrappedRequestFactory = (ClientHttpRequestFactory) ReflectionUtils
657                                                .getField(field, unwrappedRequestFactory);
658                        }
659                        while (unwrappedRequestFactory instanceof AbstractClientHttpRequestFactoryWrapper);
660                        return unwrappedRequestFactory;
661                }
662
663                /**
664                 * {@link ClientHttpRequestFactory} customizer to call a "set timeout" method.
665                 */
666                private static final class TimeoutRequestFactoryCustomizer {
667
668                        private final Duration timeout;
669
670                        private final String methodName;
671
672                        TimeoutRequestFactoryCustomizer(Duration timeout, String methodName) {
673                                this.timeout = timeout;
674                                this.methodName = methodName;
675                        }
676
677                        void customize(ClientHttpRequestFactory factory) {
678                                ReflectionUtils.invokeMethod(findMethod(factory), factory,
679                                                Math.toIntExact(this.timeout.toMillis()));
680                        }
681
682                        private Method findMethod(ClientHttpRequestFactory factory) {
683                                Method method = ReflectionUtils.findMethod(factory.getClass(),
684                                                this.methodName, int.class);
685                                if (method != null) {
686                                        return method;
687                                }
688                                throw new IllegalStateException("Request factory " + factory.getClass()
689                                                + " does not have a " + this.methodName + "(int) method");
690                        }
691
692                }
693
694        }
695
696}