hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject svn commit: r1099559 [1/2] - in /httpcomponents/httpasyncclient/trunk/httpasyncclient/src: main/java/org/apache/http/impl/nio/pool/ test/java/org/apache/http/impl/nio/concurrent/ test/java/org/apache/http/impl/nio/pool/
Date Wed, 04 May 2011 19:02:56 GMT
Author: olegk
Date: Wed May  4 19:02:55 2011
New Revision: 1099559

URL: http://svn.apache.org/viewvc?rev=1099559&view=rev
Log:
Fixed multiple bugs in non-blocking connection management code; near 100% test coverage for session pooling classes

Added:
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java   (with props)
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java   (with props)
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java   (with props)
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java   (with props)
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java   (with props)
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestSessionPool.java   (with props)
Modified:
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/LeaseRequest.java
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/RouteSpecificPool.java
    httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/SessionPool.java

Modified: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/LeaseRequest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/LeaseRequest.java?rev=1099559&r1=1099558&r2=1099559&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/LeaseRequest.java (original)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/LeaseRequest.java Wed May  4 19:02:55 2011
@@ -69,7 +69,7 @@ class LeaseRequest<T, E extends PoolEntr
         buffer.append("][");
         buffer.append(this.state);
         buffer.append("]");
-        return super.toString();
+        return buffer.toString();
     }
 
 }

Modified: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java?rev=1099559&r1=1099558&r2=1099559&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java (original)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java Wed May  4 19:02:55 2011
@@ -66,6 +66,7 @@ public abstract class PoolEntry<T> {
         } else {
             this.validUnit = Long.MAX_VALUE;
         }
+        this.expiry = this.validUnit;
     }
 
     protected T getRoute() {

Modified: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/RouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/RouteSpecificPool.java?rev=1099559&r1=1099558&r2=1099559&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/RouteSpecificPool.java (original)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/RouteSpecificPool.java Wed May  4 19:02:55 2011
@@ -29,8 +29,8 @@ package org.apache.http.impl.nio.pool;
 import java.net.SocketTimeoutException;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 
@@ -72,10 +72,21 @@ class RouteSpecificPool<T, E extends Poo
 
     public E getFreeEntry(final Object state) {
         if (!this.availableSessions.isEmpty()) {
-            ListIterator<E> it = this.availableSessions.listIterator(this.availableSessions.size());
-            while (it.hasPrevious()) {
-                E entry = it.previous();
-                if (entry.getState() == null || entry.getState().equals(state)) {
+            if (state != null) {
+                Iterator<E> it = this.availableSessions.iterator();
+                while (it.hasNext()) {
+                    E entry = it.next();
+                    if (state.equals(entry.getState())) {
+                        it.remove();
+                        this.leasedSessions.add(entry);
+                        return entry;
+                    }
+                }
+            }
+            Iterator<E> it = this.availableSessions.iterator();
+            while (it.hasNext()) {
+                E entry = it.next();
+                if (entry.getState() == null) {
                     it.remove();
                     this.leasedSessions.add(entry);
                     return entry;
@@ -85,17 +96,16 @@ class RouteSpecificPool<T, E extends Poo
         return null;
     }
 
-    public E deleteLastUsed() {
-        return this.availableSessions.poll();
-    }
-
     public boolean remove(final E entry) {
         if (entry == null) {
             throw new IllegalArgumentException("Pool entry may not be null");
         }
-        boolean foundLeased = this.leasedSessions.remove(entry);
-        boolean foundFree = this.availableSessions.remove(entry);
-        return foundLeased || foundFree;
+        if (!this.availableSessions.remove(entry)) {
+            if (!this.leasedSessions.remove(entry)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     public void freeEntry(final E entry, boolean reusable) {
@@ -177,7 +187,7 @@ class RouteSpecificPool<T, E extends Poo
         buffer.append("][pending: ");
         buffer.append(this.pendingSessions.size());
         buffer.append("]");
-        return super.toString();
+        return buffer.toString();
     }
 
 }

Modified: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/SessionPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/SessionPool.java?rev=1099559&r1=1099558&r2=1099559&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/SessionPool.java (original)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/SessionPool.java Wed May  4 19:02:55 2011
@@ -78,6 +78,12 @@ public abstract class SessionPool<T, E e
         if (routeResolver == null) {
             throw new IllegalArgumentException("Route resolver may not be null");
         }
+        if (defaultMaxPerRoute <= 0) {
+            throw new IllegalArgumentException("Max per route value may not be negative or zero");
+        }
+        if (maxTotal <= 0) {
+            throw new IllegalArgumentException("Max total value may not be negative or zero");
+        }
         this.ioreactor = ioreactor;
         this.factory = factory;
         this.sessionRequestCallback = new InternalSessionRequestCallback();
@@ -132,7 +138,10 @@ public abstract class SessionPool<T, E e
             throw new IllegalArgumentException("Route may not be null");
         }
         if (tunit == null) {
-            throw new IllegalArgumentException("Time unit must not be null.");
+            throw new IllegalArgumentException("Time unit may not be null.");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback may not be null.");
         }
         if (this.isShutDown) {
             throw new IllegalStateException("Session pool has been shut down");
@@ -164,7 +173,8 @@ public abstract class SessionPool<T, E e
                 if (reusable) {
                     this.availableSessions.add(entry);
                 } else {
-                    entryShutdown(entry);
+                    IOSession iosession = entry.getIOSession();
+                    iosession.close();
                 }
                 processPendingRequests();
             }
@@ -173,17 +183,6 @@ public abstract class SessionPool<T, E e
         }
     }
 
-    private int getAllocatedTotal() {
-        return this.leasedSessions.size() +
-            this.pendingSessions.size() +
-            this.availableSessions.size();
-    }
-
-    private void entryShutdown(final PoolEntry<T> entry) {
-        IOSession iosession = entry.getIOSession();
-        iosession.close();
-    }
-
     private void processPendingRequests() {
         ListIterator<LeaseRequest<T, E>> it = this.leasingRequests.listIterator();
         while (it.hasNext()) {
@@ -194,15 +193,6 @@ public abstract class SessionPool<T, E e
             int timeout = request.getConnectTimeout();
             PoolEntryCallback<T, E> callback = request.getCallback();
 
-            if (getAllocatedTotal() >= this.maxTotal) {
-                if (!this.availableSessions.isEmpty()) {
-                    E entry = this.availableSessions.remove();
-                    entryShutdown(entry);
-                    RouteSpecificPool<T, E> pool = getPool(entry.getRoute());
-                    pool.remove(entry);
-                }
-            }
-
             RouteSpecificPool<T, E> pool = getPool(request.getRoute());
             E entry = null;
             for (;;) {
@@ -211,7 +201,10 @@ public abstract class SessionPool<T, E e
                     break;
                 }
                 IOSession iosession = entry.getIOSession();
-                if (iosession.isClosed() || entry.isExpired(System.currentTimeMillis())) {
+                if (entry.isExpired(System.currentTimeMillis())) {
+                    iosession.close();
+                }
+                if (iosession.isClosed()) {
                     this.availableSessions.remove(entry);
                     pool.freeEntry(entry, false);
                 } else {
@@ -223,30 +216,41 @@ public abstract class SessionPool<T, E e
                 this.availableSessions.remove(entry);
                 this.leasedSessions.add(entry);
                 callback.completed(entry);
-            } else {
-                int max = getMaxPerRoute(route);
-                if (pool.getAvailableCount() > 0 && pool.getAllocatedCount() >= max) {
-                    entry = pool.deleteLastUsed();
-                    if (entry != null) {
-                        this.availableSessions.remove(entry);
-                        entryShutdown(entry);
-                    }
+                continue;
+            }
+            if (pool.getAllocatedCount() < getMaxPerRoute(route)) {
+                int totalUsed = this.pendingSessions.size() + this.leasedSessions.size();
+                int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
+                if (freeCapacity == 0) {
+                    continue;
                 }
-                if (pool.getAllocatedCount() < max) {
-                    it.remove();
-                    SessionRequest sessionRequest = this.ioreactor.connect(
-                            this.routeResolver.resolveRemoteAddress(route),
-                            this.routeResolver.resolveLocalAddress(route),
-                            route,
-                            this.sessionRequestCallback);
-                    sessionRequest.setConnectTimeout(timeout);
-                    this.pendingSessions.add(sessionRequest);
-                    pool.addPending(sessionRequest, callback);
+                int totalAvailable = this.availableSessions.size();
+                if (totalAvailable > freeCapacity - 1) {
+                    dropLastUsed();
                 }
+                it.remove();
+                SessionRequest sessionRequest = this.ioreactor.connect(
+                        this.routeResolver.resolveRemoteAddress(route),
+                        this.routeResolver.resolveLocalAddress(route),
+                        route,
+                        this.sessionRequestCallback);
+                sessionRequest.setConnectTimeout(timeout);
+                this.pendingSessions.add(sessionRequest);
+                pool.addPending(sessionRequest, callback);
             }
         }
     }
 
+    private void dropLastUsed() {
+        if (!this.availableSessions.isEmpty()) {
+            E entry = this.availableSessions.removeFirst();
+            IOSession iosession = entry.getIOSession();
+            iosession.close();
+            RouteSpecificPool<T, E> pool = getPool(entry.getRoute());
+            pool.remove(entry);
+        }
+    }
+
     protected void requestCompleted(final SessionRequest request) {
         if (this.isShutDown) {
             return;
@@ -405,8 +409,11 @@ public abstract class SessionPool<T, E e
             while (it.hasNext()) {
                 E entry = it.next();
                 if (entry.getUpdated() <= deadline) {
+                    IOSession iosession = entry.getIOSession();
+                    iosession.close();
+                    RouteSpecificPool<T, E> pool = getPool(entry.getRoute());
+                    pool.remove(entry);
                     it.remove();
-                    entryShutdown(entry);
                 }
             }
             processPendingRequests();
@@ -423,8 +430,11 @@ public abstract class SessionPool<T, E e
             while (it.hasNext()) {
                 E entry = it.next();
                 if (entry.isExpired(now)) {
+                    IOSession iosession = entry.getIOSession();
+                    iosession.close();
+                    RouteSpecificPool<T, E> pool = getPool(entry.getRoute());
+                    pool.remove(entry);
                     it.remove();
-                    entryShutdown(entry);
                 }
             }
             processPendingRequests();
@@ -437,13 +447,13 @@ public abstract class SessionPool<T, E e
     public String toString() {
         StringBuilder buffer = new StringBuilder();
         buffer.append("[leased: ");
-        buffer.append(this.leasedSessions.size());
+        buffer.append(this.leasedSessions);
         buffer.append("][available: ");
-        buffer.append(this.availableSessions.size());
+        buffer.append(this.availableSessions);
         buffer.append("][pending: ");
-        buffer.append(this.pendingSessions.size());
+        buffer.append(this.pendingSessions);
         buffer.append("]");
-        return super.toString();
+        return buffer.toString();
     }
 
     class InternalSessionRequestCallback implements SessionRequestCallback {

Added: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java?rev=1099559&view=auto
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java (added)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java Wed May  4 19:02:55 2011
@@ -0,0 +1,73 @@
+/*
+ * ====================================================================
+ * 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.http.impl.nio.concurrent;
+
+import org.apache.http.nio.concurrent.FutureCallback;
+
+class BasicFutureCallback<T> implements FutureCallback<T> {
+
+    private T result;
+    private Exception ex;
+    private boolean completed;
+    private boolean failed;
+    private boolean cancelled;
+
+    public void completed(final T result) {
+        this.result = result;
+        this.completed = true;
+    }
+
+    public T getResult() {
+        return this.result;
+    }
+
+    public Exception getException() {
+        return this.ex;
+    }
+
+    public void failed(final Exception ex) {
+        this.ex = ex;
+        this.failed = true;
+    }
+
+    public void cancelled() {
+        this.cancelled = true;
+    }
+
+    public boolean isCompleted() {
+        return this.completed;
+    }
+
+    public boolean isFailed() {
+        return this.failed;
+    }
+
+    public boolean isCancelled() {
+        return this.cancelled;
+    }
+
+}
\ No newline at end of file

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/BasicFutureCallback.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java?rev=1099559&view=auto
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java (added)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java Wed May  4 19:02:55 2011
@@ -0,0 +1,231 @@
+/*
+ * ====================================================================
+ * 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.http.impl.nio.concurrent;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Assert;
+
+import org.apache.http.nio.concurrent.BasicFuture;
+import org.junit.Test;
+
+public class TestBasicFuture {
+
+    @Test
+    public void testCompleted() throws Exception {
+        BasicFutureCallback<Object> callback = new BasicFutureCallback<Object>();
+        BasicFuture<Object> future = new BasicFuture<Object>(callback);
+
+        Assert.assertFalse(future.isDone());
+
+        Object result = new Object();
+        Exception boom = new Exception();
+        future.completed(result);
+        future.failed(boom);
+        Assert.assertTrue(callback.isCompleted());
+        Assert.assertSame(result, callback.getResult());
+        Assert.assertFalse(callback.isFailed());
+        Assert.assertNull(callback.getException());
+        Assert.assertFalse(callback.isCancelled());
+
+        Assert.assertSame(result, future.get());
+        Assert.assertTrue(future.isDone());
+        Assert.assertFalse(future.isCancelled());
+
+    }
+
+    @Test
+    public void testCompletedWithTimeout() throws Exception {
+        BasicFutureCallback<Object> callback = new BasicFutureCallback<Object>();
+        BasicFuture<Object> future = new BasicFuture<Object>(callback);
+
+        Assert.assertFalse(future.isDone());
+
+        Object result = new Object();
+        Exception boom = new Exception();
+        future.completed(result);
+        future.failed(boom);
+        Assert.assertTrue(callback.isCompleted());
+        Assert.assertSame(result, callback.getResult());
+        Assert.assertFalse(callback.isFailed());
+        Assert.assertNull(callback.getException());
+        Assert.assertFalse(callback.isCancelled());
+
+        Assert.assertSame(result, future.get(1, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(future.isDone());
+        Assert.assertFalse(future.isCancelled());
+    }
+
+    @Test
+    public void testFailed() throws Exception {
+        BasicFutureCallback<Object> callback = new BasicFutureCallback<Object>();
+        BasicFuture<Object> future = new BasicFuture<Object>(callback);
+        Object result = new Object();
+        Exception boom = new Exception();
+        future.failed(boom);
+        future.completed(result);
+        Assert.assertFalse(callback.isCompleted());
+        Assert.assertNull(callback.getResult());
+        Assert.assertTrue(callback.isFailed());
+        Assert.assertSame(boom, callback.getException());
+        Assert.assertFalse(callback.isCancelled());
+
+        try {
+            future.get();
+        } catch (ExecutionException ex) {
+            Assert.assertSame(boom, ex.getCause());
+        }
+        Assert.assertTrue(future.isDone());
+        Assert.assertFalse(future.isCancelled());
+    }
+
+    @Test
+    public void testCancelled() throws Exception {
+        BasicFutureCallback<Object> callback = new BasicFutureCallback<Object>();
+        BasicFuture<Object> future = new BasicFuture<Object>(callback);
+        Object result = new Object();
+        Exception boom = new Exception();
+        future.cancel(true);
+        future.failed(boom);
+        future.completed(result);
+        Assert.assertFalse(callback.isCompleted());
+        Assert.assertNull(callback.getResult());
+        Assert.assertFalse(callback.isFailed());
+        Assert.assertNull(callback.getException());
+        Assert.assertTrue(callback.isCancelled());
+
+        Assert.assertNull(future.get());
+        Assert.assertTrue(future.isDone());
+        Assert.assertTrue(future.isCancelled());
+    }
+
+    @Test
+    public void testAsyncCompleted() throws Exception {
+        final BasicFuture<Object> future = new BasicFuture<Object>(null);
+        final Object result = new Object();
+
+        Thread t = new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(100);
+                    future.completed(result);
+                } catch (InterruptedException boom) {
+                }
+            }
+
+        };
+        t.setDaemon(true);
+        t.start();
+        Assert.assertSame(result, future.get(60, TimeUnit.SECONDS));
+        Assert.assertTrue(future.isDone());
+        Assert.assertFalse(future.isCancelled());
+    }
+
+    @Test
+    public void testAsyncFailed() throws Exception {
+        final BasicFuture<Object> future = new BasicFuture<Object>(null);
+        final Exception boom = new Exception();
+
+        Thread t = new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(100);
+                    future.failed(boom);
+                } catch (InterruptedException ex) {
+                }
+            }
+
+        };
+        t.setDaemon(true);
+        t.start();
+        try {
+            future.get(60, TimeUnit.SECONDS);
+        } catch (ExecutionException ex) {
+            Assert.assertSame(boom, ex.getCause());
+        }
+        Assert.assertTrue(future.isDone());
+        Assert.assertFalse(future.isCancelled());
+    }
+
+    @Test
+    public void testAsyncCancelled() throws Exception {
+        final BasicFuture<Object> future = new BasicFuture<Object>(null);
+
+        Thread t = new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(100);
+                    future.cancel(true);
+                } catch (InterruptedException ex) {
+                }
+            }
+
+        };
+        t.setDaemon(true);
+        t.start();
+        Assert.assertNull(future.get(60, TimeUnit.SECONDS));
+        Assert.assertTrue(future.isDone());
+        Assert.assertTrue(future.isCancelled());
+    }
+
+    @Test(expected=TimeoutException.class)
+    public void testAsyncTimeout() throws Exception {
+        final BasicFuture<Object> future = new BasicFuture<Object>(null);
+        final Object result = new Object();
+
+        Thread t = new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(200);
+                    future.completed(result);
+                } catch (InterruptedException ex) {
+                }
+            }
+
+        };
+        t.setDaemon(true);
+        t.start();
+        future.get(1, TimeUnit.MILLISECONDS);
+    }
+
+    @Test(expected=TimeoutException.class)
+    public void testAsyncNegativeTimeout() throws Exception {
+        final BasicFuture<Object> future = new BasicFuture<Object>(null);
+        future.get(-1, TimeUnit.MILLISECONDS);
+    }
+
+}

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/concurrent/TestBasicFuture.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java?rev=1099559&view=auto
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java (added)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java Wed May  4 19:02:55 2011
@@ -0,0 +1,71 @@
+/*
+ * ====================================================================
+ * 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.http.impl.nio.pool;
+
+class BasicPoolEntryCallback implements PoolEntryCallback<String, PoolEntry<String>> {
+
+    private PoolEntry<String> entry;
+    private Exception ex;
+    private boolean completed;
+    private boolean failed;
+    private boolean cancelled;
+
+    public void completed(final PoolEntry<String> entry) {
+        this.entry = entry;
+        this.completed = true;
+    }
+
+    public PoolEntry<String> getEntry() {
+        return this.entry;
+    }
+
+    public Exception getException() {
+        return this.ex;
+    }
+
+    public void failed(final Exception ex) {
+        this.ex = ex;
+        this.failed = true;
+    }
+
+    public void cancelled() {
+        this.cancelled = true;
+    }
+
+    public boolean isCompleted() {
+        return this.completed;
+    }
+
+    public boolean isFailed() {
+        return this.failed;
+    }
+
+    public boolean isCancelled() {
+        return this.cancelled;
+    }
+
+}
\ No newline at end of file

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/BasicPoolEntryCallback.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java?rev=1099559&view=auto
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java (added)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java Wed May  4 19:02:55 2011
@@ -0,0 +1,112 @@
+/*
+ * ====================================================================
+ * 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.http.impl.nio.pool;
+
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+import org.apache.http.nio.reactor.IOSession;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestPoolEntry {
+
+    static class MockPoolEntry extends PoolEntry<String> {
+
+        public MockPoolEntry(final String route,
+                long timeToLive, final TimeUnit tunit) {
+            super(route, Mockito.mock(IOSession.class), timeToLive, tunit);
+        }
+
+        public MockPoolEntry(final String route, final IOSession iosession,
+                long timeToLive, final TimeUnit tunit) {
+            super(route, iosession, timeToLive, tunit);
+        }
+
+    }
+
+    @Test
+    public void testBasics() throws Exception {
+        MockPoolEntry entry1 = new MockPoolEntry("route1", 10L, TimeUnit.MILLISECONDS);
+        long now = System.currentTimeMillis();
+        Assert.assertEquals("route1", entry1.getRoute());
+        Assert.assertTrue(now >= entry1.getCreated());
+        Assert.assertEquals(entry1.getValidUnit(), entry1.getExpiry());
+        Assert.assertEquals(entry1.getCreated() + 10L, entry1.getValidUnit());
+    }
+
+    @Test
+    public void testInvalidConstruction() throws Exception {
+        try {
+            new MockPoolEntry(null, Mockito.mock(IOSession.class), 0L, TimeUnit.MILLISECONDS);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            new MockPoolEntry("stuff", null, 0L, TimeUnit.MILLISECONDS);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            new MockPoolEntry("stuff", Mockito.mock(IOSession.class), 0L, null);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testValidInfinitely() throws Exception {
+        MockPoolEntry entry1 = new MockPoolEntry("route1", 0L, TimeUnit.MILLISECONDS);
+        Assert.assertEquals(Long.MAX_VALUE, entry1.getValidUnit());
+        Assert.assertEquals(entry1.getValidUnit(), entry1.getExpiry());
+    }
+
+    @Test
+    public void testExpiry() throws Exception {
+        MockPoolEntry entry1 = new MockPoolEntry("route1", 0L, TimeUnit.MILLISECONDS);
+        Assert.assertEquals(Long.MAX_VALUE, entry1.getExpiry());
+        entry1.updateExpiry(50L, TimeUnit.MILLISECONDS);
+        Assert.assertEquals(entry1.getUpdated() + 50L, entry1.getExpiry());
+        entry1.updateExpiry(0L, TimeUnit.MILLISECONDS);
+        Assert.assertEquals(Long.MAX_VALUE, entry1.getExpiry());
+
+        MockPoolEntry entry2 = new MockPoolEntry("route1", 100L, TimeUnit.MILLISECONDS);
+        Assert.assertEquals(entry2.getCreated() + 100L, entry2.getExpiry());
+        entry2.updateExpiry(150L, TimeUnit.MILLISECONDS);
+        Assert.assertEquals(entry2.getCreated() + 100L, entry2.getExpiry());
+        entry2.updateExpiry(50L, TimeUnit.MILLISECONDS);
+        Assert.assertEquals(entry2.getUpdated() + 50L, entry2.getExpiry());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testInvalidExpiry() throws Exception {
+        MockPoolEntry entry1 = new MockPoolEntry("route1", 0L, TimeUnit.MILLISECONDS);
+        entry1.updateExpiry(50L, null);
+    }
+
+}

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestPoolEntry.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java?rev=1099559&view=auto
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java (added)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java Wed May  4 19:02:55 2011
@@ -0,0 +1,386 @@
+/*
+ * ====================================================================
+ * 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.http.impl.nio.pool;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.reactor.SessionRequest;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestRouteSpecificPool {
+
+    static class LocalPoolEntry extends PoolEntry<String> {
+
+        public LocalPoolEntry(final String route, final IOSession iosession,
+                long timeToLive, final TimeUnit tunit) {
+            super(route, iosession, timeToLive, tunit);
+        }
+
+    }
+
+    static class LocalPoolEntryFactory implements PoolEntryFactory<String, PoolEntry<String>> {
+
+        public PoolEntry<String> createEntry(final String route, final IOSession session) {
+            return new LocalPoolEntry(route, session, 0L, TimeUnit.MILLISECONDS);
+        }
+
+    };
+
+    static class LocalRoutePool extends RouteSpecificPool<String, PoolEntry<String>> {
+
+        public LocalRoutePool() {
+            super("whatever", new LocalPoolEntryFactory());
+        }
+
+    };
+
+    @Test
+    public void testEmptyPool() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        Assert.assertEquals(0, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+        Assert.assertEquals("[route: whatever][leased: 0][available: 0][pending: 0]", pool.toString());
+    }
+
+    @Test
+    public void testSuccessfulConnect() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        IOSession session = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest.getSession()).thenReturn(session);
+        BasicPoolEntryCallback callback = new BasicPoolEntryCallback();
+        pool.addPending(sessionRequest, callback);
+        Assert.assertEquals(1, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(1, pool.getPendingCount());
+        PoolEntry<String> entry = pool.completed(sessionRequest);
+        Assert.assertNotNull(entry);
+        Assert.assertSame(session, entry.getIOSession());
+        Assert.assertTrue(callback.isCompleted());
+        Assert.assertFalse(callback.isFailed());
+        Assert.assertFalse(callback.isCancelled());
+
+        Assert.assertEquals(1, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(1, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+    }
+
+    @Test
+    public void testFailedConnect() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        IOException ex = new IOException();
+        SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest.getException()).thenReturn(ex);
+        BasicPoolEntryCallback callback = new BasicPoolEntryCallback();
+        pool.addPending(sessionRequest, callback);
+        Assert.assertEquals(1, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(1, pool.getPendingCount());
+        pool.failed(sessionRequest);
+        Assert.assertFalse(callback.isCompleted());
+        Assert.assertTrue(callback.isFailed());
+        Assert.assertFalse(callback.isCancelled());
+
+        Assert.assertEquals(0, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+    }
+
+    @Test
+    public void testCancelledConnect() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
+        BasicPoolEntryCallback callback = new BasicPoolEntryCallback();
+        pool.addPending(sessionRequest, callback);
+        Assert.assertEquals(1, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(1, pool.getPendingCount());
+        pool.cancelled(sessionRequest);
+        Assert.assertFalse(callback.isCompleted());
+        Assert.assertFalse(callback.isFailed());
+        Assert.assertTrue(callback.isCancelled());
+
+        Assert.assertEquals(0, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+    }
+
+    @Test
+    public void testConnectTimeout() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
+        BasicPoolEntryCallback callback = new BasicPoolEntryCallback();
+        pool.addPending(sessionRequest, callback);
+        Assert.assertEquals(1, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(1, pool.getPendingCount());
+        pool.timeout(sessionRequest);
+        Assert.assertFalse(callback.isCompleted());
+        Assert.assertTrue(callback.isFailed());
+        Assert.assertFalse(callback.isCancelled());
+        Assert.assertTrue(callback.getException() instanceof SocketTimeoutException);
+
+        Assert.assertEquals(0, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+    }
+
+    @Test
+    public void testLeaseRelease() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        IOSession session1 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest1.getSession()).thenReturn(session1);
+        pool.addPending(sessionRequest1, new BasicPoolEntryCallback());
+        IOSession session2 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest2.getSession()).thenReturn(session2);
+        pool.addPending(sessionRequest2, new BasicPoolEntryCallback());
+        IOSession session3 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest3 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest3.getSession()).thenReturn(session3);
+        pool.addPending(sessionRequest3, new BasicPoolEntryCallback());
+
+        Assert.assertEquals(3, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(3, pool.getPendingCount());
+
+        PoolEntry<String> entry1 = pool.completed(sessionRequest1);
+        Assert.assertNotNull(entry1);
+        PoolEntry<String> entry2 = pool.completed(sessionRequest2);
+        Assert.assertNotNull(entry2);
+        PoolEntry<String> entry3 = pool.completed(sessionRequest3);
+        Assert.assertNotNull(entry3);
+
+        Assert.assertEquals(3, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(3, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+
+        pool.freeEntry(entry1, true);
+        pool.freeEntry(entry2, false);
+        pool.freeEntry(entry3, true);
+
+        Assert.assertEquals(2, pool.getAllocatedCount());
+        Assert.assertEquals(2, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+
+        Assert.assertNotNull(pool.getFreeEntry(null));
+        Assert.assertNotNull(pool.getFreeEntry(null));
+        Assert.assertNull(pool.getFreeEntry(null));
+
+        Assert.assertEquals(2, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(2, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+    }
+
+    @Test
+    public void testLeaseReleaseStateful() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        IOSession session1 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest1.getSession()).thenReturn(session1);
+        pool.addPending(sessionRequest1, new BasicPoolEntryCallback());
+        IOSession session2 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest2.getSession()).thenReturn(session2);
+        pool.addPending(sessionRequest2, new BasicPoolEntryCallback());
+        IOSession session3 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest3 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest3.getSession()).thenReturn(session3);
+        pool.addPending(sessionRequest3, new BasicPoolEntryCallback());
+
+        PoolEntry<String> entry1 = pool.completed(sessionRequest1);
+        Assert.assertNotNull(entry1);
+        PoolEntry<String> entry2 = pool.completed(sessionRequest2);
+        Assert.assertNotNull(entry2);
+        PoolEntry<String> entry3 = pool.completed(sessionRequest3);
+        Assert.assertNotNull(entry3);
+
+        entry2.setState(Boolean.FALSE);
+        pool.freeEntry(entry1, true);
+        pool.freeEntry(entry2, true);
+        pool.freeEntry(entry3, true);
+
+        Assert.assertEquals(entry2, pool.getFreeEntry(Boolean.FALSE));
+        Assert.assertEquals(entry1, pool.getFreeEntry(Boolean.FALSE));
+        Assert.assertEquals(entry3, pool.getFreeEntry(null));
+        Assert.assertEquals(null, pool.getFreeEntry(null));
+
+        entry1.setState(Boolean.TRUE);
+        entry2.setState(Boolean.FALSE);
+        entry3.setState(Boolean.TRUE);
+        pool.freeEntry(entry1, true);
+        pool.freeEntry(entry2, true);
+        pool.freeEntry(entry3, true);
+
+        Assert.assertEquals(null, pool.getFreeEntry(null));
+        Assert.assertEquals(entry2, pool.getFreeEntry(Boolean.FALSE));
+        Assert.assertEquals(null, pool.getFreeEntry(Boolean.FALSE));
+        Assert.assertEquals(entry1, pool.getFreeEntry(Boolean.TRUE));
+        Assert.assertEquals(entry3, pool.getFreeEntry(Boolean.TRUE));
+        Assert.assertEquals(null, pool.getFreeEntry(Boolean.TRUE));
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testReleaseInvalidEntry() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        IOSession session = Mockito.mock(IOSession.class);
+        LocalPoolEntry entry = new LocalPoolEntry("whatever", session, 0L, TimeUnit.MILLISECONDS);
+        pool.freeEntry(entry, true);
+    }
+
+    @Test
+    public void testRemove() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        IOSession session1 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest1.getSession()).thenReturn(session1);
+        pool.addPending(sessionRequest1, new BasicPoolEntryCallback());
+        IOSession session2 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest2.getSession()).thenReturn(session2);
+        pool.addPending(sessionRequest2, new BasicPoolEntryCallback());
+        IOSession session3 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest3 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest3.getSession()).thenReturn(session3);
+        pool.addPending(sessionRequest3, new BasicPoolEntryCallback());
+
+        Assert.assertEquals(3, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(3, pool.getPendingCount());
+
+        PoolEntry<String> entry1 = pool.completed(sessionRequest1);
+        Assert.assertNotNull(entry1);
+        PoolEntry<String> entry2 = pool.completed(sessionRequest2);
+        Assert.assertNotNull(entry2);
+        PoolEntry<String> entry3 = pool.completed(sessionRequest3);
+        Assert.assertNotNull(entry3);
+
+        Assert.assertEquals(3, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(3, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+
+        Assert.assertTrue(pool.remove(entry2));
+        Assert.assertFalse(pool.remove(entry2));
+
+        Assert.assertEquals(2, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(2, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+
+        pool.freeEntry(entry1, true);
+        pool.freeEntry(entry3, true);
+
+        Assert.assertEquals(2, pool.getAllocatedCount());
+        Assert.assertEquals(2, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+
+        Assert.assertTrue(pool.remove(entry1));
+        Assert.assertTrue(pool.remove(entry3));
+
+        Assert.assertEquals(0, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testReleaseInvalid() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        pool.freeEntry(null, true);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testRemoveInvalid() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        pool.remove(null);
+    }
+
+    @Test
+    public void testShutdown() throws Exception {
+        LocalRoutePool pool = new LocalRoutePool();
+        IOSession session1 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest1.getSession()).thenReturn(session1);
+        pool.addPending(sessionRequest1, new BasicPoolEntryCallback());
+        IOSession session2 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest2.getSession()).thenReturn(session2);
+        pool.addPending(sessionRequest2, new BasicPoolEntryCallback());
+        IOSession session3 = Mockito.mock(IOSession.class);
+        SessionRequest sessionRequest3 = Mockito.mock(SessionRequest.class);
+        Mockito.when(sessionRequest3.getSession()).thenReturn(session3);
+        pool.addPending(sessionRequest3, new BasicPoolEntryCallback());
+
+        PoolEntry<String> entry1 = pool.completed(sessionRequest1);
+        Assert.assertNotNull(entry1);
+        PoolEntry<String> entry2 = pool.completed(sessionRequest2);
+        Assert.assertNotNull(entry2);
+
+        pool.freeEntry(entry1, true);
+
+        Assert.assertEquals(3, pool.getAllocatedCount());
+        Assert.assertEquals(1, pool.getAvailableCount());
+        Assert.assertEquals(1, pool.getLeasedCount());
+        Assert.assertEquals(1, pool.getPendingCount());
+
+        pool.shutdown();
+
+        Assert.assertEquals(0, pool.getAllocatedCount());
+        Assert.assertEquals(0, pool.getAvailableCount());
+        Assert.assertEquals(0, pool.getLeasedCount());
+        Assert.assertEquals(0, pool.getPendingCount());
+
+        Mockito.verify(sessionRequest3).cancel();
+        Mockito.verify(session2).close();
+        Mockito.verify(session1).close();
+    }
+
+}

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/impl/nio/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message