From 3bbfee5e938f03048c1b8b0cc976b536633df73f Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 29 Jun 2023 15:11:20 +0200 Subject: [PATCH 1/2] Support JDK HttpClient in ClientHttpRequestFactories See gh-36118 --- .../client/ClientHttpRequestFactories.java | 37 +++++++++++++ ...entHttpRequestFactoriesJdkClientTests.java | 52 +++++++++++++++++++ ...ClientHttpRequestFactoriesSimpleTests.java | 4 +- ...geSenderBuilderSimpleIntegrationTests.java | 2 +- 4 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java index 347f6c4b0c..63b6595ae9 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java @@ -49,6 +49,7 @@ import org.springframework.boot.ssl.SslOptions; import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.JdkClientHttpRequestFactory; import org.springframework.http.client.JettyClientHttpRequestFactory; import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; @@ -79,6 +80,10 @@ public final class ClientHttpRequestFactories { private static final boolean JETTY_CLIENT_PRESENT = ClassUtils.isPresent(JETTY_CLIENT_CLASS, null); + static final String JDK_CLIENT_CLASS = "java.net.http.HttpClient"; + + private static final boolean JDK_CLIENT_PRESENT = ClassUtils.isPresent(JDK_CLIENT_CLASS, null); + private ClientHttpRequestFactories() { } @@ -99,6 +104,9 @@ public final class ClientHttpRequestFactories { if (JETTY_CLIENT_PRESENT) { return Jetty.get(settings); } + if (JDK_CLIENT_PRESENT) { + return Jdk.get(settings); + } return Simple.get(settings); } @@ -126,6 +134,9 @@ public final class ClientHttpRequestFactories { if (requestFactoryType == JettyClientHttpRequestFactory.class) { return (T) Jetty.get(settings); } + if (requestFactoryType == JdkClientHttpRequestFactory.class) { + return (T) Jdk.get(settings); + } if (requestFactoryType == SimpleClientHttpRequestFactory.class) { return (T) Simple.get(settings); } @@ -254,6 +265,32 @@ public final class ClientHttpRequestFactories { } + /** + * Support for {@link JdkClientHttpRequestFactory}. + */ + static class Jdk { + + static JdkClientHttpRequestFactory get(ClientHttpRequestFactorySettings settings) { + java.net.http.HttpClient httpClient = createHttpClient(settings.connectTimeout(), settings.sslBundle()); + JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory(httpClient); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout); + return requestFactory; + } + + private static java.net.http.HttpClient createHttpClient(Duration connectTimeout, SslBundle sslBundle) { + java.net.http.HttpClient.Builder builder = java.net.http.HttpClient.newBuilder(); + if (connectTimeout != null) { + builder.connectTimeout(connectTimeout); + } + if (sslBundle != null) { + builder.sslContext(sslBundle.createSslContext()); + } + return builder.build(); + } + + } + /** * Support for {@link SimpleClientHttpRequestFactory}. */ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java new file mode 100644 index 0000000000..a3dd693ec7 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2023 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.web.client; + +import java.net.http.HttpClient; +import java.time.Duration; + +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; +import org.springframework.http.client.JdkClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; + +/** + * Tests for {@link ClientHttpRequestFactories} when JDK HttpClient is the + * predominant HTTP client. + * + * @author Andy Wilkinson + */ +@ClassPathExclusions({ "httpclient5-*.jar", "okhttp-*.jar" }) +class ClientHttpRequestFactoriesJdkClientTests + extends AbstractClientHttpRequestFactoriesTests { + + ClientHttpRequestFactoriesJdkClientTests() { + super(JdkClientHttpRequestFactory.class); + } + + @Override + protected long connectTimeout(JdkClientHttpRequestFactory requestFactory) { + HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient"); + return httpClient.connectTimeout().map(Duration::toMillis).orElse(-1L); + } + + @Override + @SuppressWarnings("unchecked") + protected long readTimeout(JdkClientHttpRequestFactory requestFactory) { + return ((Duration) ReflectionTestUtils.getField(requestFactory, "readTimeout")).toMillis(); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java index f00882bc7d..189901f20c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -26,7 +26,7 @@ import org.springframework.test.util.ReflectionTestUtils; * * @author Andy Wilkinson */ -@ClassPathExclusions({ "httpclient5-*.jar", "okhttp-*.jar", "jetty-client-*.jar" }) +@ClassPathExclusions(files = {"httpclient5-*.jar", "jetty-client-*.jar", "okhttp-*.jar"}, packages = "java.net.http") class ClientHttpRequestFactoriesSimpleTests extends AbstractClientHttpRequestFactoriesTests { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java index 0014d47cd7..5872c3a092 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java @@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Stephane Nicoll */ -@ClassPathExclusions(files = { "httpclient5-*.jar", "jetty-client-*.jar", "okhttp*.jar" }) +@ClassPathExclusions(files = { "httpclient5-*.jar", "jetty-client-*.jar", "okhttp*.jar" }, packages = "java.net.http") class HttpWebServiceMessageSenderBuilderSimpleIntegrationTests { private final HttpWebServiceMessageSenderBuilder builder = new HttpWebServiceMessageSenderBuilder(); From bb2c4cc742b9b2770ea3809d8c0d9b74855d514e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 4 Jul 2023 18:12:27 +0100 Subject: [PATCH 2/2] Polish "Support JDK HttpClient in ClientHttpRequestFactories" See gh-36118 --- .../client/ClientHttpRequestFactories.java | 7 --- ...entHttpRequestFactoriesJdkClientTests.java | 52 ------------------- ...ClientHttpRequestFactoriesSimpleTests.java | 2 +- .../ClientHttpRequestFactoriesTests.java | 8 +++ ...geSenderBuilderSimpleIntegrationTests.java | 2 +- 5 files changed, 10 insertions(+), 61 deletions(-) delete mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java index 63b6595ae9..1af4d92bc9 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java @@ -80,10 +80,6 @@ public final class ClientHttpRequestFactories { private static final boolean JETTY_CLIENT_PRESENT = ClassUtils.isPresent(JETTY_CLIENT_CLASS, null); - static final String JDK_CLIENT_CLASS = "java.net.http.HttpClient"; - - private static final boolean JDK_CLIENT_PRESENT = ClassUtils.isPresent(JDK_CLIENT_CLASS, null); - private ClientHttpRequestFactories() { } @@ -104,9 +100,6 @@ public final class ClientHttpRequestFactories { if (JETTY_CLIENT_PRESENT) { return Jetty.get(settings); } - if (JDK_CLIENT_PRESENT) { - return Jdk.get(settings); - } return Simple.get(settings); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java deleted file mode 100644 index a3dd693ec7..0000000000 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesJdkClientTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012-2023 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.web.client; - -import java.net.http.HttpClient; -import java.time.Duration; - -import org.springframework.boot.testsupport.classpath.ClassPathExclusions; -import org.springframework.http.client.JdkClientHttpRequestFactory; -import org.springframework.test.util.ReflectionTestUtils; - -/** - * Tests for {@link ClientHttpRequestFactories} when JDK HttpClient is the - * predominant HTTP client. - * - * @author Andy Wilkinson - */ -@ClassPathExclusions({ "httpclient5-*.jar", "okhttp-*.jar" }) -class ClientHttpRequestFactoriesJdkClientTests - extends AbstractClientHttpRequestFactoriesTests { - - ClientHttpRequestFactoriesJdkClientTests() { - super(JdkClientHttpRequestFactory.class); - } - - @Override - protected long connectTimeout(JdkClientHttpRequestFactory requestFactory) { - HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient"); - return httpClient.connectTimeout().map(Duration::toMillis).orElse(-1L); - } - - @Override - @SuppressWarnings("unchecked") - protected long readTimeout(JdkClientHttpRequestFactory requestFactory) { - return ((Duration) ReflectionTestUtils.getField(requestFactory, "readTimeout")).toMillis(); - } - -} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java index 189901f20c..bb44254845 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesSimpleTests.java @@ -26,7 +26,7 @@ import org.springframework.test.util.ReflectionTestUtils; * * @author Andy Wilkinson */ -@ClassPathExclusions(files = {"httpclient5-*.jar", "jetty-client-*.jar", "okhttp-*.jar"}, packages = "java.net.http") +@ClassPathExclusions({ "httpclient5-*.jar", "jetty-client-*.jar", "okhttp-*.jar" }) class ClientHttpRequestFactoriesSimpleTests extends AbstractClientHttpRequestFactoriesTests { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesTests.java index 546b862d98..239023e8b9 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/ClientHttpRequestFactoriesTests.java @@ -27,6 +27,7 @@ import org.springframework.http.client.BufferingClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.JdkClientHttpRequestFactory; import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; @@ -75,6 +76,13 @@ class ClientHttpRequestFactoriesTests { assertThat(requestFactory).isInstanceOf(OkHttp3ClientHttpRequestFactory.class); } + @Test + void getOfJdkFactoryReturnsJdkFactory() { + ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(JdkClientHttpRequestFactory.class, + ClientHttpRequestFactorySettings.DEFAULTS); + assertThat(requestFactory).isInstanceOf(JdkClientHttpRequestFactory.class); + } + @Test void getOfUnknownTypeCreatesFactory() { ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(TestClientHttpRequestFactory.class, diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java index 5872c3a092..6c3a0b2ef1 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java @@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Stephane Nicoll */ -@ClassPathExclusions(files = { "httpclient5-*.jar", "jetty-client-*.jar", "okhttp*.jar" }, packages = "java.net.http") +@ClassPathExclusions({ "httpclient5-*.jar", "jetty-client-*.jar", "okhttp*.jar" }) class HttpWebServiceMessageSenderBuilderSimpleIntegrationTests { private final HttpWebServiceMessageSenderBuilder builder = new HttpWebServiceMessageSenderBuilder();