diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java new file mode 100644 index 0000000000..ee4483caf2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-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. + * You may obtain a copy of the License at + * + * https://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.autoconfigure.http.client; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.http.client.HttpClientProperties.Factory; +import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundles; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.util.StringUtils; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for + * {@link ClientHttpRequestFactoryBuilder} and {@link ClientHttpRequestFactorySettings}. + * + * @author Phillip Webb + * @since 3.4.0 + */ +@AutoConfiguration(after = SslAutoConfiguration.class) +@ConditionalOnClass(ClientHttpRequestFactory.class) +@Conditional(NotReactiveWebApplicationCondition.class) +@EnableConfigurationProperties(HttpClientProperties.class) +public class HttpClientAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties) { + Factory factory = httpClientProperties.getFactory(); + return (factory != null) ? factory.builder() : ClientHttpRequestFactoryBuilder.detect(); + } + + @Bean + @ConditionalOnMissingBean + ClientHttpRequestFactorySettings clientHttpRequestFactorySettings(HttpClientProperties httpClientProperties, + ObjectProvider sslBundles) { + SslBundle sslBundle = getSslBundle(httpClientProperties.getSsl(), sslBundles); + return new ClientHttpRequestFactorySettings(httpClientProperties.getConnectTimeout(), + httpClientProperties.getReadTimeout(), sslBundle); + } + + private SslBundle getSslBundle(HttpClientProperties.Ssl properties, ObjectProvider sslBundles) { + String name = properties.getBundle(); + return (StringUtils.hasLength(name)) ? sslBundles.getObject().getBundle(name) : null; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientProperties.java new file mode 100644 index 0000000000..f11faf7aaa --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientProperties.java @@ -0,0 +1,145 @@ +/* + * Copyright 2012-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. + * You may obtain a copy of the License at + * + * https://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.autoconfigure.http.client; + +import java.time.Duration; +import java.util.function.Supplier; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; + +/** + * {@link ConfigurationProperties @ConfigurationProperties} for a Spring's blocking HTTP + * clients. + * + * @author Phillip Webb + * @since 3.4.0 + */ +@ConfigurationProperties("spring.http.client") +public class HttpClientProperties { + + /** + * Default factory used for a client HTTP request. + */ + private Factory factory; + + /** + * Default connect timeout for a client HTTP request. + */ + private Duration connectTimeout; + + /** + * Default read timeout for a client HTTP request. + */ + private Duration readTimeout; + + /** + * Default SSL configuration for a client HTTP request. + */ + private Ssl ssl = new Ssl(); + + public Factory getFactory() { + return this.factory; + } + + public void setFactory(Factory factory) { + this.factory = factory; + } + + public Duration getConnectTimeout() { + return this.connectTimeout; + } + + public void setConnectTimeout(Duration connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public Duration getReadTimeout() { + return this.readTimeout; + } + + public void setReadTimeout(Duration readTimeout) { + this.readTimeout = readTimeout; + } + + public Ssl getSsl() { + return this.ssl; + } + + /** + * Supported factory types. + */ + public enum Factory { + + /** + * Apache HttpComponents HttpClient. + */ + HTTP_COMPONENTS(ClientHttpRequestFactoryBuilder::httpComponents), + + /** + * Jetty's HttpClient. + */ + JETTY(ClientHttpRequestFactoryBuilder::jetty), + + /** + * Reactor-Netty. + */ + REACTOR(ClientHttpRequestFactoryBuilder::reactor), + + /** + * Java's HttpClient. + */ + JDK(ClientHttpRequestFactoryBuilder::jdk), + + /** + * Standard JDK facilities. + */ + SIMPLE(ClientHttpRequestFactoryBuilder::simple); + + private final Supplier> builderSupplier; + + Factory(Supplier> builderSupplier) { + this.builderSupplier = builderSupplier; + } + + ClientHttpRequestFactoryBuilder builder() { + return this.builderSupplier.get(); + } + + } + + /** + * SSL configuration. + */ + public static class Ssl { + + /** + * SSL bundle to use. + */ + private String bundle; + + public String getBundle() { + return this.bundle; + } + + public void setBundle(String bundle) { + this.bundle = bundle; + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/NotReactiveWebApplicationCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/NotReactiveWebApplicationCondition.java new file mode 100644 index 0000000000..1d7b8080c2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/NotReactiveWebApplicationCondition.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-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. + * You may obtain a copy of the License at + * + * https://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.autoconfigure.http.client; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.NoneNestedConditions; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; + +/** + * {@link SpringBootCondition} that applies only when running in a non-reactive web + * application. + * + * @author Phillip Webb + */ +class NotReactiveWebApplicationCondition extends NoneNestedConditions { + + NotReactiveWebApplicationCondition() { + super(ConfigurationPhase.PARSE_CONFIGURATION); + } + + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) + private static final class ReactiveWebApplication { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/package-info.java new file mode 100644 index 0000000000..b5a8eac3f2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + * You may obtain a copy of the License at + * + * https://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. + */ + +/** + * Auto-configuration for client-side HTTP. + */ +package org.springframework.boot.autoconfigure.http.client; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.java index 8fc9dd9663..0bdf8fd80a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -18,10 +18,10 @@ package org.springframework.boot.autoconfigure.web.client; import java.util.function.Consumer; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; -import org.springframework.boot.web.client.ClientHttpRequestFactories; -import org.springframework.boot.web.client.ClientHttpRequestFactorySettings; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.web.client.RestClient; @@ -32,9 +32,13 @@ import org.springframework.web.client.RestClient; */ class AutoConfiguredRestClientSsl implements RestClientSsl { + private final ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder; + private final SslBundles sslBundles; - AutoConfiguredRestClientSsl(SslBundles sslBundles) { + AutoConfiguredRestClientSsl(ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder, + SslBundles sslBundles) { + this.clientHttpRequestFactoryBuilder = clientHttpRequestFactoryBuilder; this.sslBundles = sslBundles; } @@ -46,8 +50,8 @@ class AutoConfiguredRestClientSsl implements RestClientSsl { @Override public Consumer fromBundle(SslBundle bundle) { return (builder) -> { - ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS.withSslBundle(bundle); - ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings); + ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(bundle); + ClientHttpRequestFactory requestFactory = this.clientHttpRequestFactoryBuilder.build(settings); builder.requestFactory(requestFactory); }; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java index 6281d47104..c2312ce790 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java @@ -24,10 +24,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; import org.springframework.boot.ssl.SslBundles; -import org.springframework.boot.web.client.ClientHttpRequestFactories; -import org.springframework.boot.web.client.ClientHttpRequestFactorySettings; import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -48,7 +49,8 @@ import org.springframework.web.client.RestClient.Builder; * @author Moritz Halbritter * @since 3.2.0 */ -@AutoConfiguration(after = { HttpMessageConvertersAutoConfiguration.class, SslAutoConfiguration.class }) +@AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, + SslAutoConfiguration.class }) @ConditionalOnClass(RestClient.class) @Conditional(NotReactiveWebApplicationCondition.class) public class RestClientAutoConfiguration { @@ -64,14 +66,21 @@ public class RestClientAutoConfiguration { @Bean @ConditionalOnMissingBean(RestClientSsl.class) @ConditionalOnBean(SslBundles.class) - AutoConfiguredRestClientSsl restClientSsl(SslBundles sslBundles) { - return new AutoConfiguredRestClientSsl(sslBundles); + AutoConfiguredRestClientSsl restClientSsl( + ObjectProvider> clientHttpRequestFactoryBuilder, SslBundles sslBundles) { + return new AutoConfiguredRestClientSsl( + clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect), sslBundles); } @Bean @ConditionalOnMissingBean - RestClientBuilderConfigurer restClientBuilderConfigurer(ObjectProvider customizerProvider) { + RestClientBuilderConfigurer restClientBuilderConfigurer( + ObjectProvider> clientHttpRequestFactoryBuilder, + ObjectProvider clientHttpRequestFactorySettings, + ObjectProvider customizerProvider) { RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); + configurer.setRequestFactoryBuilder(clientHttpRequestFactoryBuilder.getIfAvailable()); + configurer.setRequestFactorySettings(clientHttpRequestFactorySettings.getIfAvailable()); configurer.setRestClientCustomizers(customizerProvider.orderedStream().toList()); return configurer; } @@ -80,9 +89,7 @@ public class RestClientAutoConfiguration { @Scope("prototype") @ConditionalOnMissingBean RestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) { - RestClient.Builder builder = RestClient.builder() - .requestFactory(ClientHttpRequestFactories.get(ClientHttpRequestFactorySettings.DEFAULTS)); - return restClientBuilderConfigurer.configure(builder); + return restClientBuilderConfigurer.configure(RestClient.builder()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java index 3fe41101b7..56e8b47813 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.web.client; import java.util.List; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient.Builder; @@ -30,8 +32,20 @@ import org.springframework.web.client.RestClient.Builder; */ public class RestClientBuilderConfigurer { + private ClientHttpRequestFactoryBuilder requestFactoryBuilder; + + private ClientHttpRequestFactorySettings requestFactorySettings; + private List customizers; + void setRequestFactoryBuilder(ClientHttpRequestFactoryBuilder requestFactoryBuilder) { + this.requestFactoryBuilder = requestFactoryBuilder; + } + + void setRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) { + this.requestFactorySettings = requestFactorySettings; + } + void setRestClientCustomizers(List customizers) { this.customizers = customizers; } @@ -43,6 +57,9 @@ public class RestClientBuilderConfigurer { * @return the configured builder */ public RestClient.Builder configure(RestClient.Builder builder) { + ClientHttpRequestFactoryBuilder requestFactoryBuilder = (this.requestFactoryBuilder != null) + ? this.requestFactoryBuilder : ClientHttpRequestFactoryBuilder.detect(); + builder.requestFactory(requestFactoryBuilder.build(this.requestFactorySettings)); applyCustomizers(builder); return builder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientSsl.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientSsl.java index fd892efb43..9477088eba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientSsl.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientSsl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -18,10 +18,9 @@ package org.springframework.boot.autoconfigure.web.client; import java.util.function.Consumer; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.ssl.NoSuchSslBundleException; import org.springframework.boot.ssl.SslBundle; -import org.springframework.boot.web.client.ClientHttpRequestFactories; -import org.springframework.boot.web.client.ClientHttpRequestFactorySettings; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.web.client.RestClient; @@ -38,8 +37,7 @@ import org.springframework.web.client.RestClient; * NOTE: Apply SSL configuration will replace any previously * {@link RestClient.Builder#requestFactory configured} {@link ClientHttpRequestFactory}. * If you need to configure {@link ClientHttpRequestFactory} with more than just SSL - * consider using a {@link ClientHttpRequestFactorySettings} with - * {@link ClientHttpRequestFactories}. + * consider using a {@link ClientHttpRequestFactoryBuilder}. * * @author Phillip Webb * @since 3.2.0 diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.java index f6fc73629a..a4879185a4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.java @@ -23,6 +23,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.boot.web.client.RestTemplateRequestCustomizer; @@ -32,13 +35,14 @@ import org.springframework.context.annotation.Lazy; import org.springframework.web.client.RestTemplate; /** - * {@link EnableAutoConfiguration Auto-configuration} for {@link RestTemplate}. + * {@link EnableAutoConfiguration Auto-configuration} for {@link RestTemplate} (via + * {@link RestTemplateBuilder}). * * @author Stephane Nicoll * @author Phillip Webb * @since 1.4.0 */ -@AutoConfiguration(after = HttpMessageConvertersAutoConfiguration.class) +@AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class }) @ConditionalOnClass(RestTemplate.class) @Conditional(NotReactiveWebApplicationCondition.class) public class RestTemplateAutoConfiguration { @@ -46,10 +50,14 @@ public class RestTemplateAutoConfiguration { @Bean @Lazy public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer( + ObjectProvider> clientHttpRequestFactoryBuilder, + ObjectProvider clientHttpRequestFactorySettings, ObjectProvider messageConverters, ObjectProvider restTemplateCustomizers, ObjectProvider> restTemplateRequestCustomizers) { RestTemplateBuilderConfigurer configurer = new RestTemplateBuilderConfigurer(); + configurer.setRequestFactoryBuilder(clientHttpRequestFactoryBuilder.getIfAvailable()); + configurer.setRequestFactorySettings(clientHttpRequestFactorySettings.getIfAvailable()); configurer.setHttpMessageConverters(messageConverters.getIfUnique()); configurer.setRestTemplateCustomizers(restTemplateCustomizers.orderedStream().toList()); configurer.setRestTemplateRequestCustomizers(restTemplateRequestCustomizers.orderedStream().toList()); @@ -60,8 +68,7 @@ public class RestTemplateAutoConfiguration { @Lazy @ConditionalOnMissingBean public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) { - RestTemplateBuilder builder = new RestTemplateBuilder(); - return restTemplateBuilderConfigurer.configure(builder); + return restTemplateBuilderConfigurer.configure(new RestTemplateBuilder()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateBuilderConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateBuilderConfigurer.java index 55e3351b85..5e09b490d7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateBuilderConfigurer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateBuilderConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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,8 @@ import java.util.List; import java.util.function.BiFunction; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.boot.web.client.RestTemplateRequestCustomizer; @@ -34,12 +36,24 @@ import org.springframework.util.ObjectUtils; */ public final class RestTemplateBuilderConfigurer { + private ClientHttpRequestFactoryBuilder requestFactoryBuilder; + + private ClientHttpRequestFactorySettings requestFactorySettings; + private HttpMessageConverters httpMessageConverters; private List restTemplateCustomizers; private List> restTemplateRequestCustomizers; + void setRequestFactoryBuilder(ClientHttpRequestFactoryBuilder requestFactoryBuilder) { + this.requestFactoryBuilder = requestFactoryBuilder; + } + + void setRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) { + this.requestFactorySettings = requestFactorySettings; + } + void setHttpMessageConverters(HttpMessageConverters httpMessageConverters) { this.httpMessageConverters = httpMessageConverters; } @@ -59,6 +73,12 @@ public final class RestTemplateBuilderConfigurer { * @return the configured builder */ public RestTemplateBuilder configure(RestTemplateBuilder builder) { + if (this.requestFactoryBuilder != null) { + builder = builder.requestFactoryBuilder(this.requestFactoryBuilder); + } + if (this.requestFactorySettings != null) { + builder = builder.requestFactorySettings(this.requestFactorySettings); + } if (this.httpMessageConverters != null) { builder = builder.messageConverters(this.httpMessageConverters.getConverters()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfiguration.java index 518347f8af..998051c68e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -23,6 +23,10 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.webservices.client.WebServiceMessageSenderFactory; import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer; import org.springframework.context.annotation.Bean; @@ -36,20 +40,35 @@ import org.springframework.ws.client.core.WebServiceTemplate; * @author Dmytro Nosan * @since 2.1.0 */ -@AutoConfiguration +@AutoConfiguration(after = HttpClientAutoConfiguration.class) @ConditionalOnClass({ WebServiceTemplate.class, Unmarshaller.class, Marshaller.class }) public class WebServiceTemplateAutoConfiguration { + @Bean + @ConditionalOnMissingBean + public WebServiceMessageSenderFactory webServiceHttpMessageSenderFactory( + ObjectProvider> clientHttpRequestFactoryBuilder, + ObjectProvider clientHttpRequestFactorySettings) { + return WebServiceMessageSenderFactory.http( + clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect), + clientHttpRequestFactorySettings.getIfAvailable()); + } + @Bean @ConditionalOnMissingBean public WebServiceTemplateBuilder webServiceTemplateBuilder( + ObjectProvider httpWebServiceMessageSenderBuilder, ObjectProvider webServiceTemplateCustomizers) { - WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + WebServiceTemplateBuilder templateBuilder = new WebServiceTemplateBuilder(); + WebServiceMessageSenderFactory httpMessageSenderFactory = httpWebServiceMessageSenderBuilder.getIfAvailable(); + if (httpMessageSenderFactory != null) { + templateBuilder = templateBuilder.httpMessageSenderFactory(httpMessageSenderFactory); + } List customizers = webServiceTemplateCustomizers.orderedStream().toList(); if (!customizers.isEmpty()) { - builder = builder.customizers(customizers); + templateBuilder = templateBuilder.customizers(customizers); } - return builder; + return templateBuilder; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 4250c7a355..05740b676a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -62,6 +62,7 @@ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration +org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java new file mode 100644 index 0000000000..8f3849354b --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-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. + * You may obtain a copy of the License at + * + * https://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.autoconfigure.http.client; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HttpClientAutoConfiguration}. + * + * @author Phillip Webb + */ +class HttpClientAutoConfigurationTests { + + private static final AutoConfigurations autoConfigurations = AutoConfigurations + .of(HttpClientAutoConfiguration.class, SslAutoConfiguration.class); + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(autoConfigurations); + + @Test + void configuresDetectedClientHttpRequestFactoryBuilder() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ClientHttpRequestFactoryBuilder.class)); + } + + @Test + void configuresDefinedClientHttpRequestFactoryBuilder() { + this.contextRunner.withPropertyValues("spring.http.client.factory=simple") + .run((context) -> assertThat(context.getBean(ClientHttpRequestFactoryBuilder.class)) + .isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class)); + } + + @Test + void configuresClientHttpRequestFactorySettings() { + this.contextRunner.withPropertyValues(sslPropertyValues().toArray(String[]::new)) + .withPropertyValues("spring.http.client.connect-timeout=10s", "spring.http.client.read-timeout=20s", + "spring.http.client.ssl.bundle=test") + .run((context) -> { + ClientHttpRequestFactorySettings settings = context.getBean(ClientHttpRequestFactorySettings.class); + assertThat(settings.connectTimeout()).isEqualTo(Duration.ofSeconds(10)); + assertThat(settings.readTimeout()).isEqualTo(Duration.ofSeconds(20)); + assertThat(settings.sslBundle().getKey().getAlias()).isEqualTo("alias1"); + }); + } + + private List sslPropertyValues() { + List propertyValues = new ArrayList<>(); + String location = "classpath:org/springframework/boot/autoconfigure/ssl/"; + propertyValues.add("spring.ssl.bundle.pem.test.key.alias=alias1"); + propertyValues.add("spring.ssl.bundle.pem.test.truststore.type=PKCS12"); + propertyValues.add("spring.ssl.bundle.pem.test.truststore.certificate=" + location + "rsa-cert.pem"); + propertyValues.add("spring.ssl.bundle.pem.test.truststore.private-key=" + location + "rsa-key.pem"); + return propertyValues; + } + + @Test + void whenReactiveWebApplicationBeansAreNotConfigured() { + new ReactiveWebApplicationContextRunner().withConfiguration(autoConfigurations) + .run((context) -> assertThat(context).doesNotHaveBean(ClientHttpRequestFactoryBuilder.class) + .doesNotHaveBean(ClientHttpRequestFactorySettings.class)); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientPropertiesTests.java new file mode 100644 index 0000000000..16d66577ab --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientPropertiesTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-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. + * You may obtain a copy of the License at + * + * https://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.autoconfigure.http.client; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.http.client.HttpClientProperties.Factory; +import org.springframework.boot.http.client.HttpComponentsClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.JdkClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.JettyClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ReactorClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HttpClientProperties}. + * + * @author Phillip Webb + */ +class HttpClientPropertiesTests { + + @Nested + class FactoryTests { + + @Test + void httpComponentsBuilder() { + assertThat(Factory.HTTP_COMPONENTS.builder()) + .isInstanceOf(HttpComponentsClientHttpRequestFactoryBuilder.class); + } + + @Test + void jettyBuilder() { + assertThat(Factory.JETTY.builder()).isInstanceOf(JettyClientHttpRequestFactoryBuilder.class); + } + + @Test + void reactorBuilder() { + assertThat(Factory.REACTOR.builder()).isInstanceOf(ReactorClientHttpRequestFactoryBuilder.class); + } + + @Test + void jdkBuilder() { + assertThat(Factory.JDK.builder()).isInstanceOf(JdkClientHttpRequestFactoryBuilder.class); + } + + @Test + void simpleBuilder() { + assertThat(Factory.SIMPLE.builder()).isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java index 576d6e4580..b5bb408f59 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -23,12 +23,14 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.test.util.ReflectionTestUtils; @@ -49,7 +51,7 @@ import static org.mockito.Mockito.mock; class RestClientAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class, HttpClientAutoConfiguration.class)); @Test void shouldSupplyBeans() { @@ -156,6 +158,19 @@ class RestClientAutoConfigurationTests { }); } + @Test + void whenHasFactoryProperty() { + this.contextRunner.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class)) + .withUserConfiguration(RestClientConfig.class) + .withPropertyValues("spring.http.client.factory=simple") + .run((context) -> { + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); + assertThat(restClient).extracting("clientRequestFactory") + .isInstanceOf(SimpleClientHttpRequestFactory.class); + }); + } + @Configuration(proxyBeanMethods = false) static class CodecConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfigurationTests.java index 5827529259..43d718ab41 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -25,6 +25,7 @@ import org.springframework.beans.factory.support.BeanDefinitionOverrideException import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; @@ -36,6 +37,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.mock.http.client.MockClientHttpRequest; @@ -56,8 +58,8 @@ import static org.mockito.Mockito.mock; */ class RestTemplateAutoConfigurationTests { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class)); + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(RestTemplateAutoConfiguration.class, HttpClientAutoConfiguration.class)); @Test void restTemplateBuilderConfigurerShouldBeLazilyDefined() { @@ -191,6 +193,18 @@ class RestTemplateAutoConfigurationTests { .doesNotHaveBean(RestTemplateBuilderConfigurer.class)); } + @Test + void whenHasFactoryProperty() { + this.contextRunner.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class)) + .withUserConfiguration(RestTemplateConfig.class) + .withPropertyValues("spring.http.client.factory=simple") + .run((context) -> { + assertThat(context).hasSingleBean(RestTemplate.class); + RestTemplate restTemplate = context.getBean(RestTemplate.class); + assertThat(restTemplate.getRequestFactory()).isInstanceOf(SimpleClientHttpRequestFactory.class); + }); + } + @Configuration(proxyBeanMethods = false) static class RestTemplateConfig { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java index f1bd27163c..1ca35447a0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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,8 @@ import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ContextConsumer; @@ -28,6 +30,7 @@ import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; import org.springframework.oxm.jaxb.Jaxb2Marshaller; @@ -45,8 +48,8 @@ import static org.assertj.core.api.Assertions.assertThat; */ class WebServiceTemplateAutoConfigurationTests { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(WebServiceTemplateAutoConfiguration.class)); + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(WebServiceTemplateAutoConfiguration.class, HttpClientAutoConfiguration.class)); @Test void autoConfiguredBuilderShouldNotHaveMarshallerAndUnmarshaller() { @@ -93,6 +96,19 @@ class WebServiceTemplateAutoConfigurationTests { .run((context) -> assertThat(context).hasNotFailed()); } + @Test + void whenHasFactoryProperty() { + this.contextRunner.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class)) + .withPropertyValues("spring.http.client.factory=simple") + .run(assertWebServiceTemplateBuilder((builder) -> { + WebServiceTemplate webServiceTemplate = builder.build(); + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + ClientHttpRequestMessageSender messageSender = (ClientHttpRequestMessageSender) webServiceTemplate + .getMessageSenders()[0]; + assertThat(messageSender.getRequestFactory()).isInstanceOf(SimpleClientHttpRequestFactory.class); + })); + } + private ContextConsumer assertWebServiceTemplateBuilder( Consumer builder) { return (context) -> { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationPortTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationPortTests.java index 8073080664..2b3cbdfa60 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationPortTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationPortTests.java @@ -36,7 +36,8 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Dave Syer */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port:0" }) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "management.server.port:0", "spring.http.client.factory=simple" }) class SampleActuatorUiApplicationPortTests { @LocalServerPort diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationTests.java index a5199c4b0f..93a078a633 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-ui/src/test/java/smoketest/actuator/ui/SampleActuatorUiApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -39,7 +39,8 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Dave Syer */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "server.error.include-message=always" }) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "server.error.include-message=always", "spring.http.client.factory=simple" }) class SampleActuatorUiApplicationTests { @Autowired @@ -50,7 +51,7 @@ class SampleActuatorUiApplicationTests { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); ResponseEntity entity = this.restTemplate.withBasicAuth("user", getPassword()) - .exchange("/", HttpMethod.GET, new HttpEntity(headers), String.class); + .exchange("/", HttpMethod.GET, new HttpEntity<>(headers), String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).contains("Hello"); } @@ -74,7 +75,7 @@ class SampleActuatorUiApplicationTests { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); ResponseEntity<String> entity = this.restTemplate.withBasicAuth("user", getPassword()) - .exchange("/error", HttpMethod.GET, new HttpEntity<Void>(headers), String.class); + .exchange("/error", HttpMethod.GET, new HttpEntity<>(headers), String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); assertThat(entity.getBody()).contains("<html>") .contains("<body>") diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java index cfd21a8320..17d9fc58f0 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-jdbc/src/test/java/smoketest/session/SampleSessionJdbcApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -48,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Madhura Bhave */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = "server.servlet.session.timeout:2") + properties = { "server.servlet.session.timeout:2", "spring.http.client.factory=simple" }) class SampleSessionJdbcApplicationTests { @Autowired diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-web-method-security/src/test/java/smoketest/security/method/SampleMethodSecurityApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-web-method-security/src/test/java/smoketest/security/method/SampleMethodSecurityApplicationTests.java index 48309081eb..d78199090f 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-web-method-security/src/test/java/smoketest/security/method/SampleMethodSecurityApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-web-method-security/src/test/java/smoketest/security/method/SampleMethodSecurityApplicationTests.java @@ -43,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Dave Syer * @author Scott Frederick */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.http.client.factory=simple") class SampleMethodSecurityApplicationTests { @LocalServerPort