diff --git a/httpclient/src/test/java/org/baeldung/httpclient/HttpClientConnectionManagementTest.java b/httpclient/src/test/java/org/baeldung/httpclient/HttpClientConnectionManagementTest.java index 5096725ece..37f7b07145 100644 --- a/httpclient/src/test/java/org/baeldung/httpclient/HttpClientConnectionManagementTest.java +++ b/httpclient/src/test/java/org/baeldung/httpclient/HttpClientConnectionManagementTest.java @@ -1,29 +1,373 @@ package org.baeldung.httpclient; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import org.apache.http.HeaderElement; +import org.apache.http.HeaderElementIterator; +import org.apache.http.HttpClientConnection; import org.apache.http.HttpException; import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.BasicHttpClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicHeaderElementIterator; +import org.apache.http.protocol.HTTP; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpCoreContext; +import org.apache.http.protocol.HttpRequestExecutor; +import org.apache.http.util.EntityUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; -@SuppressWarnings("unused") + public class HttpClientConnectionManagementTest { + private BasicHttpClientConnectionManager basicConnManager; + private HttpClientContext context; + private HttpRoute route; + private static final String SERVER1 = "http://echo.200please.com"; + private static final String SERVER7 = "http://localhost"; + private HttpGet get1; + private HttpGet get2; + private static CloseableHttpResponse response; + private HttpClientConnection conn1; + private HttpClientConnection conn; + private HttpClientConnection conn2; + private PoolingHttpClientConnectionManager poolingConnManager; + private CloseableHttpClient client; + + @Before + public final void before() { + get1 = new HttpGet(SERVER1); + get2 = new HttpGet(SERVER7); + route = new HttpRoute(new HttpHost("localhost", 80)); + } + + @After + public final void after() throws IllegalStateException, IOException { + if (conn != null) + conn.close(); + if (conn1 != null) + conn1.close(); + if (conn2 != null) + conn2.close(); + if (poolingConnManager != null) + poolingConnManager.shutdown(); + if (basicConnManager != null) + basicConnManager.shutdown(); + if (client != null) + client.close(); + if (response != null) + response.close(); + + } // tests @Test + @Ignore + // 2.1 IN ARTCLE public final void whenLowLevelConnectionIsEstablished_thenNoExceptions() throws IOException, HttpException, InterruptedException, ExecutionException { - final HttpClientContext context = HttpClientContext.create(); - final BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(); - final HttpRoute route = new HttpRoute(new HttpHost("localhost", 80)); - final ConnectionRequest connRequest = connManager.requestConnection(route, null); - - connManager.shutdown(); + basicConnManager = new BasicHttpClientConnectionManager(); + final ConnectionRequest connRequest = basicConnManager.requestConnection(route, null); + assertTrue(connRequest.get(1000, TimeUnit.SECONDS) != null); } + @Test + @Ignore + // 2.2 IN ARTICLE + public final void whenOpeningLowLevelConnectionWithSocketTimeout_thenNoExceptions() throws InterruptedException, ExecutionException, IOException, HttpException { + basicConnManager = new BasicHttpClientConnectionManager(); + context = HttpClientContext.create(); + final ConnectionRequest connRequest = basicConnManager.requestConnection(route, null); + conn = connRequest.get(1000, TimeUnit.SECONDS); + if (!conn.isOpen()) + basicConnManager.connect(conn, route, 1000, context); + conn.setSocketTimeout(30000); + + assertTrue(conn.getSocketTimeout() == 30000); + assertTrue(conn.isOpen()); + } + + @Test + @Ignore + // Example 3.1. TESTER VERSION + public final void WhenTwoConnectionsForTwoRequests_ThenLeaseTwoConnectionsNoExceptions() throws InterruptedException { + get1 = new HttpGet("http://localhost"); + get2 = new HttpGet("http://google.com"); + poolingConnManager = new PoolingHttpClientConnectionManager(); + final CloseableHttpClient client1 = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final CloseableHttpClient client2 = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client1, get1, poolingConnManager); + final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client2, get2, poolingConnManager); + thread1.start(); + thread1.join(); + thread2.start(); + assertTrue(poolingConnManager.getTotalStats().getLeased() == 1); + thread2.join(1000); + assertTrue(poolingConnManager.getTotalStats().getLeased() == 2); + } + + @Test + @Ignore + // Example 3.1.ARTICLE VERSION + public final void WhenTwoConnectionsForTwoRequests_ThensNoExceptions() throws InterruptedException { + get1 = new HttpGet("http://localhost"); + get2 = new HttpGet("http://google.com"); + poolingConnManager = new PoolingHttpClientConnectionManager(); + final CloseableHttpClient client1 = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final CloseableHttpClient client2 = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final MultiHttpClientConnThread thread1 = new MultiHttpClientConnThread(client1, get1); + final MultiHttpClientConnThread thread2 = new MultiHttpClientConnThread(client2, get2); + thread1.start(); + thread1.join(); + thread2.start(); + thread2.join(); + } + + @Test + @Ignore + // 3.3 + public final void whenIncreasingConnectionPool_thenNoEceptions() { + + poolingConnManager = new PoolingHttpClientConnectionManager(); + poolingConnManager.setMaxTotal(5); + poolingConnManager.setDefaultMaxPerRoute(4); + final HttpHost localhost = new HttpHost("locahost", 80); + poolingConnManager.setMaxPerRoute(new HttpRoute(localhost), 5); + } + + @Test + @Ignore + // 3.4 Tester Version + public final void whenExecutingSameRequestsInDifferentThreads_thenUseDefaultConnLimitNoExceptions() throws InterruptedException, IOException { + final HttpGet get = new HttpGet("http://google.com"); + poolingConnManager = new PoolingHttpClientConnectionManager(); + client = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager); + final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager); + final TesterVersion_MultiHttpClientConnThread thread3 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager); + thread1.start(); + thread1.join(1000); + assertTrue(poolingConnManager.getTotalStats().getLeased() == 1); + thread2.start(); + thread2.join(1000); + assertTrue(poolingConnManager.getTotalStats().getLeased() == 2); + thread3.start(); + thread3.join(1000); + } + + @Test + @Ignore + // 3.4 Article version + public final void whenExecutingSameRequestsInDifferentThreads_thenExxecuteReuqesttNoExceptions() throws InterruptedException { + final HttpGet get = new HttpGet("http://localhost"); + poolingConnManager = new PoolingHttpClientConnectionManager(); + client = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final MultiHttpClientConnThread thread1 = new MultiHttpClientConnThread(client, get); + final MultiHttpClientConnThread thread2 = new MultiHttpClientConnThread(client, get); + final MultiHttpClientConnThread thread3 = new MultiHttpClientConnThread(client, get); + thread1.start(); + thread2.start(); + thread3.start(); + thread1.join(); + thread2.join(); + thread3.join(); + } + + @Test + @Ignore + // 4.1 + public final void whenCustomizingKeepAliveStrategy_thenNoExceptions() throws ClientProtocolException, IOException { + final ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { + @Override + public long getKeepAliveDuration(final HttpResponse myResponse, final HttpContext myContext) { + final HeaderElementIterator it = new BasicHeaderElementIterator(myResponse.headerIterator(HTTP.CONN_KEEP_ALIVE)); + while (it.hasNext()) { + final HeaderElement he = it.nextElement(); + final String param = he.getName(); + final String value = he.getValue(); + if (value != null && param.equalsIgnoreCase("timeout")) { + return Long.parseLong(value) * 1000; + } + } + final HttpHost target = (HttpHost) myContext.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); + if ("localhost".equalsIgnoreCase(target.getHostName())) { + return 10 * 1000; + } else { + return 5 * 1000; + } + } + + }; + client = HttpClients.custom().setKeepAliveStrategy(myStrategy).setConnectionManager(poolingConnManager).build(); + client.execute(get1); + client.execute(get2); + } + + @Test + @Ignore + // 5.1 + public final void GivenBasicHttpClientConnManager_whenConnectionReuse_thenNoExceptions() throws InterruptedException, ExecutionException, IOException, HttpException { + basicConnManager = new BasicHttpClientConnectionManager(); + context = HttpClientContext.create(); + final HttpGet get = new HttpGet("http://localhost"); + HttpResponse thisResponse = null; + final ConnectionRequest connRequest = basicConnManager.requestConnection(route, null); + client = HttpClients.custom().setConnectionManager(basicConnManager).build(); + boolean respAvail = false; + conn = connRequest.get(10, TimeUnit.SECONDS); + if (!conn.isOpen()) { + basicConnManager.connect(conn, route, 1000, context); + basicConnManager.routeComplete(conn, route, context); + final HttpRequestExecutor exeRequest = new HttpRequestExecutor(); + context.setTargetHost((new HttpHost("localhost", 80))); + thisResponse = exeRequest.execute(get, conn, context); + respAvail = conn.isResponseAvailable(1000); + } + basicConnManager.releaseConnection(conn, null, 1, TimeUnit.SECONDS); + if (respAvail) { + client.execute(get); + } + } + + @Test + @Ignore + // 5.2 TESTER VERSION + public final void WhenConnectionsNeededGreaterThanMaxTotal_thenReuseConnectionsNoExceptions() throws InterruptedException { + poolingConnManager = new PoolingHttpClientConnectionManager(); + poolingConnManager.setDefaultMaxPerRoute(5); + poolingConnManager.setMaxTotal(5); + client = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final MultiHttpClientConnThread[] threads = new MultiHttpClientConnThread[10]; + int countConnMade = 0; + for (int i = 0; i < threads.length; i++) { + threads[i] = new MultiHttpClientConnThread(client, get1, poolingConnManager); + } + for (final MultiHttpClientConnThread thread : threads) { + thread.start(); + } + for (final MultiHttpClientConnThread thread : threads) { + thread.join(10000); + countConnMade++; + if (countConnMade == 0) + assertTrue(thread.getLeasedConn() == 5); + } + } + + @Test + // 5.2 ARTICLE VERSION + @Ignore + public final void WhenConnectionsNeededGreaterThanMaxTotal_thenLeaseMasTotalandReuseNoExceptions() throws InterruptedException { + final HttpGet get = new HttpGet("http://echo.200please.com"); + poolingConnManager = new PoolingHttpClientConnectionManager(); + poolingConnManager.setDefaultMaxPerRoute(5); + poolingConnManager.setMaxTotal(5); + client = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final MultiHttpClientConnThread[] threads = new MultiHttpClientConnThread[10]; + for (int i = 0; i < threads.length; i++) { + threads[i] = new MultiHttpClientConnThread(client, get, poolingConnManager); + } + for (final MultiHttpClientConnThread thread : threads) { + thread.start(); + } + for (final MultiHttpClientConnThread thread : threads) { + thread.join(10000); + } + } + + @Test + @Ignore + // 6.2.1 + public final void whenConfiguringTimeOut_thenNoExceptions() { + route = new HttpRoute(new HttpHost("localhost", 80)); + poolingConnManager = new PoolingHttpClientConnectionManager(); + poolingConnManager.setSocketConfig(route.getTargetHost(), SocketConfig.custom().setSoTimeout(5000).build()); + assertTrue(poolingConnManager.getSocketConfig(route.getTargetHost()).getSoTimeout() == 5000); + } + + @Test + @Ignore + // 7.1 + public final void whenHttpClientChecksStaleConns_thenNoExceptions() { + poolingConnManager = new PoolingHttpClientConnectionManager(); + client = HttpClients.custom().setDefaultRequestConfig(RequestConfig.custom().setStaleConnectionCheckEnabled(true).build()).setConnectionManager(poolingConnManager).build(); + } + + @Test + @Ignore + // 7.2 TESTER VERSION + public final void whenCustomizedIdleConnMonitor_thenEliminateIdleConnsNoExceptions() throws InterruptedException, IOException { + poolingConnManager = new PoolingHttpClientConnectionManager(); + client = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(poolingConnManager); + final HttpGet get = new HttpGet("http://google.com"); + // test this with new HttpGet("http://iotechperu.com")----First test will fail b/c there is redirect connection at that site + final TesterVersion_MultiHttpClientConnThread thread1 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager); + final TesterVersion_MultiHttpClientConnThread thread2 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager); + final TesterVersion_MultiHttpClientConnThread thread3 = new TesterVersion_MultiHttpClientConnThread(client, get, poolingConnManager); + staleMonitor.start(); + thread1.start(); + thread1.join(); + thread2.start(); + thread2.join(); + thread3.start(); + assertTrue(poolingConnManager.getTotalStats().getAvailable() == 1); + thread3.join(32000); + assertTrue(poolingConnManager.getTotalStats().getAvailable() == 0); + } + + @Test + @Ignore + // 7.2 ARTICLE VERSION + public final void whenCustomizedIdleConnMonitor_thenNoExceptions() throws InterruptedException, IOException { + final HttpGet get = new HttpGet("http://google.com"); + poolingConnManager = new PoolingHttpClientConnectionManager(); + client = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + final IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(poolingConnManager); + staleMonitor.start(); + staleMonitor.join(1000); + } + + @Test(expected = IllegalStateException.class) + @Ignore + // 8.1 + public final void whenClosingConnectionsandManager_thenCloseWithNoExceptions() throws InterruptedException, ExecutionException, IOException, HttpException { + route = new HttpRoute(new HttpHost("google.com", 80)); + final HttpGet get = new HttpGet("http://google.com"); + poolingConnManager = new PoolingHttpClientConnectionManager(); + final ConnectionRequest connRequest = poolingConnManager.requestConnection(route, null); + context = HttpClientContext.create(); + conn = connRequest.get(10, TimeUnit.SECONDS); + poolingConnManager.connect(conn, route, 10000, context); + client = HttpClients.custom().setConnectionManager(poolingConnManager).build(); + response = client.execute(get); + EntityUtils.consume(response.getEntity()); + client.close(); + conn.close(); + response.close(); + poolingConnManager.close(); + poolingConnManager.shutdown(); + client.execute(get); + conn.sendRequestHeader(get); + assertTrue(!conn.isOpen()); + assertTrue(conn.isOpen()); + assertTrue(response.getEntity() == null); + } } diff --git a/httpclient/src/test/java/org/baeldung/httpclient/IdleConnectionMonitorThread.java b/httpclient/src/test/java/org/baeldung/httpclient/IdleConnectionMonitorThread.java new file mode 100644 index 0000000000..4c4c7f36a1 --- /dev/null +++ b/httpclient/src/test/java/org/baeldung/httpclient/IdleConnectionMonitorThread.java @@ -0,0 +1,39 @@ +package org.baeldung.httpclient; + +import java.util.concurrent.TimeUnit; + +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; + +public class IdleConnectionMonitorThread extends Thread { + private final HttpClientConnectionManager connMgr; + private volatile boolean shutdown; + + public IdleConnectionMonitorThread(final PoolingHttpClientConnectionManager connMgr) { + super(); + this.connMgr = connMgr; + } + + @Override + public void run() { + try { + while (!shutdown) { + synchronized (this) { + wait(1000); + connMgr.closeExpiredConnections(); + connMgr.closeIdleConnections(30, TimeUnit.SECONDS); + } + } + } catch (final InterruptedException ex) { + shutdown(); + + } + } + + public void shutdown() { + shutdown = true; + synchronized (this) { + notifyAll(); + } + } +} diff --git a/httpclient/src/test/java/org/baeldung/httpclient/MultiHttpClientConnThread.java b/httpclient/src/test/java/org/baeldung/httpclient/MultiHttpClientConnThread.java new file mode 100644 index 0000000000..e2f8fabbdf --- /dev/null +++ b/httpclient/src/test/java/org/baeldung/httpclient/MultiHttpClientConnThread.java @@ -0,0 +1,61 @@ +package org.baeldung.httpclient; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.util.EntityUtils; + +public class MultiHttpClientConnThread extends Thread { + private final CloseableHttpClient client; + private final HttpGet get; + private PoolingHttpClientConnectionManager connManager = null; + private static HttpResponse response; + private Logger logger; + public int leasedConn; + + public MultiHttpClientConnThread(final CloseableHttpClient client, final HttpGet get, final PoolingHttpClientConnectionManager connManager) { + this.client = client; + this.get = get; + this.connManager = connManager; + logger = Logger.getLogger(MultiHttpClientConnThread.class.getName()); + leasedConn = 0; + } + + public MultiHttpClientConnThread(final CloseableHttpClient client, final HttpGet get) { + this.client = client; + this.get = get; + logger = Logger.getLogger(MultiHttpClientConnThread.class.getName()); + } + + public int getLeasedConn() { + return leasedConn; + } + + @Override + public void run() { + + try { + if (this != null) + logger.log(Level.SEVERE, "Thread Running: " + getName()); + response = client.execute(get); + if (connManager != null) { + logger.log(Level.SEVERE, "Leased Connections " + connManager.getTotalStats().getLeased()); + leasedConn = connManager.getTotalStats().getLeased(); + logger.log(Level.SEVERE, "Available Connections " + connManager.getTotalStats().getAvailable()); + } + EntityUtils.consume(response.getEntity()); + + } catch (final ClientProtocolException ex) { + + } catch (final IOException ex) { + + } + + } +} diff --git a/httpclient/src/test/java/org/baeldung/httpclient/TesterVersion_MultiHttpClientConnThread.java b/httpclient/src/test/java/org/baeldung/httpclient/TesterVersion_MultiHttpClientConnThread.java new file mode 100644 index 0000000000..f71ac7462e --- /dev/null +++ b/httpclient/src/test/java/org/baeldung/httpclient/TesterVersion_MultiHttpClientConnThread.java @@ -0,0 +1,49 @@ +package org.baeldung.httpclient; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; + +public class TesterVersion_MultiHttpClientConnThread extends Thread { + private final CloseableHttpClient client; + private final HttpGet get; + private PoolingHttpClientConnectionManager connManager = null; + private Logger logger; + public int leasedConn; + public TesterVersion_MultiHttpClientConnThread(final CloseableHttpClient client, final HttpGet get, final PoolingHttpClientConnectionManager connManager) { + this.client = client; + this.get = get; + this.connManager = connManager; + logger = Logger.getLogger(TesterVersion_MultiHttpClientConnThread.class.getName()); + leasedConn = 0; + } + + public int getLeasedConn() { + return leasedConn; + } + + @Override + public void run() { + try { + if (this != null) + logger.log(Level.SEVERE, "Thread Running: " + getName()); + client.execute(get); + if (connManager != null) { + logger.log(Level.SEVERE, "Leased Connections " + connManager.getTotalStats().getLeased()); + leasedConn = connManager.getTotalStats().getLeased(); + logger.log(Level.SEVERE, "Available Connections " + connManager.getTotalStats().getAvailable()); + } + + } catch (final ClientProtocolException ex) { + + } catch (final IOException ex) { + + } + } + +}