diff --git a/apache-httpclient/pom.xml b/apache-httpclient/pom.xml
index 47dd41dce7..c371d1fc06 100644
--- a/apache-httpclient/pom.xml
+++ b/apache-httpclient/pom.xml
@@ -120,4 +120,4 @@
5.2
-
\ No newline at end of file
+
diff --git a/core-java-modules/core-java-httpclient/pom.xml b/core-java-modules/core-java-httpclient/pom.xml
index 57b23e96c1..3df0447ff0 100644
--- a/core-java-modules/core-java-httpclient/pom.xml
+++ b/core-java-modules/core-java-httpclient/pom.xml
@@ -32,6 +32,12 @@
${assertj.version}
test
+
+ com.github.tomakehurst
+ wiremock
+ ${wiremock.version}
+ test
+
@@ -53,6 +59,7 @@
11
3.22.0
5.11.2
+ 2.27.2
\ No newline at end of file
diff --git a/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/HttpClientConnectionManagementUnitTest.java b/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/HttpClientConnectionManagementUnitTest.java
new file mode 100644
index 0000000000..3b7610276c
--- /dev/null
+++ b/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/HttpClientConnectionManagementUnitTest.java
@@ -0,0 +1,147 @@
+package com.baeldung.httpclient.conn;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.List;
+
+import static java.net.URI.create;
+
+
+public class HttpClientConnectionManagementUnitTest {
+
+ WireMockConfiguration firstConfiguration = WireMockConfiguration
+ .options()
+ .dynamicPort();
+ WireMockConfiguration secondConfiguration = WireMockConfiguration
+ .options()
+ .dynamicPort();
+ WireMockServer firstServer = new WireMockServer(firstConfiguration);
+ WireMockServer secondServer = new WireMockServer(secondConfiguration);
+ private String firstUrl;
+ private String secondUrl;
+
+ private HttpClient client = HttpClient.newHttpClient();
+ private HttpClient secondClient = HttpClient.newHttpClient();
+
+ private HttpRequest getRequest;
+ private HttpRequest secondGet;
+
+ @Before
+ public void setup() {
+ firstServer.start();
+ secondServer.start();
+
+ // add some request matchers
+ firstServer.stubFor(WireMock
+ .get(WireMock.anyUrl())
+ .willReturn(WireMock
+ .aResponse()
+ .withStatus(200)));
+ secondServer.stubFor(WireMock
+ .get(WireMock.anyUrl())
+ .willReturn(WireMock
+ .aResponse()
+ .withStatus(200)));
+
+ firstUrl = "http://localhost:" + firstServer.port() + "/first";
+ secondUrl = "http://localhost:" + secondServer.port() + "/second";
+
+ getRequest = HttpRequest
+ .newBuilder()
+ .uri(create(firstUrl))
+ .version(HttpClient.Version.HTTP_1_1)
+ .build();
+
+ secondGet = HttpRequest
+ .newBuilder()
+ .uri(create(secondUrl))
+ .version(HttpClient.Version.HTTP_1_1)
+ .build();
+ }
+
+ @After
+ public void tearDown() {
+ // display all the requests that the WireMock servers handled
+ List firstWiremockAllServeEvents = firstServer.getAllServeEvents();
+ List secondWiremockAllServeEvents = secondServer.getAllServeEvents();
+ firstWiremockAllServeEvents
+ .stream()
+ .map(event -> event
+ .getRequest()
+ .getAbsoluteUrl())
+ .forEach(System.out::println);
+ secondWiremockAllServeEvents
+ .stream()
+ .map(event -> event
+ .getRequest()
+ .getAbsoluteUrl())
+ .forEach(System.out::println);
+
+ // stop the WireMock servers
+ firstServer.stop();
+ secondServer.stop();
+ }
+
+ // Example 1. Use an HttpClient to connect to the same endpoint - reuses a connection from the internal pool
+ @Test
+ public final void givenAnHttpClient_whenTwoConnectionsToSameEndpointMadeSequentially_thenConnectionReused() throws InterruptedException, IOException {
+
+ // given two requests to the same destination
+ final HttpResponse firstResponse = client.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ final HttpResponse secondResponse = client.send(getRequest, HttpResponse.BodyHandlers.ofString());
+
+ assert (firstResponse.statusCode() == 200) && (secondResponse.statusCode() == 200);
+ }
+
+ // Example 2. Use separate HttpClients to connect to the same endpoint - creates a connection per client
+ @Test
+ public final void givenTwoHttpClients_whenEachClientMakesConnectionSequentially_thenConnectionCreatedForEach() throws InterruptedException, IOException {
+
+ // given requests from two different client to same destination
+ final HttpResponse firstResponse = client.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ final HttpResponse secondResponse = secondClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+
+ assert (firstResponse.statusCode() == 200) && (secondResponse.statusCode() == 200);
+ }
+
+ // Example 3. Use an HttpClient to Connect to first, second, then first endpoint again.
+ // New connections made each time when pool size is 1, or re-used when not restricted.
+ // Make sure to set the JVM arg when running the test:
+ // -Djdk.httpclient.connectionPoolSize=1
+ @Test
+ public final void givenAnHttpClientAndAPoolSizeOfOne_whenTwoConnectionsMadeBackToOriginal_thenFirstConnectionPurged() throws InterruptedException, IOException {
+
+ // given 3 requests, two to the first server and one to the second server
+ final HttpResponse firstResponse = client.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ final HttpResponse secondResponse = client.send(secondGet, HttpResponse.BodyHandlers.ofString());
+ final HttpResponse thirdResponse = client.send(getRequest, HttpResponse.BodyHandlers.ofString());
+
+ assert ((firstResponse.statusCode() == 200) && (secondResponse.statusCode() == 200) && (thirdResponse.statusCode() == 200));
+ }
+
+ // Example 4. Use an HttpClient to connect, wait for connection keepalive to pass, then connect again. New connection made for both calls.
+ // Make sure to set the JVM arg when running the test:
+ // -Djdk.httpclient.keepalive.timeout=2
+ @Test
+ public final void givenAnHttpClientAndConnectionKeepAliveOfTwoSeconds_whenCallMadeAfterKeepaliveExpires_thenNewConnection() throws InterruptedException, IOException {
+
+ // given 2 requests to the same destination with the second call made after the keepalive timeout has passed
+ final HttpResponse firstResponse = client.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ Thread.sleep(3000); // exceeds 2 seconds configured by JVM arg
+ final HttpResponse secondResponse = client.send(getRequest, HttpResponse.BodyHandlers.ofString());
+
+ assert ((firstResponse.statusCode() == 200) && (secondResponse.statusCode() == 200));
+ }
+
+}
+
diff --git a/core-java-modules/core-java-httpclient/src/test/resources/jetty-logging.properties b/core-java-modules/core-java-httpclient/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000000..a21bdbcd09
--- /dev/null
+++ b/core-java-modules/core-java-httpclient/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StrErrLog
+org.eclipse.jetty.LEVEL=DEBUG
+jetty.logs=logs
\ No newline at end of file
diff --git a/javaxval/pom.xml b/javaxval/pom.xml
index 76472e29fb..1feed71abb 100644
--- a/javaxval/pom.xml
+++ b/javaxval/pom.xml
@@ -46,7 +46,7 @@
test
-
+