From 5113f0abf38424bdeeaf5fc2826b658c021af60d Mon Sep 17 00:00:00 2001 From: ZIRAKrezovic <145535273+ZIRAKrezovic@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:28:23 +0200 Subject: [PATCH] Add support for configuring RestTemplate via RestTemplateBuilder (#4255) (#4272) --- .../eureka/RestTemplateEurekaClientTest.java | 31 +++++++++++++- ...coveryClientOptionalArgsConfiguration.java | 10 +++-- ...ekaConfigServerBootstrapConfiguration.java | 9 ++-- ...stTemplateDiscoveryClientOptionalArgs.java | 16 +++++++- .../RestTemplateTransportClientFactories.java | 7 ++-- .../RestTemplateTransportClientFactory.java | 41 +++++++++++++++---- 6 files changed, 95 insertions(+), 19 deletions(-) diff --git a/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/RestTemplateEurekaClientTest.java b/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/RestTemplateEurekaClientTest.java index 5ce74316c..480571835 100644 --- a/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/RestTemplateEurekaClientTest.java +++ b/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/RestTemplateEurekaClientTest.java @@ -24,8 +24,11 @@ import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.cloud.configuration.TlsProperties; import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration; import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier; @@ -76,7 +79,7 @@ public class RestTemplateEurekaClientTest extends BaseCertTest { EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) throws GeneralSecurityException, IOException { return configuration.restTemplateDiscoveryClientOptionalArgs(tlsProperties, - eurekaClientHttpRequestFactorySupplier); + eurekaClientHttpRequestFactorySupplier, new RestTemplateBuilderObjectProvider()); } } @@ -88,4 +91,30 @@ public class RestTemplateEurekaClientTest extends BaseCertTest { } + private static class RestTemplateBuilderObjectProvider implements ObjectProvider { + + private final RestTemplateBuilder builder = new RestTemplateBuilder(); + + @Override + public RestTemplateBuilder getObject(Object... args) throws BeansException { + return builder; + } + + @Override + public RestTemplateBuilder getIfAvailable() throws BeansException { + return builder; + } + + @Override + public RestTemplateBuilder getIfUnique() throws BeansException { + return builder; + } + + @Override + public RestTemplateBuilder getObject() throws BeansException { + return builder; + } + + } + } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java index d621a8213..ef0733c62 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2024 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. @@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.cloud.configuration.SSLContextFactory; import org.springframework.cloud.configuration.TlsProperties; import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; @@ -49,6 +50,7 @@ import org.springframework.web.reactive.function.client.WebClient; /** * @author Daniel Lavoie + * @author Armin Krezovic */ @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(RestTemplateTimeoutProperties.class) @@ -69,11 +71,11 @@ public class DiscoveryClientOptionalArgsConfiguration { @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true, havingValue = "false") public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(TlsProperties tlsProperties, - EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) - throws GeneralSecurityException, IOException { + EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier, + ObjectProvider restTemplateBuilders) throws GeneralSecurityException, IOException { logger.info("Eureka HTTP Client uses RestTemplate."); RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs( - eurekaClientHttpRequestFactorySupplier); + eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable); setupTLS(result, tlsProperties); return result; } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java index b9f33cf28..104c6f1a1 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.cloud.config.client.ConfigServerInstanceProvider; import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator; import org.springframework.cloud.configuration.TlsProperties; @@ -52,6 +53,7 @@ import org.springframework.web.reactive.function.client.WebClient; * discovery. * * @author Dave Syer + * @author Armin Krezovic */ @ConditionalOnClass(ConfigServicePropertySourceLocator.class) @Conditional(EurekaConfigServerBootstrapConfiguration.EurekaConfigServerBootstrapCondition.class) @@ -71,9 +73,10 @@ public class EurekaConfigServerBootstrapConfiguration { havingValue = "false") public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config, Environment env, @Nullable TlsProperties properties, - EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) { + EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier, + ObjectProvider restTemplateBuilders) { return (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory(properties, - eurekaClientHttpRequestFactorySupplier) + eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable) .newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env)); } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateDiscoveryClientOptionalArgs.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateDiscoveryClientOptionalArgs.java index 0e74905b4..663afb6ae 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateDiscoveryClientOptionalArgs.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateDiscoveryClientOptionalArgs.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2024 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. @@ -16,18 +16,32 @@ package org.springframework.cloud.netflix.eureka.http; +import java.util.function.Supplier; + import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs; +import org.springframework.boot.web.client.RestTemplateBuilder; + /** * @author Daniel Lavoie + * @author Armin Krezovic */ public class RestTemplateDiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs { protected final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier; + protected final Supplier restTemplateBuilderSupplier; + public RestTemplateDiscoveryClientOptionalArgs( EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) { + this(eurekaClientHttpRequestFactorySupplier, RestTemplateBuilder::new); + } + + public RestTemplateDiscoveryClientOptionalArgs( + EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier, + Supplier restTemplateBuilderSupplier) { this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier; + this.restTemplateBuilderSupplier = restTemplateBuilderSupplier; } } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactories.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactories.java index d6f8b0cc0..09f4c1c0a 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactories.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactories.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2024 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. @@ -29,6 +29,7 @@ import com.netflix.discovery.shared.transport.jersey.TransportClientFactories; /** * @author Daniel Lavoie + * @author Armin Krezovic */ public class RestTemplateTransportClientFactories implements TransportClientFactories { @@ -42,7 +43,7 @@ public class RestTemplateTransportClientFactories implements TransportClientFact public TransportClientFactory newTransportClientFactory(EurekaClientConfig clientConfig, Collection additionalFilters, InstanceInfo myInstanceInfo) { return new RestTemplateTransportClientFactory(this.args.getSSLContext(), this.args.getHostnameVerifier(), - this.args.eurekaClientHttpRequestFactorySupplier); + this.args.eurekaClientHttpRequestFactorySupplier, this.args.restTemplateBuilderSupplier); } @Override @@ -50,7 +51,7 @@ public class RestTemplateTransportClientFactories implements TransportClientFact final Collection additionalFilters, final InstanceInfo myInstanceInfo, final Optional sslContext, final Optional hostnameVerifier) { return new RestTemplateTransportClientFactory(this.args.getSSLContext(), this.args.getHostnameVerifier(), - this.args.eurekaClientHttpRequestFactorySupplier); + this.args.eurekaClientHttpRequestFactorySupplier, this.args.restTemplateBuilderSupplier); } } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java index 1ba3cca84..bed106276 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2024 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. @@ -21,6 +21,7 @@ import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.Optional; +import java.util.function.Supplier; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; @@ -44,6 +45,7 @@ import com.netflix.discovery.shared.resolver.EurekaEndpoint; import com.netflix.discovery.shared.transport.EurekaHttpClient; import com.netflix.discovery.shared.transport.TransportClientFactory; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.cloud.configuration.SSLContextFactory; import org.springframework.cloud.configuration.TlsProperties; import org.springframework.http.HttpHeaders; @@ -63,6 +65,7 @@ import org.springframework.web.util.UriComponentsBuilder; * deserialization. * * @author Daniel Lavoie + * @author Armin Krezovic */ public class RestTemplateTransportClientFactory implements TransportClientFactory { @@ -72,11 +75,20 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor private final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier; + private final Supplier restTemplateBuilderSupplier; + public RestTemplateTransportClientFactory(TlsProperties tlsProperties, - EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) { + EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier, + Supplier restTemplateBuilderSupplier) { this.sslContext = context(tlsProperties); this.hostnameVerifier = Optional.empty(); this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier; + this.restTemplateBuilderSupplier = restTemplateBuilderSupplier; + } + + public RestTemplateTransportClientFactory(TlsProperties tlsProperties, + EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) { + this(tlsProperties, eurekaClientHttpRequestFactorySupplier, RestTemplateBuilder::new); } private Optional context(TlsProperties properties) { @@ -93,16 +105,23 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor public RestTemplateTransportClientFactory(Optional sslContext, Optional hostnameVerifier, - EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) { + EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier, + Supplier restTemplateBuilderSupplier) { this.sslContext = sslContext; this.hostnameVerifier = hostnameVerifier; this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier; + this.restTemplateBuilderSupplier = restTemplateBuilderSupplier; + } + + public RestTemplateTransportClientFactory(Optional sslContext, + Optional hostnameVerifier, + EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) { + + this(sslContext, hostnameVerifier, eurekaClientHttpRequestFactorySupplier, RestTemplateBuilder::new); } public RestTemplateTransportClientFactory() { - this.sslContext = Optional.empty(); - this.hostnameVerifier = Optional.empty(); - this.eurekaClientHttpRequestFactorySupplier = new DefaultEurekaClientHttpRequestFactorySupplier(); + this(Optional.empty(), Optional.empty(), new DefaultEurekaClientHttpRequestFactorySupplier()); } @Override @@ -120,7 +139,15 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor private RestTemplate restTemplate(String serviceUrl) { ClientHttpRequestFactory requestFactory = this.eurekaClientHttpRequestFactorySupplier .get(this.sslContext.orElse(null), this.hostnameVerifier.orElse(null)); - RestTemplate restTemplate = new RestTemplate(requestFactory); + + RestTemplate restTemplate; + + if (restTemplateBuilderSupplier != null && restTemplateBuilderSupplier.get() != null) { + restTemplate = restTemplateBuilderSupplier.get().requestFactory(() -> requestFactory).build(); + } + else { + restTemplate = new RestTemplate(requestFactory); + } try { URI serviceURI = new URI(serviceUrl);