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 - +