HTTPCLIENT-1000: Maximum connection lifetimes settings for ThreadSafeClientConnManager.

Contributed by Michajlo Matijkiw <michajlo_matijkiw at comcast.com>



git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1001144 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-09-25 06:35:07 +00:00
parent 86c464d15e
commit 1e70d5a39e
6 changed files with 121 additions and 13 deletions

View File

@ -1,6 +1,9 @@
Changes since 4.1 ALPHA2
-------------------
* [HTTPCLIENT-1000] Maximum connection lifetimes settings for ThreadSafeClientConnManager.
Contributed by Michajlo Matijkiw <michajlo_matijkiw at comcast.com>
* [HTTPCLIENT-960] HttpMultipart doesn't generate Content-Type header for binary parts in
BROWSER_COMPATIBLE mode.
Contributed by Oleg Kalnichevski <olegk at apache.org>

View File

@ -46,6 +46,7 @@ public class BasicPoolEntry extends AbstractPoolEntry {
private final long created;
private long updated;
private long validUntil;
private long expiry;
/**
@ -60,6 +61,8 @@ public class BasicPoolEntry extends AbstractPoolEntry {
throw new IllegalArgumentException("HTTP route may not be null");
}
this.created = System.currentTimeMillis();
this.validUntil = Long.MAX_VALUE;
this.expiry = this.validUntil;
}
/**
@ -70,11 +73,32 @@ public class BasicPoolEntry extends AbstractPoolEntry {
*/
public BasicPoolEntry(ClientConnectionOperator op,
HttpRoute route) {
this(op, route, -1, TimeUnit.MILLISECONDS);
}
/**
* Creates a new pool entry with a specified maximum lifetime.
*
* @param op the connection operator
* @param route the planned route for the connection
* @param connTTL maximum lifetime of this entry, <=0 implies "infinity"
* @param timeunit TimeUnit of connTTL
*
* @since 4.1
*/
public BasicPoolEntry(ClientConnectionOperator op,
HttpRoute route, long connTTL, TimeUnit timeunit) {
super(op, route);
if (route == null) {
throw new IllegalArgumentException("HTTP route may not be null");
}
this.created = System.currentTimeMillis();
if (connTTL > 0) {
this.validUntil = this.created + timeunit.toMillis(connTTL);
} else {
this.validUntil = Long.MAX_VALUE;
}
this.expiry = this.validUntil;
}
protected final OperatedClientConnection getConnection() {
@ -116,16 +140,22 @@ public class BasicPoolEntry extends AbstractPoolEntry {
return this.expiry;
}
public long getValidUntil() {
return this.validUntil;
}
/**
* @since 4.1
*/
public void updateExpiry(long time, TimeUnit timeunit) {
this.updated = System.currentTimeMillis();
long newExpiry;
if (time > 0) {
this.expiry = this.updated + timeunit.toMillis(time);
newExpiry = this.updated + timeunit.toMillis(time);
} else {
this.expiry = Long.MAX_VALUE;
newExpiry = Long.MAX_VALUE;
}
this.expiry = Math.min(validUntil, newExpiry);
}
/**

View File

@ -90,6 +90,10 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
/** Map of route-specific pools */
protected final Map<HttpRoute, RouteSpecificPool> routeToPool;
private final long connTTL;
private final TimeUnit connTTLTimeUnit;
protected volatile boolean shutdown;
protected volatile int maxTotalConnections;
@ -105,6 +109,18 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
final ClientConnectionOperator operator,
final ConnPerRoute connPerRoute,
int maxTotalConnections) {
this(operator, connPerRoute, maxTotalConnections, -1, TimeUnit.MILLISECONDS);
}
/**
* @since 4.1
*/
public ConnPoolByRoute(
final ClientConnectionOperator operator,
final ConnPerRoute connPerRoute,
int maxTotalConnections,
long connTTL,
final TimeUnit connTTLTimeUnit) {
super();
if (operator == null) {
throw new IllegalArgumentException("Connection operator may not be null");
@ -120,6 +136,8 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
this.freeConnections = createFreeConnQueue();
this.waitingThreads = createWaitingThreadQueue();
this.routeToPool = createRouteToPoolMap();
this.connTTL = connTTL;
this.connTTLTimeUnit = connTTLTimeUnit;
}
protected Lock getLock() {
@ -532,7 +550,7 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
}
// the entry will create the connection when needed
BasicPoolEntry entry = new BasicPoolEntry(op, rospl.getRoute());
BasicPoolEntry entry = new BasicPoolEntry(op, rospl.getRoute(), connTTL, connTTLTimeUnit);
poolLock.lock();
try {

View File

@ -87,6 +87,20 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
* @param schreg the scheme registry.
*/
public ThreadSafeClientConnManager(final SchemeRegistry schreg) {
this(schreg, -1, TimeUnit.MILLISECONDS);
}
/**
* Creates a new thread safe connection manager.
*
* @param schreg the scheme registry.
* @param connTTL max connection lifetime, <=0 implies "infinity"
* @param connTTLTimeUnit TimeUnit of connTTL
*
* @since 4.1
*/
public ThreadSafeClientConnManager(final SchemeRegistry schreg,
long connTTL, TimeUnit connTTLTimeUnit) {
super();
if (schreg == null) {
throw new IllegalArgumentException("Scheme registry may not be null");
@ -95,7 +109,7 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
this.schemeRegistry = schreg;
this.connPerRoute = new ConnPerRouteBean();
this.connOperator = createConnectionOperator(schreg);
this.pool = createConnectionPool() ;
this.pool = createConnectionPool(connTTL, connTTLTimeUnit) ;
this.connectionPool = this.pool;
}
@ -149,8 +163,8 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
*
* @since 4.1
*/
protected ConnPoolByRoute createConnectionPool() {
return new ConnPoolByRoute(connOperator, connPerRoute, 20);
protected ConnPoolByRoute createConnectionPool(long connTTL, TimeUnit connTTLTimeUnit) {
return new ConnPoolByRoute(connOperator, connPerRoute, 20, connTTL, connTTLTimeUnit);
}
/**

View File

@ -81,9 +81,14 @@ public class TestTSCCMWithServer extends ServerTestBase {
* @return a connection manager to test
*/
public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg) {
return createTSCCM(schreg, -1, TimeUnit.MILLISECONDS);
}
public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg,
long connTTL, TimeUnit connTTLTimeUnit) {
if (schreg == null)
schreg = supportedSchemes;
return new ThreadSafeClientConnManager(schreg);
return new ThreadSafeClientConnManager(schreg, connTTL, connTTLTimeUnit);
}
/**
@ -354,7 +359,7 @@ public class TestTSCCMWithServer extends ServerTestBase {
}
@Test
public void testCloseExpiredConnections() throws Exception {
public void testCloseExpiredIdleConnections() throws Exception {
ThreadSafeClientConnManager mgr = createTSCCM(null);
mgr.setMaxTotal(1);
@ -390,6 +395,44 @@ public class TestTSCCMWithServer extends ServerTestBase {
mgr.shutdown();
}
@Test
public void testCloseExpiredTTLConnections() throws Exception {
ThreadSafeClientConnManager mgr = createTSCCM(null, 100, TimeUnit.MILLISECONDS);
mgr.setMaxTotal(1);
final HttpHost target = getServerHttp();
final HttpRoute route = new HttpRoute(target, null, false);
ManagedClientConnection conn = getConnection(mgr, route);
conn.open(route, httpContext, defaultParams);
Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
// Release, let remain idle for forever
mgr.releaseConnection(conn, -1, TimeUnit.MILLISECONDS);
// Released, still active.
Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
mgr.closeExpiredConnections();
// Time has not expired yet.
Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
Thread.sleep(150);
mgr.closeExpiredConnections();
// TTL expired now, connections are destroyed.
Assert.assertEquals("connectionsInPool", 0, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 0, mgr.getConnectionsInPool(route));
mgr.shutdown();
}
/**
* Tests releasing connection from #abort method called from the
* main execution thread while there is no blocking I/O operation.

View File

@ -27,20 +27,20 @@
package org.apache.http.impl.conn.tsccm;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import org.apache.http.HttpHost;
import org.apache.http.conn.ClientConnectionOperator;
import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.impl.conn.GetConnThread;
import org.junit.Assert;
import org.junit.Test;
@ -101,7 +101,7 @@ public class TestSpuriousWakeup {
}
@Override
protected ConnPoolByRoute createConnectionPool() {
protected ConnPoolByRoute createConnectionPool(long connTTL, TimeUnit connTTLUnit) {
extendedCPBR = new XConnPoolByRoute(connOperator, connPerRoute, 20);
// no connection GC required
return extendedCPBR;