All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.boot.web.client.RestTemplateBuilder Maven / Gradle / Ivy

There is a newer version: 3.2.5
Show newest version
/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.web.client;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.client.support.BasicAuthorizationInterceptor;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriTemplateHandler;

/**
 * Builder that can be used to configure and create a {@link RestTemplate}. Provides
 * convenience methods to register {@link #messageConverters(HttpMessageConverter...)
 * converters}, {@link #errorHandler(ResponseErrorHandler) error handlers} and
 * {@link #uriTemplateHandler(UriTemplateHandler) UriTemplateHandlers}.
 * 

* By default the built {@link RestTemplate} will attempt to use the most suitable * {@link ClientHttpRequestFactory}, call {@link #detectRequestFactory(boolean) * detectRequestFactory(false)} if you prefer to keep the default. In a typical * auto-configured Spring Boot application this builder is available as a bean and can be * injected whenever a {@link RestTemplate} is needed. * * @author Stephane Nicoll * @author Phillip Webb * @author Andy Wilkinson * @since 1.4.0 */ public class RestTemplateBuilder { private static final Map REQUEST_FACTORY_CANDIDATES; static { Map candidates = new LinkedHashMap(); candidates.put("org.apache.http.client.HttpClient", "org.springframework.http.client.HttpComponentsClientHttpRequestFactory"); candidates.put("okhttp3.OkHttpClient", "org.springframework.http.client.OkHttp3ClientHttpRequestFactory"); candidates.put("com.squareup.okhttp.OkHttpClient", "org.springframework.http.client.OkHttpClientHttpRequestFactory"); candidates.put("io.netty.channel.EventLoopGroup", "org.springframework.http.client.Netty4ClientHttpRequestFactory"); REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates); } private final boolean detectRequestFactory; private final String rootUri; private final Set> messageConverters; private final ClientHttpRequestFactory requestFactory; private final UriTemplateHandler uriTemplateHandler; private final ResponseErrorHandler errorHandler; private final BasicAuthorizationInterceptor basicAuthorization; private final Set restTemplateCustomizers; private final Set requestFactoryCustomizers; private final Set interceptors; /** * Create a new {@link RestTemplateBuilder} instance. * @param customizers any {@link RestTemplateCustomizer RestTemplateCustomizers} that * should be applied when the {@link RestTemplate} is built */ public RestTemplateBuilder(RestTemplateCustomizer... customizers) { Assert.notNull(customizers, "Customizers must not be null"); this.detectRequestFactory = true; this.rootUri = null; this.messageConverters = null; this.requestFactory = null; this.uriTemplateHandler = null; this.errorHandler = null; this.basicAuthorization = null; this.restTemplateCustomizers = Collections.unmodifiableSet( new LinkedHashSet(Arrays.asList(customizers))); this.requestFactoryCustomizers = Collections.emptySet(); this.interceptors = Collections.emptySet(); } private RestTemplateBuilder(boolean detectRequestFactory, String rootUri, Set> messageConverters, ClientHttpRequestFactory requestFactory, UriTemplateHandler uriTemplateHandler, ResponseErrorHandler errorHandler, BasicAuthorizationInterceptor basicAuthorization, Set restTemplateCustomizers, Set requestFactoryCustomizers, Set interceptors) { super(); this.detectRequestFactory = detectRequestFactory; this.rootUri = rootUri; this.messageConverters = messageConverters; this.requestFactory = requestFactory; this.uriTemplateHandler = uriTemplateHandler; this.errorHandler = errorHandler; this.basicAuthorization = basicAuthorization; this.restTemplateCustomizers = restTemplateCustomizers; this.requestFactoryCustomizers = requestFactoryCustomizers; this.interceptors = interceptors; } /** * Set if the {@link ClientHttpRequestFactory} should be detected based on the * classpath. Default if {@code true}. * @param detectRequestFactory if the {@link ClientHttpRequestFactory} should be * detected * @return a new builder instance */ public RestTemplateBuilder detectRequestFactory(boolean detectRequestFactory) { return new RestTemplateBuilder(detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Set a root URL that should be applied to each request that starts with {@code '/'}. * See {@link RootUriTemplateHandler} for details. * @param rootUri the root URI or {@code null} * @return a new builder instance */ public RestTemplateBuilder rootUri(String rootUri) { return new RestTemplateBuilder(this.detectRequestFactory, rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Set the {@link HttpMessageConverter HttpMessageConverters} that should be used with * the {@link RestTemplate}. Setting this value will replace any previously configured * converters. * @param messageConverters the converters to set * @return a new builder instance * @see #additionalMessageConverters(HttpMessageConverter...) */ public RestTemplateBuilder messageConverters( HttpMessageConverter... messageConverters) { Assert.notNull(messageConverters, "MessageConverters must not be null"); return messageConverters(Arrays.asList(messageConverters)); } /** * Set the {@link HttpMessageConverter HttpMessageConverters} that should be used with * the {@link RestTemplate}. Setting this value will replace any previously configured * converters. * @param messageConverters the converters to set * @return a new builder instance * @see #additionalMessageConverters(HttpMessageConverter...) */ public RestTemplateBuilder messageConverters( Collection> messageConverters) { Assert.notNull(messageConverters, "MessageConverters must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, Collections.unmodifiableSet( new LinkedHashSet>(messageConverters)), this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Add additional {@link HttpMessageConverter HttpMessageConverters} that should be * used with the {@link RestTemplate}. * @param messageConverters the converters to add * @return a new builder instance * @see #messageConverters(HttpMessageConverter...) */ public RestTemplateBuilder additionalMessageConverters( HttpMessageConverter... messageConverters) { Assert.notNull(messageConverters, "MessageConverters must not be null"); return additionalMessageConverters(Arrays.asList(messageConverters)); } /** * Add additional {@link HttpMessageConverter HttpMessageConverters} that should be * used with the {@link RestTemplate}. * @param messageConverters the converters to add * @return a new builder instance * @see #messageConverters(HttpMessageConverter...) */ public RestTemplateBuilder additionalMessageConverters( Collection> messageConverters) { Assert.notNull(messageConverters, "MessageConverters must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, append(this.messageConverters, messageConverters), this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Set the {@link HttpMessageConverter HttpMessageConverters} that should be used with * the {@link RestTemplate} to the default set. Calling this method will replace any * previously defined converters. * @return a new builder instance * @see #messageConverters(HttpMessageConverter...) */ public RestTemplateBuilder defaultMessageConverters() { return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, Collections.unmodifiableSet(new LinkedHashSet>( new RestTemplate().getMessageConverters())), this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Set the {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors} that * should be used with the {@link RestTemplate}. Setting this value will replace any * previously defined interceptors. * @param interceptors the interceptors to set * @return a new builder instance * @since 1.4.1 * @see #additionalInterceptors(ClientHttpRequestInterceptor...) */ public RestTemplateBuilder interceptors( ClientHttpRequestInterceptor... interceptors) { Assert.notNull(interceptors, "interceptors must not be null"); return interceptors(Arrays.asList(interceptors)); } /** * Set the {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors} that * should be used with the {@link RestTemplate}. Setting this value will replace any * previously defined interceptors. * @param interceptors the interceptors to set * @return a new builder instance * @since 1.4.1 * @see #additionalInterceptors(ClientHttpRequestInterceptor...) */ public RestTemplateBuilder interceptors( Collection interceptors) { Assert.notNull(interceptors, "interceptors must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, Collections.unmodifiableSet( new LinkedHashSet(interceptors))); } /** * Add additional {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors} * that should be used with the {@link RestTemplate}. * @param interceptors the interceptors to add * @return a new builder instance * @since 1.4.1 * @see #interceptors(ClientHttpRequestInterceptor...) */ public RestTemplateBuilder additionalInterceptors( ClientHttpRequestInterceptor... interceptors) { Assert.notNull(interceptors, "interceptors must not be null"); return additionalInterceptors(Arrays.asList(interceptors)); } /** * Add additional {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors} * that should be used with the {@link RestTemplate}. * @param interceptors the interceptors to add * @return a new builder instance * @since 1.4.1 * @see #interceptors(ClientHttpRequestInterceptor...) */ public RestTemplateBuilder additionalInterceptors( Collection interceptors) { Assert.notNull(interceptors, "interceptors must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, append(this.interceptors, interceptors)); } /** * Set the {@link ClientHttpRequestFactory} class that should be used with the * {@link RestTemplate}. * @param requestFactory the request factory to use * @return a new builder instance */ public RestTemplateBuilder requestFactory( Class requestFactory) { Assert.notNull(requestFactory, "RequestFactory must not be null"); return requestFactory(createRequestFactory(requestFactory)); } private ClientHttpRequestFactory createRequestFactory( Class requestFactory) { try { Constructor constructor = requestFactory.getDeclaredConstructor(); constructor.setAccessible(true); return (ClientHttpRequestFactory) constructor.newInstance(); } catch (Exception ex) { throw new IllegalStateException(ex); } } /** * Set the {@link ClientHttpRequestFactory} that should be used with the * {@link RestTemplate}. * @param requestFactory the request factory to use * @return a new builder instance */ public RestTemplateBuilder requestFactory(ClientHttpRequestFactory requestFactory) { Assert.notNull(requestFactory, "RequestFactory must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Set the {@link UriTemplateHandler} that should be used with the * {@link RestTemplate}. * @param uriTemplateHandler the URI template handler to use * @return a new builder instance */ public RestTemplateBuilder uriTemplateHandler(UriTemplateHandler uriTemplateHandler) { Assert.notNull(uriTemplateHandler, "UriTemplateHandler must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Set the {@link ResponseErrorHandler} that should be used with the * {@link RestTemplate}. * @param errorHandler the error handler to use * @return a new builder instance */ public RestTemplateBuilder errorHandler(ResponseErrorHandler errorHandler) { Assert.notNull(errorHandler, "ErrorHandler must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, errorHandler, this.basicAuthorization, this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Add HTTP basic authentication to requests. See * {@link BasicAuthorizationInterceptor} for details. * @param username the user name * @param password the password * @return a new builder instance */ public RestTemplateBuilder basicAuthorization(String username, String password) { return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, new BasicAuthorizationInterceptor(username, password), this.restTemplateCustomizers, this.requestFactoryCustomizers, this.interceptors); } /** * Set the {@link RestTemplateCustomizer RestTemplateCustomizers} that should be * applied to the {@link RestTemplate}. Customizers are applied in the order that they * were added after builder configuration has been applied. Setting this value will * replace any previously configured customizers. * @param restTemplateCustomizers the customizers to set * @return a new builder instance * @see #additionalCustomizers(RestTemplateCustomizer...) */ public RestTemplateBuilder customizers( RestTemplateCustomizer... restTemplateCustomizers) { Assert.notNull(restTemplateCustomizers, "RestTemplateCustomizers must not be null"); return customizers(Arrays.asList(restTemplateCustomizers)); } /** * Set the {@link RestTemplateCustomizer RestTemplateCustomizers} that should be * applied to the {@link RestTemplate}. Customizers are applied in the order that they * were added after builder configuration has been applied. Setting this value will * replace any previously configured customizers. * @param restTemplateCustomizers the customizers to set * @return a new builder instance * @see #additionalCustomizers(RestTemplateCustomizer...) */ public RestTemplateBuilder customizers( Collection restTemplateCustomizers) { Assert.notNull(restTemplateCustomizers, "RestTemplateCustomizers must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, Collections.unmodifiableSet(new LinkedHashSet( restTemplateCustomizers)), this.requestFactoryCustomizers, this.interceptors); } /** * Add {@link RestTemplateCustomizer RestTemplateCustomizers} that should be applied * to the {@link RestTemplate}. Customizers are applied in the order that they were * added after builder configuration has been applied. * @param restTemplateCustomizers the customizers to add * @return a new builder instance * @see #customizers(RestTemplateCustomizer...) */ public RestTemplateBuilder additionalCustomizers( RestTemplateCustomizer... restTemplateCustomizers) { Assert.notNull(restTemplateCustomizers, "RestTemplateCustomizers must not be null"); return additionalCustomizers(Arrays.asList(restTemplateCustomizers)); } /** * Add {@link RestTemplateCustomizer RestTemplateCustomizers} that should be applied * to the {@link RestTemplate}. Customizers are applied in the order that they were * added after builder configuration has been applied. * @param customizers the customizers to add * @return a new builder instance * @see #customizers(RestTemplateCustomizer...) */ public RestTemplateBuilder additionalCustomizers( Collection customizers) { Assert.notNull(customizers, "RestTemplateCustomizers must not be null"); return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, append(this.restTemplateCustomizers, customizers), this.requestFactoryCustomizers, this.interceptors); } /** * Sets the connect timeout in milliseconds on the underlying * {@link ClientHttpRequestFactory}. * @param connectTimeout the connect timeout in milliseconds * @return a new builder instance. */ public RestTemplateBuilder setConnectTimeout(int connectTimeout) { return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, append(this.requestFactoryCustomizers, new ConnectTimeoutRequestFactoryCustomizer(connectTimeout)), this.interceptors); } /** * Sets the read timeout in milliseconds on the underlying * {@link ClientHttpRequestFactory}. * @param readTimeout the read timeout in milliseconds * @return a new builder instance. */ public RestTemplateBuilder setReadTimeout(int readTimeout) { return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri, this.messageConverters, this.requestFactory, this.uriTemplateHandler, this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers, append(this.requestFactoryCustomizers, new ReadTimeoutRequestFactoryCustomizer(readTimeout)), this.interceptors); } /** * Build a new {@link RestTemplate} instance and configure it using this builder. * @return a configured {@link RestTemplate} instance. * @see #build(Class) * @see #configure(RestTemplate) */ public RestTemplate build() { return build(RestTemplate.class); } /** * Build a new {@link RestTemplate} instance of the specified type and configure it * using this builder. * @param the type of rest template * @param restTemplateClass the template type to create * @return a configured {@link RestTemplate} instance. * @see RestTemplateBuilder#build() * @see #configure(RestTemplate) */ public T build(Class restTemplateClass) { return configure(BeanUtils.instantiate(restTemplateClass)); } /** * Configure the provided {@link RestTemplate} instance using this builder. * @param the type of rest template * @param restTemplate the {@link RestTemplate} to configure * @return the rest template instance * @see RestTemplateBuilder#build() * @see RestTemplateBuilder#build(Class) */ public T configure(T restTemplate) { configureRequestFactory(restTemplate); if (!CollectionUtils.isEmpty(this.messageConverters)) { restTemplate.setMessageConverters( new ArrayList>(this.messageConverters)); } if (this.uriTemplateHandler != null) { restTemplate.setUriTemplateHandler(this.uriTemplateHandler); } if (this.errorHandler != null) { restTemplate.setErrorHandler(this.errorHandler); } if (this.rootUri != null) { RootUriTemplateHandler.addTo(restTemplate, this.rootUri); } if (this.basicAuthorization != null) { restTemplate.getInterceptors().add(this.basicAuthorization); } if (!CollectionUtils.isEmpty(this.restTemplateCustomizers)) { for (RestTemplateCustomizer customizer : this.restTemplateCustomizers) { customizer.customize(restTemplate); } } restTemplate.getInterceptors().addAll(this.interceptors); return restTemplate; } private void configureRequestFactory(RestTemplate restTemplate) { ClientHttpRequestFactory requestFactory = null; if (this.requestFactory != null) { requestFactory = this.requestFactory; } else if (this.detectRequestFactory) { requestFactory = detectRequestFactory(); } if (requestFactory != null) { ClientHttpRequestFactory unwrappedRequestFactory = unwrapRequestFactoryIfNecessary( requestFactory); for (RequestFactoryCustomizer customizer : this.requestFactoryCustomizers) { customizer.customize(unwrappedRequestFactory); } restTemplate.setRequestFactory(requestFactory); } } private ClientHttpRequestFactory unwrapRequestFactoryIfNecessary( ClientHttpRequestFactory requestFactory) { if (!(requestFactory instanceof AbstractClientHttpRequestFactoryWrapper)) { return requestFactory; } ClientHttpRequestFactory unwrappedRequestFactory = requestFactory; Field field = ReflectionUtils.findField( AbstractClientHttpRequestFactoryWrapper.class, "requestFactory"); ReflectionUtils.makeAccessible(field); do { unwrappedRequestFactory = (ClientHttpRequestFactory) ReflectionUtils .getField(field, unwrappedRequestFactory); } while (unwrappedRequestFactory instanceof AbstractClientHttpRequestFactoryWrapper); return unwrappedRequestFactory; } private ClientHttpRequestFactory detectRequestFactory() { for (Map.Entry candidate : REQUEST_FACTORY_CANDIDATES .entrySet()) { ClassLoader classLoader = getClass().getClassLoader(); if (ClassUtils.isPresent(candidate.getKey(), classLoader)) { Class factoryClass = ClassUtils.resolveClassName(candidate.getValue(), classLoader); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) BeanUtils .instantiate(factoryClass); initializeIfNecessary(requestFactory); return requestFactory; } } return new SimpleClientHttpRequestFactory(); } private void initializeIfNecessary(ClientHttpRequestFactory requestFactory) { if (requestFactory instanceof InitializingBean) { try { ((InitializingBean) requestFactory).afterPropertiesSet(); } catch (Exception ex) { throw new IllegalStateException( "Failed to initialize request factory " + requestFactory, ex); } } } private Set append(Set set, T addition) { Set result = new LinkedHashSet( set == null ? Collections.emptySet() : set); result.add(addition); return Collections.unmodifiableSet(result); } private Set append(Set set, Collection additions) { Set result = new LinkedHashSet( set == null ? Collections.emptySet() : set); result.addAll(additions); return Collections.unmodifiableSet(result); } /** * Strategy interface used to customize the {@link ClientHttpRequestFactory}. */ private interface RequestFactoryCustomizer { void customize(ClientHttpRequestFactory factory); } /** * {@link RequestFactoryCustomizer} to call a "set timeout" method. */ private static abstract class TimeoutRequestFactoryCustomizer implements RequestFactoryCustomizer { private final int timeout; private final String methodName; TimeoutRequestFactoryCustomizer(int timeout, String methodName) { this.timeout = timeout; this.methodName = methodName; } @Override public void customize(ClientHttpRequestFactory factory) { ReflectionUtils.invokeMethod(findMethod(factory), factory, this.timeout); } private Method findMethod(ClientHttpRequestFactory factory) { Method method = ReflectionUtils.findMethod(factory.getClass(), this.methodName, int.class); if (method != null) { return method; } throw new IllegalStateException("Request factory " + factory.getClass() + " does not have a " + this.methodName + "(int) method"); } } /** * {@link RequestFactoryCustomizer} to set the read timeout. */ private static class ReadTimeoutRequestFactoryCustomizer extends TimeoutRequestFactoryCustomizer { ReadTimeoutRequestFactoryCustomizer(int readTimeout) { super(readTimeout, "setReadTimeout"); } } /** * {@link RequestFactoryCustomizer} to set the connect timeout. */ private static class ConnectTimeoutRequestFactoryCustomizer extends TimeoutRequestFactoryCustomizer { ConnectTimeoutRequestFactoryCustomizer(int connectTimeout) { super(connectTimeout, "setConnectTimeout"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy