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