/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 * * http://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. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.ogt.http.impl.conn; import java.util.concurrent.TimeUnit; import org.apache.ogt.http.HttpHost; import org.apache.ogt.http.HttpVersion; import org.apache.ogt.http.conn.ClientConnectionManager; import org.apache.ogt.http.conn.ClientConnectionRequest; import org.apache.ogt.http.conn.ConnectionPoolTimeoutException; import org.apache.ogt.http.conn.ManagedClientConnection; import org.apache.ogt.http.conn.routing.HttpRoute; import org.apache.ogt.http.conn.scheme.PlainSocketFactory; import org.apache.ogt.http.conn.scheme.Scheme; import org.apache.ogt.http.conn.scheme.SchemeRegistry; import org.apache.ogt.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.ogt.http.params.BasicHttpParams; import org.apache.ogt.http.params.HttpParams; import org.apache.ogt.http.params.HttpProtocolParams; import org.junit.Assert; import org.junit.Test; /** * Tests for <code>ThreadSafeClientConnManager</code> that do not require * a server to communicate with. */ public class TestTSCCMNoServer { private static ManagedClientConnection getConnection( final ClientConnectionManager mgr, final HttpRoute route, long timeout, TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException { ClientConnectionRequest connRequest = mgr.requestConnection(route, null); return connRequest.getConnection(timeout, unit); } private static ManagedClientConnection getConnection( final ClientConnectionManager mgr, final HttpRoute route) throws ConnectionPoolTimeoutException, InterruptedException { ClientConnectionRequest connRequest = mgr.requestConnection(route, null); return connRequest.getConnection(0, null); } /** * Helper to instantiate a <code>ThreadSafeClientConnManager</code>. * * @param schreg the scheme registry, or * <code>null</code> to use defaults * * @return a connection manager to test */ public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg) { if (schreg == null) schreg = createSchemeRegistry(); return new ThreadSafeClientConnManager(schreg); } /** * Instantiates default parameters. * * @return the default parameters */ public HttpParams createDefaultParams() { HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setUseExpectContinue(params, false); return params; } /** * Instantiates a default scheme registry. * * @return the default scheme registry */ public SchemeRegistry createSchemeRegistry() { SchemeRegistry schreg = new SchemeRegistry(); schreg.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); return schreg; } @Test public void testConstructor() { SchemeRegistry schreg = createSchemeRegistry(); ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(schreg); Assert.assertNotNull(mgr); mgr.shutdown(); } @Test(expected=IllegalArgumentException.class) public void testIllegalConstructor() { new ThreadSafeClientConnManager(null); } @Test(expected=IllegalArgumentException.class) public void testGetConnection() throws InterruptedException, ConnectionPoolTimeoutException { ThreadSafeClientConnManager mgr = createTSCCM(null); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); ManagedClientConnection conn = getConnection(mgr, route); Assert.assertNotNull(conn); Assert.assertNull(conn.getRoute()); Assert.assertFalse(conn.isOpen()); mgr.releaseConnection(conn, -1, null); try { getConnection(mgr, null); } finally { mgr.shutdown(); } } // testTimeout in 3.x TestHttpConnectionManager is redundant // several other tests here rely on timeout behavior @Test public void testMaxConnTotal() throws InterruptedException, ConnectionPoolTimeoutException { ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(2); mgr.setDefaultMaxPerRoute(1); HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); HttpRoute route1 = new HttpRoute(target1, null, false); HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); HttpRoute route2 = new HttpRoute(target2, null, false); ManagedClientConnection conn1 = getConnection(mgr, route1); Assert.assertNotNull(conn1); ManagedClientConnection conn2 = getConnection(mgr, route2); Assert.assertNotNull(conn2); try { // this should fail quickly, connection has not been released getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } // release one of the connections mgr.releaseConnection(conn2, -1, null); conn2 = null; // there should be a connection available now try { getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS); } catch (ConnectionPoolTimeoutException cptx) { Assert.fail("connection should have been available: " + cptx); } mgr.shutdown(); } @Test public void testMaxConnPerHost() throws Exception { HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); HttpRoute route1 = new HttpRoute(target1, null, false); HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); HttpRoute route2 = new HttpRoute(target2, null, false); HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http"); HttpRoute route3 = new HttpRoute(target3, null, false); ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(100); mgr.setDefaultMaxPerRoute(1); mgr.setMaxForRoute(route2, 2); mgr.setMaxForRoute(route3, 3); // route 3, limit 3 ManagedClientConnection conn1 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn1); ManagedClientConnection conn2 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); ManagedClientConnection conn3 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn3); try { // should fail quickly, connection has not been released getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } // route 2, limit 2 conn1 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); try { // should fail quickly, connection has not been released getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } // route 1, should use default limit of 1 conn1 = getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); try { // should fail quickly, connection has not been released getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } // check releaseConnection with invalid arguments try { mgr.releaseConnection(null, -1, null); Assert.fail("null connection adapter not detected"); } catch (IllegalArgumentException iax) { // expected } try { mgr.releaseConnection(new ClientConnAdapterMockup(null), -1, null); Assert.fail("foreign connection adapter not detected"); } catch (IllegalArgumentException iax) { // expected } mgr.shutdown(); } @Test public void testReleaseConnection() throws Exception { ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(3); mgr.setDefaultMaxPerRoute(1); HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); HttpRoute route1 = new HttpRoute(target1, null, false); HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); HttpRoute route2 = new HttpRoute(target2, null, false); HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http"); HttpRoute route3 = new HttpRoute(target3, null, false); // the first three allocations should pass ManagedClientConnection conn1 = getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); ManagedClientConnection conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); ManagedClientConnection conn3 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn1); Assert.assertNotNull(conn2); Assert.assertNotNull(conn3); // obtaining another connection for either of the three should fail // this is somehow redundant with testMaxConnPerHost try { getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } try { getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } try { getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } // now release one and check that exactly that one can be obtained then mgr.releaseConnection(conn2, -1, null); conn2 = null; try { getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } // this one succeeds conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); try { getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException e) { // expected } mgr.shutdown(); } @Test public void testDeleteClosedConnections() throws InterruptedException, ConnectionPoolTimeoutException { ThreadSafeClientConnManager mgr = createTSCCM(null); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); ManagedClientConnection conn = getConnection(mgr, route); Assert.assertEquals("connectionsInPool", mgr.getConnectionsInPool(), 1); Assert.assertEquals("connectionsInPool(host)", mgr.getConnectionsInPool(route), 1); mgr.releaseConnection(conn, -1, null); Assert.assertEquals("connectionsInPool", mgr.getConnectionsInPool(), 1); Assert.assertEquals("connectionsInPool(host)", mgr.getConnectionsInPool(route), 1); // this implicitly deletes them mgr.closeIdleConnections(0L, TimeUnit.MILLISECONDS); Assert.assertEquals("connectionsInPool", mgr.getConnectionsInPool(), 0); Assert.assertEquals("connectionsInPool(host)", mgr.getConnectionsInPool(route), 0); mgr.shutdown(); } @Test public void testShutdown() throws Exception { // 3.x: TestHttpConnectionManager.testShutdown ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(1); mgr.setDefaultMaxPerRoute(1); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); // get the only connection, then start an extra thread // on shutdown, the extra thread should get an exception ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout gct.start(); Thread.sleep(100); // give extra thread time to block mgr.shutdown(); // First release the connection. If the manager keeps working // despite the shutdown, this will deblock the extra thread. // The release itself should turn into a no-op, without exception. mgr.releaseConnection(conn, -1, null); gct.join(10000); Assert.assertNull("thread should not have obtained connection", gct.getConnection()); Assert.assertNotNull("thread should have gotten an exception", gct.getException()); Assert.assertSame("thread got wrong exception", IllegalStateException.class, gct.getException().getClass()); // the manager is down, we should not be able to get a connection try { getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); Assert.fail("shut-down manager does not raise exception"); } catch (IllegalStateException isx) { // expected } } @Test public void testInterruptThread() throws Exception { // 3.x: TestHttpConnectionManager.testWaitingThreadInterrupted ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(1); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); // get the only connection, then start an extra thread ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout gct.start(); Thread.sleep(100); // give extra thread time to block // interrupt the thread, it should cancel waiting with an exception gct.interrupt(); gct.join(10000); Assert.assertNotNull("thread should have gotten an exception", gct.getException()); Assert.assertSame("thread got wrong exception", InterruptedException.class, gct.getException().getClass()); // make sure the manager is still working try { getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.fail("should have gotten a timeout"); } catch (ConnectionPoolTimeoutException e) { // expected } mgr.releaseConnection(conn, -1, null); // this time: no exception conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull("should have gotten a connection", conn); mgr.shutdown(); } @Test public void testReusePreference() throws Exception { // 3.x: TestHttpConnectionManager.testHostReusePreference ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(1); HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); HttpRoute route1 = new HttpRoute(target1, null, false); HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); HttpRoute route2 = new HttpRoute(target2, null, false); // get the only connection, then start two extra threads ManagedClientConnection conn = getConnection(mgr, route1, 1L, TimeUnit.MILLISECONDS); GetConnThread gct1 = new GetConnThread(mgr, route1, 1000L); GetConnThread gct2 = new GetConnThread(mgr, route2, 1000L); // the second thread is started first, to distinguish the // route-based reuse preference from first-come, first-served gct2.start(); Thread.sleep(100); // give the thread time to block gct1.start(); Thread.sleep(100); // give the thread time to block // releasing the connection for route1 should deblock thread1 // the other thread gets a timeout mgr.releaseConnection(conn, -1, null); gct1.join(10000); gct2.join(10000); Assert.assertNotNull("thread 1 should have gotten a connection", gct1.getConnection()); Assert.assertNull ("thread 2 should NOT have gotten a connection", gct2.getConnection()); mgr.shutdown(); } @Test public void testAbortAfterRequestStarts() throws Exception { ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(1); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); // get the only connection, then start an extra thread ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); ClientConnectionRequest request = mgr.requestConnection(route, null); GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout gct.start(); Thread.sleep(100); // give extra thread time to block request.abortRequest(); gct.join(10000); Assert.assertNotNull("thread should have gotten an exception", gct.getException()); Assert.assertSame("thread got wrong exception", InterruptedException.class, gct.getException().getClass()); // make sure the manager is still working try { getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.fail("should have gotten a timeout"); } catch (ConnectionPoolTimeoutException e) { // expected } mgr.releaseConnection(conn, -1, null); // this time: no exception conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull("should have gotten a connection", conn); mgr.shutdown(); } @Test public void testAbortBeforeRequestStarts() throws Exception { ThreadSafeClientConnManager mgr = createTSCCM(null); mgr.setMaxTotal(1); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); // get the only connection, then start an extra thread ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); ClientConnectionRequest request = mgr.requestConnection(route, null); request.abortRequest(); GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout gct.start(); Thread.sleep(100); // give extra thread time to block gct.join(10000); Assert.assertNotNull("thread should have gotten an exception", gct.getException()); Assert.assertSame("thread got wrong exception", InterruptedException.class, gct.getException().getClass()); // make sure the manager is still working try { getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.fail("should have gotten a timeout"); } catch (ConnectionPoolTimeoutException e) { // expected } mgr.releaseConnection(conn, -1, null); // this time: no exception conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull("should have gotten a connection", conn); mgr.shutdown(); } }