Polish "Add duration support for setConnectTimout and setReadTimeout"

See gh-13355
This commit is contained in:
Stephane Nicoll 2018-06-05 15:44:52 +02:00
parent 83f7df920b
commit 8691d01aaf
1 changed files with 120 additions and 98 deletions

View File

@ -19,12 +19,14 @@ package org.springframework.boot.web.client;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.springframework.beans.BeanUtils;
@ -76,7 +78,7 @@ public class RestTemplateBuilder {
private final Set<RestTemplateCustomizer> restTemplateCustomizers;
private final Set<RequestFactoryCustomizer> requestFactoryCustomizers;
private final RequestFactoryCustomizer requestFactoryCustomizer;
private final Set<ClientHttpRequestInterceptor> interceptors;
@ -96,7 +98,7 @@ public class RestTemplateBuilder {
this.basicAuthorization = null;
this.restTemplateCustomizers = Collections
.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(customizers)));
this.requestFactoryCustomizers = Collections.emptySet();
this.requestFactoryCustomizer = new RequestFactoryCustomizer();
this.interceptors = Collections.emptySet();
}
@ -106,7 +108,7 @@ public class RestTemplateBuilder {
UriTemplateHandler uriTemplateHandler, ResponseErrorHandler errorHandler,
BasicAuthorizationInterceptor basicAuthorization,
Set<RestTemplateCustomizer> restTemplateCustomizers,
Set<RequestFactoryCustomizer> requestFactoryCustomizers,
RequestFactoryCustomizer requestFactoryCustomizer,
Set<ClientHttpRequestInterceptor> interceptors) {
this.detectRequestFactory = detectRequestFactory;
this.rootUri = rootUri;
@ -116,7 +118,7 @@ public class RestTemplateBuilder {
this.errorHandler = errorHandler;
this.basicAuthorization = basicAuthorization;
this.restTemplateCustomizers = restTemplateCustomizers;
this.requestFactoryCustomizers = requestFactoryCustomizers;
this.requestFactoryCustomizer = requestFactoryCustomizer;
this.interceptors = interceptors;
}
@ -131,7 +133,7 @@ public class RestTemplateBuilder {
return new RestTemplateBuilder(detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.restTemplateCustomizers, this.requestFactoryCustomizer,
this.interceptors);
}
@ -145,7 +147,7 @@ public class RestTemplateBuilder {
return new RestTemplateBuilder(this.detectRequestFactory, rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.restTemplateCustomizers, this.requestFactoryCustomizer,
this.interceptors);
}
@ -179,7 +181,7 @@ public class RestTemplateBuilder {
new LinkedHashSet<HttpMessageConverter<?>>(messageConverters)),
this.requestFactorySupplier, this.uriTemplateHandler, this.errorHandler,
this.basicAuthorization, this.restTemplateCustomizers,
this.requestFactoryCustomizers, this.interceptors);
this.requestFactoryCustomizer, this.interceptors);
}
/**
@ -209,7 +211,7 @@ public class RestTemplateBuilder {
append(this.messageConverters, messageConverters),
this.requestFactorySupplier, this.uriTemplateHandler, this.errorHandler,
this.basicAuthorization, this.restTemplateCustomizers,
this.requestFactoryCustomizers, this.interceptors);
this.requestFactoryCustomizer, this.interceptors);
}
/**
@ -225,7 +227,7 @@ public class RestTemplateBuilder {
new LinkedHashSet<>(new RestTemplate().getMessageConverters())),
this.requestFactorySupplier, this.uriTemplateHandler, this.errorHandler,
this.basicAuthorization, this.restTemplateCustomizers,
this.requestFactoryCustomizers, this.interceptors);
this.requestFactoryCustomizer, this.interceptors);
}
/**
@ -258,7 +260,7 @@ public class RestTemplateBuilder {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.restTemplateCustomizers, this.requestFactoryCustomizer,
Collections.unmodifiableSet(new LinkedHashSet<>(interceptors)));
}
@ -290,7 +292,7 @@ public class RestTemplateBuilder {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.restTemplateCustomizers, this.requestFactoryCustomizer,
append(this.interceptors, interceptors));
}
@ -332,7 +334,7 @@ public class RestTemplateBuilder {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, requestFactorySupplier, this.uriTemplateHandler,
this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers,
this.requestFactoryCustomizers, this.interceptors);
this.requestFactoryCustomizer, this.interceptors);
}
/**
@ -346,7 +348,7 @@ public class RestTemplateBuilder {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier, uriTemplateHandler,
this.errorHandler, this.basicAuthorization, this.restTemplateCustomizers,
this.requestFactoryCustomizers, this.interceptors);
this.requestFactoryCustomizer, this.interceptors);
}
/**
@ -360,7 +362,7 @@ public class RestTemplateBuilder {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, errorHandler, this.basicAuthorization,
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.restTemplateCustomizers, this.requestFactoryCustomizer,
this.interceptors);
}
@ -376,7 +378,7 @@ public class RestTemplateBuilder {
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler,
new BasicAuthorizationInterceptor(username, password),
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.restTemplateCustomizers, this.requestFactoryCustomizer,
this.interceptors);
}
@ -414,7 +416,7 @@ public class RestTemplateBuilder {
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
Collections.unmodifiableSet(new LinkedHashSet<RestTemplateCustomizer>(
restTemplateCustomizers)),
this.requestFactoryCustomizers, this.interceptors);
this.requestFactoryCustomizer, this.interceptors);
}
/**
@ -447,7 +449,22 @@ public class RestTemplateBuilder {
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
append(this.restTemplateCustomizers, customizers),
this.requestFactoryCustomizers, this.interceptors);
this.requestFactoryCustomizer, this.interceptors);
}
/**
* Sets the connection timeout on the underlying {@link ClientHttpRequestFactory}.
* @param connectTimeout the connection timeout
* @return a new builder instance.
* @since 2.1.0
*/
public RestTemplateBuilder setConnectTimeout(Duration connectTimeout) {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers,
this.requestFactoryCustomizer.connectTimeout(connectTimeout),
this.interceptors);
}
/**
@ -455,14 +472,25 @@ public class RestTemplateBuilder {
* {@link ClientHttpRequestFactory}.
* @param connectTimeout the connection timeout in milliseconds
* @return a new builder instance.
* @deprecated since 2.1.0 in favor of {@link #setConnectTimeout(Duration)}
*/
@Deprecated
public RestTemplateBuilder setConnectTimeout(int connectTimeout) {
return setConnectTimeout(Duration.ofMillis(connectTimeout));
}
/**
* Sets the read timeout on the underlying {@link ClientHttpRequestFactory}.
* @param readTimeout the read timeout
* @return a new builder instance.
* @since 2.1.0
*/
public RestTemplateBuilder setReadTimeout(Duration readTimeout) {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers,
append(this.requestFactoryCustomizers,
new ConnectTimeoutRequestFactoryCustomizer(connectTimeout)),
this.requestFactoryCustomizer.readTimeout(readTimeout),
this.interceptors);
}
@ -471,15 +499,11 @@ public class RestTemplateBuilder {
* {@link ClientHttpRequestFactory}.
* @param readTimeout the read timeout in milliseconds
* @return a new builder instance.
* @deprecated since 2.1.0 in favour of {@link #setReadTimeout(Duration)}
*/
@Deprecated
public RestTemplateBuilder setReadTimeout(int readTimeout) {
return new RestTemplateBuilder(this.detectRequestFactory, this.rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers,
append(this.requestFactoryCustomizers,
new ReadTimeoutRequestFactoryCustomizer(readTimeout)),
this.interceptors);
return setReadTimeout(Duration.ofMillis(readTimeout));
}
/**
@ -549,105 +573,103 @@ public class RestTemplateBuilder {
requestFactory = new ClientHttpRequestFactorySupplier().get();
}
if (requestFactory != null) {
ClientHttpRequestFactory unwrappedRequestFactory = unwrapRequestFactoryIfNecessary(
requestFactory);
for (RequestFactoryCustomizer customizer : this.requestFactoryCustomizers) {
customizer.customize(unwrappedRequestFactory);
if (this.requestFactoryCustomizer != null) {
this.requestFactoryCustomizer.accept(requestFactory);
}
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 <T> Set<T> append(Set<T> set, T addition) {
Set<T> result = new LinkedHashSet<>(set != null ? set : Collections.emptySet());
result.add(addition);
return Collections.unmodifiableSet(result);
}
private <T> Set<T> append(Set<T> set, Collection<? extends T> additions) {
Set<T> result = new LinkedHashSet<>(set != null ? set : Collections.emptySet());
result.addAll(additions);
return Collections.unmodifiableSet(result);
}
/**
* Strategy interface used to customize the {@link ClientHttpRequestFactory}.
*/
private interface RequestFactoryCustomizer {
private static class RequestFactoryCustomizer
implements Consumer<ClientHttpRequestFactory> {
void customize(ClientHttpRequestFactory factory);
private final Duration connectTimeout;
}
private final Duration readTimeout;
/**
* {@link RequestFactoryCustomizer} to call a "set timeout" method.
*/
private abstract static class TimeoutRequestFactoryCustomizer
implements RequestFactoryCustomizer {
RequestFactoryCustomizer() {
this(null, null);
}
private final int timeout;
private RequestFactoryCustomizer(Duration connectTimeout, Duration readTimeout) {
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
}
private final String methodName;
public RequestFactoryCustomizer connectTimeout(Duration connectTimeout) {
return new RequestFactoryCustomizer(connectTimeout, this.readTimeout);
}
TimeoutRequestFactoryCustomizer(int timeout, String methodName) {
this.timeout = timeout;
this.methodName = methodName;
public RequestFactoryCustomizer readTimeout(Duration readTimeout) {
return new RequestFactoryCustomizer(this.connectTimeout, readTimeout);
}
@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;
public void accept(ClientHttpRequestFactory requestFactory) {
ClientHttpRequestFactory unwrappedRequestFactory = unwrapRequestFactoryIfNecessary(
requestFactory);
if (this.connectTimeout != null) {
new TimeoutRequestFactoryCustomizer(this.connectTimeout,
"setConnectTimeout").customize(unwrappedRequestFactory);
}
if (this.readTimeout != null) {
new TimeoutRequestFactoryCustomizer(this.readTimeout, "setReadTimeout")
.customize(unwrappedRequestFactory);
}
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");
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;
}
}
/**
* {@link ClientHttpRequestFactory} customizer to call a "set timeout" method.
*/
private static final class TimeoutRequestFactoryCustomizer {
/**
* {@link RequestFactoryCustomizer} to set the connection timeout.
*/
private static class ConnectTimeoutRequestFactoryCustomizer
extends TimeoutRequestFactoryCustomizer {
private final Duration timeout;
private final String methodName;
TimeoutRequestFactoryCustomizer(Duration timeout, String methodName) {
this.timeout = timeout;
this.methodName = methodName;
}
void customize(ClientHttpRequestFactory factory) {
ReflectionUtils.invokeMethod(findMethod(factory), factory,
Math.toIntExact(this.timeout.toMillis()));
}
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");
}
ConnectTimeoutRequestFactoryCustomizer(int connectTimeout) {
super(connectTimeout, "setConnectTimeout");
}
}