You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2011/08/22 17:41:35 UTC
svn commit: r1160308 - in /httpcomponents/httpcore/trunk: ./
httpcore-nio/src/main/java/org/apache/http/nio/pool/
httpcore-nio/src/test/java/org/apache/http/nio/pool/
httpcore/src/main/java/org/apache/http/pool/
httpcore/src/test/java/org/apache/http/p...
Author: olegk
Date: Mon Aug 22 15:41:34 2011
New Revision: 1160308
URL: http://svn.apache.org/viewvc?rev=1160308&view=rev
Log:
HTTPCORE-269: Connection pools incorrectly handle lease requests when the max limit for the given route has been exceeded and all connections in the route pool are stateful
Modified:
httpcomponents/httpcore/trunk/RELEASE_NOTES.txt
httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/AbstractNIOConnPool.java
httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/LeaseRequest.java
httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/RouteSpecificPool.java
httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestNIOConnPool.java
httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestRouteSpecificPool.java
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java
httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java
httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java
Modified: httpcomponents/httpcore/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/RELEASE_NOTES.txt?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpcore/trunk/RELEASE_NOTES.txt Mon Aug 22 15:41:34 2011
@@ -1,3 +1,10 @@
+Changes since 4.2-ALPHA1
+
+* [HTTPCORE-269] Connection pools incorrectly handle lease requests when the max limit for the given
+ route has been exceeded and all connections in the route pool are stateful.
+ Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
Release 4.2-ALPHA1
-------------------
Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/AbstractNIOConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/AbstractNIOConnPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/AbstractNIOConnPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/AbstractNIOConnPool.java Mon Aug 22 15:41:34 2011
@@ -37,6 +37,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -184,10 +185,7 @@ public abstract class AbstractNIOConnPoo
}
this.lock.lock();
try {
- int timeout = (int) tunit.toMillis(connectTimeout);
- if (timeout < 0) {
- timeout = 0;
- }
+ long timeout = connectTimeout > 0 ? tunit.toMillis(connectTimeout) : 0;
BasicFuture<E> future = new BasicFuture<E>(callback);
LeaseRequest<T, C, E> request = new LeaseRequest<T, C, E>(route, state, timeout, future);
this.leasingRequests.add(request);
@@ -238,10 +236,17 @@ public abstract class AbstractNIOConnPoo
T route = request.getRoute();
Object state = request.getState();
- int timeout = request.getConnectTimeout();
+ long deadline = request.getDeadline();
BasicFuture<E> future = request.getFuture();
- RouteSpecificPool<T, C, E> pool = getPool(request.getRoute());
+ long now = System.currentTimeMillis();
+ if (now > deadline) {
+ it.remove();
+ future.failed(new TimeoutException());
+ continue;
+ }
+
+ RouteSpecificPool<T, C, E> pool = getPool(route);
E entry = null;
for (;;) {
entry = pool.getFree(state);
@@ -263,7 +268,24 @@ public abstract class AbstractNIOConnPoo
future.completed(entry);
continue;
}
- if (pool.getAllocatedCount() < getMaxPerRoute(route)) {
+
+ // New connection is needed
+ int maxPerRoute = getMaxPerRoute(route);
+ // Shrink the pool prior to allocating a new connection
+ int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);
+ if (excess > 0) {
+ for (int i = 0; i < excess; i++) {
+ E lastUsed = pool.getLastUsed();
+ if (lastUsed == null) {
+ break;
+ }
+ closeEntry(lastUsed);
+ this.available.remove(lastUsed);
+ pool.remove(lastUsed);
+ }
+ }
+
+ if (pool.getAllocatedCount() < maxPerRoute) {
int totalUsed = this.pending.size() + this.leased.size();
int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
if (freeCapacity == 0) {
@@ -271,7 +293,12 @@ public abstract class AbstractNIOConnPoo
}
int totalAvailable = this.available.size();
if (totalAvailable > freeCapacity - 1) {
- dropLastUsed();
+ if (!this.available.isEmpty()) {
+ E lastUsed = this.available.removeFirst();
+ closeEntry(lastUsed);
+ RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());
+ otherpool.remove(lastUsed);
+ }
}
it.remove();
SessionRequest sessionRequest = this.ioreactor.connect(
@@ -279,19 +306,31 @@ public abstract class AbstractNIOConnPoo
resolveLocalAddress(route),
route,
this.sessionRequestCallback);
- sessionRequest.setConnectTimeout(timeout);
+ int timout = request.getConnectTimeout() < Integer.MAX_VALUE ?
+ (int) request.getConnectTimeout() : Integer.MAX_VALUE;
+ sessionRequest.setConnectTimeout(timout);
this.pending.add(sessionRequest);
pool.addPending(sessionRequest, future);
}
}
}
- private void dropLastUsed() {
- if (!this.available.isEmpty()) {
- E entry = this.available.removeFirst();
- closeEntry(entry);
- RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
- pool.remove(entry);
+ public void validatePendingRequests() {
+ this.lock.lock();
+ try {
+ long now = System.currentTimeMillis();
+ ListIterator<LeaseRequest<T, C, E>> it = this.leasingRequests.listIterator();
+ while (it.hasNext()) {
+ LeaseRequest<T, C, E> request = it.next();
+ long deadline = request.getDeadline();
+ if (now > deadline) {
+ it.remove();
+ BasicFuture<E> future = request.getFuture();
+ future.failed(new TimeoutException());
+ }
+ }
+ } finally {
+ this.lock.unlock();
}
}
Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/LeaseRequest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/LeaseRequest.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/LeaseRequest.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/LeaseRequest.java Mon Aug 22 15:41:34 2011
@@ -35,18 +35,21 @@ class LeaseRequest<T, C, E extends PoolE
private final T route;
private final Object state;
- private final int connectTimeout;
+ private final long connectTimeout;
+ private final long deadline;
private final BasicFuture<E> future;
public LeaseRequest(
final T route,
final Object state,
- final int connectTimeout,
+ final long connectTimeout,
final BasicFuture<E> future) {
super();
this.route = route;
this.state = state;
this.connectTimeout = connectTimeout;
+ this.deadline = connectTimeout > 0 ? System.currentTimeMillis() + connectTimeout :
+ Long.MAX_VALUE;
this.future = future;
}
@@ -58,12 +61,16 @@ class LeaseRequest<T, C, E extends PoolE
return this.state;
}
- public BasicFuture<E> getFuture() {
- return this.future;
+ public long getConnectTimeout() {
+ return this.connectTimeout;
}
- public int getConnectTimeout() {
- return this.connectTimeout;
+ public long getDeadline() {
+ return this.deadline;
+ }
+
+ public BasicFuture<E> getFuture() {
+ return this.future;
}
@Override
Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/RouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/RouteSpecificPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/RouteSpecificPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/nio/pool/RouteSpecificPool.java Mon Aug 22 15:41:34 2011
@@ -101,6 +101,14 @@ abstract class RouteSpecificPool<T, C, E
return null;
}
+ public E getLastUsed() {
+ if (!this.available.isEmpty()) {
+ return this.available.getFirst();
+ } else {
+ return null;
+ }
+ }
+
public boolean remove(final E entry) {
if (entry == null) {
throw new IllegalArgumentException("Pool entry may not be null");
Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestNIOConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestNIOConnPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestNIOConnPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestNIOConnPool.java Mon Aug 22 15:41:34 2011
@@ -599,6 +599,103 @@ public class TestNIOConnPool {
}
@Test
+ public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception {
+ IOSession iosession1 = Mockito.mock(IOSession.class);
+ SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
+ Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
+ Mockito.when(sessionRequest1.getSession()).thenReturn(iosession1);
+
+ IOSession iosession2 = Mockito.mock(IOSession.class);
+ SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
+ Mockito.when(sessionRequest2.getAttachment()).thenReturn("somehost");
+ Mockito.when(sessionRequest2.getSession()).thenReturn(iosession2);
+
+ IOSession iosession3 = Mockito.mock(IOSession.class);
+ SessionRequest sessionRequest3 = Mockito.mock(SessionRequest.class);
+ Mockito.when(sessionRequest3.getAttachment()).thenReturn("somehost");
+ Mockito.when(sessionRequest3.getSession()).thenReturn(iosession3);
+
+ ConnectingIOReactor ioreactor = Mockito.mock(ConnectingIOReactor.class);
+ Mockito.when(ioreactor.connect(
+ Mockito.eq(InetSocketAddress.createUnresolved("somehost", 80)),
+ Mockito.any(SocketAddress.class),
+ Mockito.any(), Mockito.any(SessionRequestCallback.class))).
+ thenReturn(sessionRequest1, sessionRequest2, sessionRequest3);
+
+ LocalSessionPool pool = new LocalSessionPool(ioreactor, 2, 10);
+ pool.setMaxPerRoute("somehost", 2);
+ pool.setMaxTotal(2);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+
+ Mockito.verify(ioreactor, Mockito.times(2)).connect(
+ Mockito.eq(InetSocketAddress.createUnresolved("somehost", 80)),
+ Mockito.any(SocketAddress.class),
+ Mockito.any(), Mockito.any(SessionRequestCallback.class));
+
+ pool.requestCompleted(sessionRequest1);
+ pool.requestCompleted(sessionRequest2);
+
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry1 = future1.get();
+ Assert.assertNotNull(entry1);
+ Assert.assertTrue(future2.isDone());
+ LocalPoolEntry entry2 = future2.get();
+ Assert.assertNotNull(entry2);
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(2, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ entry1.setState("some-stuff");
+ pool.release(entry1, true);
+ entry2.setState("some-stuff");
+ pool.release(entry2, true);
+
+ Future<LocalPoolEntry> future3 = pool.lease("somehost", "some-stuff");
+ Future<LocalPoolEntry> future4 = pool.lease("somehost", "some-stuff");
+
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry3 = future3.get();
+ Assert.assertNotNull(entry3);
+ Assert.assertTrue(future4.isDone());
+ LocalPoolEntry entry4 = future4.get();
+ Assert.assertNotNull(entry4);
+
+ Mockito.verify(ioreactor, Mockito.times(2)).connect(
+ Mockito.eq(InetSocketAddress.createUnresolved("somehost", 80)),
+ Mockito.any(SocketAddress.class),
+ Mockito.any(), Mockito.any(SessionRequestCallback.class));
+
+ pool.release(entry3, true);
+ pool.release(entry4, true);
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(2, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ Future<LocalPoolEntry> future5 = pool.lease("somehost", "some-other-stuff");
+
+ Assert.assertFalse(future5.isDone());
+
+ Mockito.verify(ioreactor, Mockito.times(3)).connect(
+ Mockito.eq(InetSocketAddress.createUnresolved("somehost", 80)),
+ Mockito.any(SocketAddress.class),
+ Mockito.any(), Mockito.any(SessionRequestCallback.class));
+
+ Mockito.verify(iosession1).close();
+ Mockito.verify(iosession2, Mockito.never()).close();
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(1, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(1, totals.getPending());
+ }
+
+ @Test
public void testCreateNewIfExpired() throws Exception {
IOSession iosession1 = Mockito.mock(IOSession.class);
Mockito.when(iosession1.isClosed()).thenReturn(Boolean.TRUE);
@@ -777,6 +874,42 @@ public class TestNIOConnPool {
Assert.assertEquals(0, stats.getPending());
}
+ @Test
+ public void testLeaseRequestTimeout() throws Exception {
+ IOSession iosession1 = Mockito.mock(IOSession.class);
+ Mockito.when(iosession1.isClosed()).thenReturn(Boolean.TRUE);
+ SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
+ Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
+ Mockito.when(sessionRequest1.getSession()).thenReturn(iosession1);
+
+ ConnectingIOReactor ioreactor = Mockito.mock(ConnectingIOReactor.class);
+ Mockito.when(ioreactor.connect(
+ Mockito.any(SocketAddress.class), Mockito.any(SocketAddress.class),
+ Mockito.any(), Mockito.any(SessionRequestCallback.class))).
+ thenReturn(sessionRequest1);
+
+ LocalSessionPool pool = new LocalSessionPool(ioreactor, 1, 1);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
+ Future<LocalPoolEntry> future3 = pool.lease("somehost", null, 10, TimeUnit.MILLISECONDS, null);
+
+ pool.requestCompleted(sessionRequest1);
+
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry1 = future1.get();
+ Assert.assertNotNull(entry1);
+ Assert.assertFalse(future2.isDone());
+ Assert.assertFalse(future3.isDone());
+
+ Thread.sleep(100);
+
+ pool.validatePendingRequests();
+
+ Assert.assertFalse(future2.isDone());
+ Assert.assertTrue(future3.isDone());
+ }
+
@Test(expected=IllegalArgumentException.class)
public void testCloseIdleInvalid() throws Exception {
ConnectingIOReactor ioreactor = Mockito.mock(ConnectingIOReactor.class);
Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestRouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestRouteSpecificPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestRouteSpecificPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/nio/pool/TestRouteSpecificPool.java Mon Aug 22 15:41:34 2011
@@ -75,6 +75,7 @@ public class TestRouteSpecificPool {
Assert.assertEquals(0, pool.getAvailableCount());
Assert.assertEquals(0, pool.getLeasedCount());
Assert.assertEquals(0, pool.getPendingCount());
+ Assert.assertNull(pool.getLastUsed());
Assert.assertEquals("[route: whatever][leased: 0][available: 0][pending: 0]", pool.toString());
}
@@ -213,6 +214,8 @@ public class TestRouteSpecificPool {
pool.free(entry2, false);
pool.free(entry3, true);
+ Assert.assertSame(entry1, pool.getLastUsed());
+
Assert.assertEquals(2, pool.getAllocatedCount());
Assert.assertEquals(2, pool.getAvailableCount());
Assert.assertEquals(0, pool.getLeasedCount());
Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java Mon Aug 22 15:41:34 2011
@@ -199,13 +199,34 @@ public abstract class AbstractConnPool<T
return entry;
}
- if (pool.getAllocatedCount() < getMaxPerRoute(route)) {
+ // New connection is needed
+ int maxPerRoute = getMaxPerRoute(route);
+ // Shrink the pool prior to allocating a new connection
+ int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);
+ if (excess > 0) {
+ for (int i = 0; i < excess; i++) {
+ E lastUsed = pool.getLastUsed();
+ if (lastUsed == null) {
+ break;
+ }
+ closeEntry(lastUsed);
+ this.available.remove(lastUsed);
+ pool.remove(lastUsed);
+ }
+ }
+
+ if (pool.getAllocatedCount() < maxPerRoute) {
int totalUsed = this.leased.size();
int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
if (freeCapacity > 0) {
int totalAvailable = this.available.size();
if (totalAvailable > freeCapacity - 1) {
- dropLastUsed();
+ if (!this.available.isEmpty()) {
+ E lastUsed = this.available.removeFirst();
+ closeEntry(lastUsed);
+ RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());
+ otherpool.remove(lastUsed);
+ }
}
C conn = createConnection(route);
entry = pool.add(conn);
@@ -269,15 +290,6 @@ public abstract class AbstractConnPool<T
}
}
- private void dropLastUsed() {
- if (!this.available.isEmpty()) {
- E entry = this.available.removeFirst();
- closeEntry(entry);
- RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
- pool.remove(entry);
- }
- }
-
private int getMaxPerRoute(final T route) {
Integer v = this.maxPerRoute.get(route);
if (v != null) {
Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java Mon Aug 22 15:41:34 2011
@@ -99,6 +99,14 @@ abstract class RouteSpecificPool<T, C, E
return null;
}
+ public E getLastUsed() {
+ if (!this.available.isEmpty()) {
+ return this.available.getFirst();
+ } else {
+ return null;
+ }
+ }
+
public boolean remove(final E entry) {
if (entry == null) {
throw new IllegalArgumentException("Pool entry may not be null");
Modified: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java Mon Aug 22 15:41:34 2011
@@ -424,6 +424,66 @@ public class TestConnPool {
}
@Test
+ public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1, conn2, conn3);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+ pool.setMaxPerRoute("somehost", 2);
+ pool.setMaxTotal(2);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
+ t1.start();
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
+ t2.start();
+
+ t1.join(GRACE_PERIOD);
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry1 = t1.getEntry();
+ Assert.assertNotNull(entry1);
+ t2.join(GRACE_PERIOD);
+ Assert.assertTrue(future2.isDone());
+ LocalPoolEntry entry2 = t2.getEntry();
+ Assert.assertNotNull(entry2);
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(2, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ entry1.setState("some-stuff");
+ pool.release(entry1, true);
+ entry2.setState("some-stuff");
+ pool.release(entry2, true);
+
+ Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("somehost"));
+
+ Future<LocalPoolEntry> future3 = pool.lease("somehost", "some-other-stuff");
+ GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
+ t3.start();
+
+ t3.join(GRACE_PERIOD);
+ Assert.assertTrue(future3.isDone());
+ LocalPoolEntry entry3 = t3.getEntry();
+ Assert.assertNotNull(entry3);
+
+ Mockito.verify(connFactory, Mockito.times(3)).create(Mockito.eq("somehost"));
+
+ Mockito.verify(conn1).close();
+ Mockito.verify(conn2, Mockito.never()).close();
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(1, totals.getAvailable());
+ Assert.assertEquals(1, totals.getLeased());
+ }
+
+ @Test
public void testCreateNewIfExpired() throws Exception {
HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
Modified: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java?rev=1160308&r1=1160307&r2=1160308&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java Mon Aug 22 15:41:34 2011
@@ -76,6 +76,7 @@ public class TestRouteSpecificPool {
Assert.assertEquals(0, pool.getAvailableCount());
Assert.assertEquals(0, pool.getLeasedCount());
Assert.assertEquals(0, pool.getPendingCount());
+ Assert.assertNull(pool.getLastUsed());
Assert.assertEquals("[route: whatever][leased: 0][available: 0][pending: 0]", pool.toString());
}
@@ -120,6 +121,8 @@ public class TestRouteSpecificPool {
Assert.assertEquals(0, pool.getLeasedCount());
Assert.assertEquals(0, pool.getPendingCount());
+ Assert.assertSame(entry1, pool.getLastUsed());
+
Assert.assertNotNull(pool.getFree(null));
Assert.assertNotNull(pool.getFree(null));
Assert.assertNull(pool.getFree(null));