hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject svn commit: r1772441 [4/4] - in /httpcomponents/httpcore/trunk: httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/ httpcore5-ab/src/test/java/org/apache/hc/core5/http/benchmark/ httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/...
Date Sat, 03 Dec 2016 09:47:15 GMT
Copied: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java (from r1772397, httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/nio/AbstractNIOConnPool.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java?p2=httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java&p1=httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/nio/AbstractNIOConnPool.java&r1=1772397&r2=1772441&rev=1772441&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/nio/AbstractNIOConnPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java Sat Dec  3 09:47:13 2016
@@ -24,10 +24,9 @@
  * <http://www.apache.org/>.
  *
  */
-package org.apache.hc.core5.pool.nio;
+package org.apache.hc.core5.pool;
 
-import java.io.IOException;
-import java.net.SocketAddress;
+import java.io.Closeable;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -47,43 +46,30 @@ import org.apache.hc.core5.annotation.Co
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.concurrent.BasicFuture;
 import org.apache.hc.core5.concurrent.FutureCallback;
-import org.apache.hc.core5.net.NamedEndpoint;
-import org.apache.hc.core5.pool.ConnPool;
-import org.apache.hc.core5.pool.ConnPoolControl;
-import org.apache.hc.core5.pool.PoolEntry;
-import org.apache.hc.core5.pool.PoolEntryCallback;
-import org.apache.hc.core5.pool.PoolStats;
-import org.apache.hc.core5.reactor.ConnectionInitiator;
-import org.apache.hc.core5.reactor.IOSession;
-import org.apache.hc.core5.reactor.SessionRequest;
-import org.apache.hc.core5.reactor.SessionRequestCallback;
+import org.apache.hc.core5.function.Callback;
 import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.Asserts;
 import org.apache.hc.core5.util.LangUtils;
 
 /**
- * Abstract non-blocking connection pool.
+ * Connection pool with strict max connection limit guarantees.
  *
  * @param <T> route
  * @param <C> connection object
- * @param <E> pool entry
  *
  * @since 4.2
  */
 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
-public abstract class AbstractNIOConnPool<T, C, E extends PoolEntry<T, C>>
-                                                  implements ConnPool<T, E>, ConnPoolControl<T> {
+public class StrictConnPool<T, C extends Closeable> implements ControlledConnPool<T, C> {
 
-    private final ConnectionInitiator connectionInitiator;
-    private final NIOConnFactory<T, C> connFactory;
-    private final SocketAddressResolver<T> addressResolver;
-    private final SessionRequestCallback sessionRequestCallback;
-    private final Map<T, RouteSpecificPool<T, C, E>> routeToPool;
-    private final LinkedList<LeaseRequest<T, C, E>> leasingRequests;
-    private final Set<SessionRequest> pending;
-    private final Set<E> leased;
-    private final LinkedList<E> available;
-    private final ConcurrentLinkedQueue<LeaseRequest<T, C, E>> completedRequests;
+    private final long timeToLive;
+    private final TimeUnit timeUnit;
+    private ConnPoolListener<T> connPoolListener;
+    private final Map<T, RoutePool<T, C>> routeToPool;
+    private final LinkedList<LeaseRequest<T, C>> leasingRequests;
+    private final Set<PoolEntry<T, C>> leased;
+    private final LinkedList<PoolEntry<T, C>> available;
+    private final ConcurrentLinkedQueue<LeaseRequest<T, C>> completedRequests;
     private final Map<T, Integer> maxPerRoute;
     private final Lock lock;
     private final AtomicBoolean isShutDown;
@@ -92,27 +78,22 @@ public abstract class AbstractNIOConnPoo
     private volatile int maxTotal;
 
     /**
-     * @since 4.3
+     * @since 5.0
      */
-    public AbstractNIOConnPool(
-            final ConnectionInitiator connectionInitiator,
-            final NIOConnFactory<T, C> connFactory,
-            final SocketAddressResolver<T> addressResolver,
+    public StrictConnPool(
             final int defaultMaxPerRoute,
-            final int maxTotal) {
+            final int maxTotal,
+            final long timeToLive,
+            final TimeUnit timeUnit,
+            final ConnPoolListener<T> connPoolListener) {
         super();
-        Args.notNull(connectionInitiator, "I/O reactor");
-        Args.notNull(connFactory, "Connection factory");
-        Args.notNull(addressResolver, "Address resolver");
         Args.positive(defaultMaxPerRoute, "Max per route value");
         Args.positive(maxTotal, "Max total value");
-        this.connectionInitiator = connectionInitiator;
-        this.connFactory = connFactory;
-        this.addressResolver = addressResolver;
-        this.sessionRequestCallback = new InternalSessionRequestCallback();
+        this.timeToLive = timeToLive;
+        this.timeUnit = timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS;
+        this.connPoolListener = connPoolListener;
         this.routeToPool = new HashMap<>();
         this.leasingRequests = new LinkedList<>();
-        this.pending = new HashSet<>();
         this.leased = new HashSet<>();
         this.available = new LinkedList<>();
         this.completedRequests = new ConcurrentLinkedQueue<>();
@@ -123,50 +104,25 @@ public abstract class AbstractNIOConnPoo
         this.maxTotal = maxTotal;
     }
 
-    protected abstract E createEntry(T route, C conn);
-
-    /**
-     * @since 4.3
-     */
-    protected void onLease(final E entry) {
-    }
-
-    /**
-     * @since 4.3
-     */
-    protected void onRelease(final E entry) {
-    }
-
-    /**
-     * @since 4.4
-     */
-    protected void onReuse(final E entry) {
+    public StrictConnPool(final int defaultMaxPerRoute, final int maxTotal) {
+        this(defaultMaxPerRoute, maxTotal, 0, TimeUnit.MILLISECONDS, null);
     }
 
     public boolean isShutdown() {
         return this.isShutDown.get();
     }
 
+    @Override
     public void shutdown() {
         if (this.isShutDown.compareAndSet(false, true)) {
             fireCallbacks();
             this.lock.lock();
             try {
-                for (final SessionRequest sessionRequest: this.pending) {
-                    sessionRequest.cancel();
-                }
-                for (final E entry: this.available) {
-                    entry.close();
-                }
-                for (final E entry: this.leased) {
-                    entry.close();
-                }
-                for (final RouteSpecificPool<T, C, E> pool: this.routeToPool.values()) {
+                for (final RoutePool<T, C> pool: this.routeToPool.values()) {
                     pool.shutdown();
                 }
                 this.routeToPool.clear();
                 this.leased.clear();
-                this.pending.clear();
                 this.available.clear();
                 this.leasingRequests.clear();
             } finally {
@@ -175,44 +131,31 @@ public abstract class AbstractNIOConnPoo
         }
     }
 
-    private RouteSpecificPool<T, C, E> getPool(final T route) {
-        RouteSpecificPool<T, C, E> pool = this.routeToPool.get(route);
-        if (pool == null) {
-            pool = new RouteSpecificPool<T, C, E>(route) {
-
-                @Override
-                protected E createEntry(final T route, final C conn) {
-                    return AbstractNIOConnPool.this.createEntry(route, conn);
-                }
+    @Override
+    public void close() throws Exception {
+        shutdown();
+    }
 
-            };
+    private RoutePool<T, C> getPool(final T route) {
+        RoutePool<T, C> pool = this.routeToPool.get(route);
+        if (pool == null) {
+            pool = new RoutePool<>(route);
             this.routeToPool.put(route, pool);
         }
         return pool;
     }
 
-    public Future<E> lease(
+    public Future<PoolEntry<T, C>> lease(
             final T route, final Object state,
-            final long connectTimeout, final TimeUnit tunit,
-            final FutureCallback<E> callback) {
-        return this.lease(route, state, connectTimeout, connectTimeout, tunit, callback);
-    }
-
-    /**
-     * @since 4.3
-     */
-    public Future<E> lease(
-            final T route, final Object state,
-            final long connectTimeout, final long leaseTimeout, final TimeUnit tunit,
-            final FutureCallback<E> callback) {
+            final long leaseTimeout, final TimeUnit tunit,
+            final FutureCallback<PoolEntry<T, C>> callback) {
         Args.notNull(route, "Route");
         Args.notNull(tunit, "Time unit");
         Asserts.check(!this.isShutDown.get(), "Connection pool shut down");
-        final BasicFuture<E> future = new BasicFuture<>(callback);
+        final BasicFuture<PoolEntry<T, C>> future = new BasicFuture<>(callback);
         this.lock.lock();
         try {
-            final long timeout = connectTimeout > 0 ? tunit.toMillis(connectTimeout) : 0;
-            final LeaseRequest<T, C, E> request = new LeaseRequest<>(route, state, timeout, leaseTimeout, future);
+            final LeaseRequest<T, C> request = new LeaseRequest<>(route, state, leaseTimeout, future);
             final boolean completed = processPendingRequest(request);
             if (!request.isDone() && !completed) {
                 this.leasingRequests.add(request);
@@ -228,32 +171,38 @@ public abstract class AbstractNIOConnPoo
     }
 
     @Override
-    public Future<E> lease(final T route, final Object state, final FutureCallback<E> callback) {
+    public Future<PoolEntry<T, C>> lease(final T route, final Object state, final FutureCallback<PoolEntry<T, C>> callback) {
         return lease(route, state, -1, TimeUnit.MICROSECONDS, callback);
     }
 
-    public Future<E> lease(final T route, final Object state) {
+    public Future<PoolEntry<T, C>> lease(final T route, final Object state) {
         return lease(route, state, -1, TimeUnit.MICROSECONDS, null);
     }
 
     @Override
-    public void release(final E entry, final boolean reusable) {
+    public void release(final PoolEntry<T, C> entry, final boolean reusable) {
         if (entry == null) {
             return;
         }
         if (this.isShutDown.get()) {
             return;
         }
+        if (!reusable) {
+            entry.discardConnection();
+        }
         this.lock.lock();
         try {
             if (this.leased.remove(entry)) {
-                final RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
-                pool.free(entry, reusable);
-                if (reusable) {
+                final RoutePool<T, C> pool = getPool(entry.getRoute());
+                final boolean keepAlive = entry.hasConnection() && reusable;
+                pool.free(entry, keepAlive);
+                if (keepAlive) {
                     this.available.addFirst(entry);
-                    onRelease(entry);
+                    if (this.connPoolListener != null) {
+                        this.connPoolListener.onRelease(entry.getRoute(), this);
+                    }
                 } else {
-                    entry.close();
+                    entry.discardConnection();
                 }
                 processNextPendingRequest();
             }
@@ -264,10 +213,10 @@ public abstract class AbstractNIOConnPoo
     }
 
     private void processPendingRequests() {
-        final ListIterator<LeaseRequest<T, C, E>> it = this.leasingRequests.listIterator();
+        final ListIterator<LeaseRequest<T, C>> it = this.leasingRequests.listIterator();
         while (it.hasNext()) {
-            final LeaseRequest<T, C, E> request = it.next();
-            final BasicFuture<E> future = request.getFuture();
+            final LeaseRequest<T, C> request = it.next();
+            final BasicFuture<PoolEntry<T, C>> future = request.getFuture();
             if (future.isCancelled()) {
                 it.remove();
                 continue;
@@ -283,10 +232,10 @@ public abstract class AbstractNIOConnPoo
     }
 
     private void processNextPendingRequest() {
-        final ListIterator<LeaseRequest<T, C, E>> it = this.leasingRequests.listIterator();
+        final ListIterator<LeaseRequest<T, C>> it = this.leasingRequests.listIterator();
         while (it.hasNext()) {
-            final LeaseRequest<T, C, E> request = it.next();
-            final BasicFuture<E> future = request.getFuture();
+            final LeaseRequest<T, C> request = it.next();
+            final BasicFuture<PoolEntry<T, C>> future = request.getFuture();
             if (future.isCancelled()) {
                 it.remove();
                 continue;
@@ -304,7 +253,7 @@ public abstract class AbstractNIOConnPoo
         }
     }
 
-    private boolean processPendingRequest(final LeaseRequest<T, C, E> request) {
+    private boolean processPendingRequest(final LeaseRequest<T, C> request) {
         final T route = request.getRoute();
         final Object state = request.getState();
         final long deadline = request.getDeadline();
@@ -315,15 +264,15 @@ public abstract class AbstractNIOConnPoo
             return false;
         }
 
-        final RouteSpecificPool<T, C, E> pool = getPool(route);
-        E entry;
+        final RoutePool<T, C> pool = getPool(route);
+        PoolEntry<T, C> entry;
         for (;;) {
             entry = pool.getFree(state);
             if (entry == null) {
                 break;
             }
-            if (entry.isClosed() || entry.isExpired(System.currentTimeMillis())) {
-                entry.close();
+            if (entry.getExpiry() < System.currentTimeMillis()) {
+                entry.discardConnection();
                 this.available.remove(entry);
                 pool.free(entry, false);
             } else {
@@ -334,8 +283,9 @@ public abstract class AbstractNIOConnPoo
             this.available.remove(entry);
             this.leased.add(entry);
             request.completed(entry);
-            onReuse(entry);
-            onLease(entry);
+            if (this.connPoolListener != null) {
+                this.connPoolListener.onLease(entry.getRoute(), this);
+            }
             return true;
         }
 
@@ -345,59 +295,48 @@ public abstract class AbstractNIOConnPoo
         final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);
         if (excess > 0) {
             for (int i = 0; i < excess; i++) {
-                final E lastUsed = pool.getLastUsed();
+                final PoolEntry<T, C> lastUsed = pool.getLastUsed();
                 if (lastUsed == null) {
                     break;
                 }
-                lastUsed.close();
+                lastUsed.discardConnection();
                 this.available.remove(lastUsed);
                 pool.remove(lastUsed);
             }
         }
 
         if (pool.getAllocatedCount() < maxPerRoute) {
-            final int totalUsed = this.pending.size() + this.leased.size();
-            final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
+            final int freeCapacity = Math.max(this.maxTotal - this.leased.size(), 0);
             if (freeCapacity == 0) {
                 return false;
             }
             final int totalAvailable = this.available.size();
             if (totalAvailable > freeCapacity - 1) {
                 if (!this.available.isEmpty()) {
-                    final E lastUsed = this.available.removeLast();
-                    lastUsed.close();
-                    final RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());
+                    final PoolEntry<T, C> lastUsed = this.available.removeLast();
+                    lastUsed.discardConnection();
+                    final RoutePool<T, C> otherpool = getPool(lastUsed.getRoute());
                     otherpool.remove(lastUsed);
                 }
             }
 
-            final NamedEndpoint remoteEndpoint;
-            final SocketAddress localAddress;
-            try {
-                remoteEndpoint = this.addressResolver.resolveRemoteEndpoint(route);
-                localAddress = this.addressResolver.resolveLocalAddress(route);
-            } catch (final IOException ex) {
-                request.failed(ex);
-                return false;
+            entry = pool.createEntry(this.timeToLive, this.timeUnit);
+            this.leased.add(entry);
+            request.completed(entry);
+            if (this.connPoolListener != null) {
+                this.connPoolListener.onLease(entry.getRoute(), this);
             }
-            final SessionRequest sessionRequest = this.connectionInitiator.connect(
-                    remoteEndpoint, localAddress, route, this.sessionRequestCallback);
-            final int timout = request.getConnectTimeout() < Integer.MAX_VALUE ?
-                    (int) request.getConnectTimeout() : Integer.MAX_VALUE;
-            sessionRequest.setConnectTimeout(timout);
-            this.pending.add(sessionRequest);
-            pool.addPending(sessionRequest, request.getFuture());
             return true;
         }
         return false;
     }
 
     private void fireCallbacks() {
-        LeaseRequest<T, C, E> request;
+        LeaseRequest<T, C> request;
         while ((request = this.completedRequests.poll()) != null) {
-            final BasicFuture<E> future = request.getFuture();
+            final BasicFuture<PoolEntry<T, C>> future = request.getFuture();
             final Exception ex = request.getException();
-            final E result = request.getResult();
+            final PoolEntry<T, C> result = request.getResult();
             boolean successfullyCompleted = false;
             if (ex != null) {
                 future.failed(ex);
@@ -418,9 +357,9 @@ public abstract class AbstractNIOConnPoo
         this.lock.lock();
         try {
             final long now = System.currentTimeMillis();
-            final ListIterator<LeaseRequest<T, C, E>> it = this.leasingRequests.listIterator();
+            final ListIterator<LeaseRequest<T, C>> it = this.leasingRequests.listIterator();
             while (it.hasNext()) {
-                final LeaseRequest<T, C, E> request = it.next();
+                final LeaseRequest<T, C> request = it.next();
                 final long deadline = request.getDeadline();
                 if (now > deadline) {
                     it.remove();
@@ -434,94 +373,6 @@ public abstract class AbstractNIOConnPoo
         fireCallbacks();
     }
 
-    protected void requestCompleted(final SessionRequest request) {
-        if (this.isShutDown.get()) {
-            return;
-        }
-        @SuppressWarnings("unchecked")
-        final
-        T route = (T) request.getAttachment();
-        this.lock.lock();
-        try {
-            this.pending.remove(request);
-            final RouteSpecificPool<T, C, E> pool = getPool(route);
-            final IOSession session = request.getSession();
-            try {
-                final C conn = this.connFactory.create(route, session);
-                final E entry = pool.createEntry(request, conn);
-                if (pool.completed(request, entry)) {
-                    this.leased.add(entry);
-                    onLease(entry);
-                } else {
-                    this.available.add(entry);
-                    processNextPendingRequest();
-                }
-            } catch (final IOException ex) {
-                pool.failed(request, ex);
-            }
-        } finally {
-            this.lock.unlock();
-        }
-        fireCallbacks();
-    }
-
-    protected void requestCancelled(final SessionRequest request) {
-        if (this.isShutDown.get()) {
-            return;
-        }
-        @SuppressWarnings("unchecked")
-        final
-        T route = (T) request.getAttachment();
-        this.lock.lock();
-        try {
-            this.pending.remove(request);
-            final RouteSpecificPool<T, C, E> pool = getPool(route);
-            pool.cancelled(request);
-            processNextPendingRequest();
-        } finally {
-            this.lock.unlock();
-        }
-        fireCallbacks();
-    }
-
-    protected void requestFailed(final SessionRequest request) {
-        if (this.isShutDown.get()) {
-            return;
-        }
-        @SuppressWarnings("unchecked")
-        final
-        T route = (T) request.getAttachment();
-        this.lock.lock();
-        try {
-            this.pending.remove(request);
-            final RouteSpecificPool<T, C, E> pool = getPool(route);
-            pool.failed(request, request.getException());
-            processNextPendingRequest();
-        } finally {
-            this.lock.unlock();
-        }
-        fireCallbacks();
-    }
-
-    protected void requestTimeout(final SessionRequest request) {
-        if (this.isShutDown.get()) {
-            return;
-        }
-        @SuppressWarnings("unchecked")
-        final
-        T route = (T) request.getAttachment();
-        this.lock.lock();
-        try {
-            this.pending.remove(request);
-            final RouteSpecificPool<T, C, E> pool = getPool(route);
-            pool.timeout(request);
-            processNextPendingRequest();
-        } finally {
-            this.lock.unlock();
-        }
-        fireCallbacks();
-    }
-
     private int getMax(final T route) {
         final Integer v = this.maxPerRoute.get(route);
         if (v != null) {
@@ -601,7 +452,7 @@ public abstract class AbstractNIOConnPoo
         try {
             return new PoolStats(
                     this.leased.size(),
-                    this.pending.size(),
+                    this.leasingRequests.size(),
                     this.available.size(),
                     this.maxTotal);
         } finally {
@@ -614,16 +465,16 @@ public abstract class AbstractNIOConnPoo
         Args.notNull(route, "Route");
         this.lock.lock();
         try {
-            final RouteSpecificPool<T, C, E> pool = getPool(route);
+            final RoutePool<T, C> pool = getPool(route);
             int pendingCount = 0;
-            for (LeaseRequest<T, C, E> request: leasingRequests) {
+            for (LeaseRequest<T, C> request: leasingRequests) {
                 if (LangUtils.equals(route, request.getRoute())) {
                     pendingCount++;
                 }
             }
             return new PoolStats(
                     pool.getLeasedCount(),
-                    pendingCount + pool.getPendingCount(),
+                    pendingCount,
                     pool.getAvailableCount(),
                     getMax(route));
         } finally {
@@ -650,15 +501,15 @@ public abstract class AbstractNIOConnPoo
      *
      * @since 4.3
      */
-    protected void enumAvailable(final PoolEntryCallback<T, C> callback) {
+    public void enumAvailable(final Callback<PoolEntry<T, C>> callback) {
         this.lock.lock();
         try {
-            final Iterator<E> it = this.available.iterator();
+            final Iterator<PoolEntry<T, C>> it = this.available.iterator();
             while (it.hasNext()) {
-                final E entry = it.next();
-                callback.process(entry);
-                if (entry.isClosed()) {
-                    final RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
+                final PoolEntry<T, C> entry = it.next();
+                callback.execute(entry);
+                if (!entry.hasConnection()) {
+                    final RoutePool<T, C> pool = getPool(entry.getRoute());
                     pool.remove(entry);
                     it.remove();
                 }
@@ -675,13 +526,13 @@ public abstract class AbstractNIOConnPoo
      *
      * @since 4.3
      */
-    protected void enumLeased(final PoolEntryCallback<T, C> callback) {
+    public void enumLeased(final Callback<PoolEntry<T, C>> callback) {
         this.lock.lock();
         try {
-            final Iterator<E> it = this.leased.iterator();
+            final Iterator<PoolEntry<T, C>> it = this.leased.iterator();
             while (it.hasNext()) {
-                final E entry = it.next();
-                callback.process(entry);
+                final PoolEntry<T, C> entry = it.next();
+                callback.execute(entry);
             }
             processPendingRequests();
         } finally {
@@ -690,16 +541,17 @@ public abstract class AbstractNIOConnPoo
     }
 
     private void purgePoolMap() {
-        final Iterator<Map.Entry<T, RouteSpecificPool<T, C, E>>> it = this.routeToPool.entrySet().iterator();
+        final Iterator<Map.Entry<T, RoutePool<T, C>>> it = this.routeToPool.entrySet().iterator();
         while (it.hasNext()) {
-            final Map.Entry<T, RouteSpecificPool<T, C, E>> entry = it.next();
-            final RouteSpecificPool<T, C, E> pool = entry.getValue();
+            final Map.Entry<T, RoutePool<T, C>> entry = it.next();
+            final RoutePool<T, C> pool = entry.getValue();
             if (pool.getAllocatedCount() == 0) {
                 it.remove();
             }
         }
     }
 
+    @Override
     public void closeIdle(final long idletime, final TimeUnit tunit) {
         Args.notNull(tunit, "Time unit");
         long time = tunit.toMillis(idletime);
@@ -707,26 +559,27 @@ public abstract class AbstractNIOConnPoo
             time = 0;
         }
         final long deadline = System.currentTimeMillis() - time;
-        enumAvailable(new PoolEntryCallback<T, C>() {
+        enumAvailable(new Callback<PoolEntry<T, C>>() {
 
             @Override
-            public void process(final PoolEntry<T, C> entry) {
+            public void execute(final PoolEntry<T, C> entry) {
                 if (entry.getUpdated() <= deadline) {
-                    entry.close();
+                    entry.discardConnection();
                 }
             }
 
         });
     }
 
+    @Override
     public void closeExpired() {
         final long now = System.currentTimeMillis();
-        enumAvailable(new PoolEntryCallback<T, C>() {
+        enumAvailable(new Callback<PoolEntry<T, C>>() {
 
             @Override
-            public void process(final PoolEntry<T, C> entry) {
-                if (entry.isExpired(now)) {
-                    entry.close();
+            public void execute(final PoolEntry<T, C> entry) {
+                if (entry.getExpiry() < now) {
+                    entry.discardConnection();
                 }
             }
 
@@ -737,37 +590,13 @@ public abstract class AbstractNIOConnPoo
     public String toString() {
         final StringBuilder buffer = new StringBuilder();
         buffer.append("[leased: ");
-        buffer.append(this.leased);
+        buffer.append(this.leased.size());
         buffer.append("][available: ");
-        buffer.append(this.available);
+        buffer.append(this.available.size());
         buffer.append("][pending: ");
-        buffer.append(this.pending);
+        buffer.append(this.leasingRequests.size());
         buffer.append("]");
         return buffer.toString();
     }
 
-    class InternalSessionRequestCallback implements SessionRequestCallback {
-
-        @Override
-        public void completed(final SessionRequest request) {
-            requestCompleted(request);
-        }
-
-        @Override
-        public void cancelled(final SessionRequest request) {
-            requestCancelled(request);
-        }
-
-        @Override
-        public void failed(final SessionRequest request) {
-            requestFailed(request);
-        }
-
-        @Override
-        public void timeout(final SessionRequest request) {
-            requestTimeout(request);
-        }
-
-    }
-
 }

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/AbstractMultiworkerIOReactor.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/AbstractMultiworkerIOReactor.java?rev=1772441&r1=1772440&r2=1772441&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/AbstractMultiworkerIOReactor.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/AbstractMultiworkerIOReactor.java Sat Dec  3 09:47:13 2016
@@ -45,6 +45,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.hc.core5.function.Callback;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -396,7 +397,7 @@ public abstract class AbstractMultiworke
      *
      * @since 5.0
      */
-    public void enumSessions(final IOSessionCallback callback) throws IOException {
+    public void enumSessions(final Callback<IOSession> callback) {
         if (callback == null) {
             return;
         }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorImpl.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorImpl.java?rev=1772441&r1=1772440&r2=1772441&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorImpl.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorImpl.java Sat Dec  3 09:47:13 2016
@@ -41,6 +41,7 @@ import java.util.concurrent.ConcurrentLi
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.hc.core5.function.Callback;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -321,7 +322,7 @@ class IOReactorImpl implements IOReactor
         }
     }
 
-    void enumSessions(final IOSessionCallback callback) throws IOException {
+    void enumSessions(final Callback<IOSession> callback) {
         if (this.selector.isOpen()) {
             try {
                 final Set<SelectionKey> keys = this.selector.keys();

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java?rev=1772441&r1=1772440&r2=1772441&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java Sat Dec  3 09:47:13 2016
@@ -27,6 +27,7 @@
 
 package org.apache.hc.core5.reactor;
 
+import java.io.Closeable;
 import java.net.SocketAddress;
 import java.nio.channels.ByteChannel;
 
@@ -46,7 +47,7 @@ import java.nio.channels.ByteChannel;
  *
  * @since 4.0
  */
-public interface IOSession {
+public interface IOSession extends Closeable {
 
     int ACTIVE       = 0;
     int CLOSING      = 1;

Modified: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestChunkCoding.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestChunkCoding.java?rev=1772441&r1=1772440&r2=1772441&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestChunkCoding.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestChunkCoding.java Sat Dec  3 09:47:13 2016
@@ -45,7 +45,7 @@ import org.apache.hc.core5.http.config.H
 import org.apache.hc.core5.http.io.SessionInputBuffer;
 import org.apache.hc.core5.http.io.SessionOutputBuffer;
 import org.apache.hc.core5.http.message.BasicHeader;
-import org.apache.hc.core5.http.Supplier;
+import org.apache.hc.core5.function.Supplier;
 import org.junit.Assert;
 import org.junit.Test;
 

Modified: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java?rev=1772441&r1=1772440&r2=1772441&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java Sat Dec  3 09:47:13 2016
@@ -40,15 +40,15 @@ import org.apache.hc.core5.http.HttpStat
 import org.apache.hc.core5.http.MethodNotSupportedException;
 import org.apache.hc.core5.http.ProtocolException;
 import org.apache.hc.core5.http.UnsupportedHttpVersionException;
-import org.apache.hc.core5.http.io.entity.InputStreamEntity;
+import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
 import org.apache.hc.core5.http.io.HttpRequestHandlerMapper;
 import org.apache.hc.core5.http.io.HttpServerConnection;
+import org.apache.hc.core5.http.io.entity.InputStreamEntity;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
-import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.junit.Assert;
 import org.junit.Before;
@@ -207,7 +207,8 @@ public class TestHttpService {
                 connReuseStrategy,
                 responseFactory,
                 handlerResolver,
-                expectationVerifier);
+                expectationVerifier,
+                null);
         final HttpCoreContext context = HttpCoreContext.create();
         final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
         request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
@@ -244,7 +245,8 @@ public class TestHttpService {
                 connReuseStrategy,
                 responseFactory,
                 handlerResolver,
-                expectationVerifier);
+                expectationVerifier,
+                null);
         final HttpCoreContext context = HttpCoreContext.create();
         final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
         request.addHeader(HttpHeaders.EXPECT, "100-continue");

Added: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java?rev=1772441&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java Sat Dec  3 09:47:13 2016
@@ -0,0 +1,90 @@
+/*
+ * ====================================================================
+ * 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.hc.core5.pool;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hc.core5.http.HttpConnection;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestPoolEntry {
+
+    @Test
+    public void testBasics() throws Exception {
+        final PoolEntry<String, HttpConnection> entry1 = new PoolEntry<>("route1", 10L, TimeUnit.MILLISECONDS);
+
+        Assert.assertEquals("route1", entry1.getRoute());
+        Assert.assertEquals(0, entry1.getUpdated());
+        Assert.assertEquals(0, entry1.getExpiry());
+
+        entry1.assignConnection(Mockito.mock(HttpConnection.class));
+        final long now = System.currentTimeMillis();
+        Assert.assertEquals("route1", entry1.getRoute());
+        Assert.assertTrue(now >= entry1.getUpdated());
+        Assert.assertEquals(entry1.getValidityDeadline(), entry1.getExpiry());
+        Assert.assertEquals(entry1.getUpdated() + 10L, entry1.getValidityDeadline());
+
+        entry1.discardConnection();
+        Assert.assertEquals(0, entry1.getUpdated());
+        Assert.assertEquals(0, entry1.getExpiry());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidConstruction() throws Exception {
+        new PoolEntry<String, HttpConnection>(null);
+    }
+
+    @Test
+    public void testValidInfinitely() throws Exception {
+        final PoolEntry<String, HttpConnection> entry1 = new PoolEntry<>("route1", 0L, TimeUnit.MILLISECONDS);
+        entry1.assignConnection(Mockito.mock(HttpConnection.class));
+        Assert.assertEquals(Long.MAX_VALUE, entry1.getValidityDeadline());
+        Assert.assertEquals(entry1.getValidityDeadline(), entry1.getExpiry());
+    }
+
+    @Test
+    public void testExpiry() throws Exception {
+        final PoolEntry<String, HttpConnection> entry1 = new PoolEntry<>("route1", 0L, TimeUnit.MILLISECONDS);
+        entry1.assignConnection(Mockito.mock(HttpConnection.class));
+        Assert.assertEquals(Long.MAX_VALUE, entry1.getExpiry());
+        entry1.updateConnection(50L, TimeUnit.MILLISECONDS, null);
+        Assert.assertEquals(entry1.getUpdated() + 50L, entry1.getExpiry());
+        entry1.updateConnection(0L, TimeUnit.MILLISECONDS, null);
+        Assert.assertEquals(Long.MAX_VALUE, entry1.getExpiry());
+
+        final PoolEntry<String, HttpConnection> entry2 = new PoolEntry<>("route1", 100L, TimeUnit.MILLISECONDS);
+        entry2.assignConnection(Mockito.mock(HttpConnection.class));
+        Assert.assertEquals(entry2.getUpdated() + 100L, entry2.getExpiry());
+        entry2.updateConnection(150L, TimeUnit.MILLISECONDS, null);
+        Assert.assertEquals(entry2.getUpdated() + 100L, entry2.getExpiry());
+        entry2.updateConnection(50L, TimeUnit.MILLISECONDS, null);
+        Assert.assertEquals(entry2.getUpdated() + 50L, entry2.getExpiry());
+    }
+
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Copied: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestRouteSpecificPool.java (from r1772397, httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/io/TestRouteSpecificPool.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestRouteSpecificPool.java?p2=httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestRouteSpecificPool.java&p1=httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/io/TestRouteSpecificPool.java&r1=1772397&r2=1772441&rev=1772441&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/io/TestRouteSpecificPool.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestRouteSpecificPool.java Sat Dec  3 09:47:13 2016
@@ -24,12 +24,11 @@
  * <http://www.apache.org/>.
  *
  */
-package org.apache.hc.core5.pool.io;
+package org.apache.hc.core5.pool;
 
-import java.io.IOException;
 import java.net.Socket;
+import java.util.concurrent.TimeUnit;
 
-import org.apache.hc.core5.pool.PoolEntry;
 import org.junit.Assert;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -38,74 +37,42 @@ public class TestRouteSpecificPool {
 
     private static final String ROUTE = "whatever";
 
-    static class LocalPoolEntry extends PoolEntry<String, Socket> {
-
-        public LocalPoolEntry(final String route, final Socket socket) {
-            super(null, route, socket);
-        }
-
-        @Override
-        public void close() {
-            try {
-                getConnection().close();
-            } catch (final IOException ignore) {
-            }
-        }
-
-        @Override
-        public boolean isClosed() {
-            return getConnection().isClosed();
-        }
-
-    }
-
-    static class LocalRoutePool extends RouteSpecificPool<String, Socket, LocalPoolEntry> {
-
-        public LocalRoutePool() {
-            super(ROUTE);
-        }
-
-        @Override
-        protected LocalPoolEntry createEntry(final Socket socket) {
-            return new LocalPoolEntry(getRoute(), socket);
-        }
-
-    }
-
     @Test
     public void testEmptyPool() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         Assert.assertEquals(ROUTE, pool.getRoute());
         Assert.assertEquals(0, pool.getAllocatedCount());
         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());
+        Assert.assertEquals("[route: whatever][leased: 0][available: 0]", pool.toString());
     }
 
     @Test
     public void testAdd() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         final Socket conn = Mockito.mock(Socket.class);
-        final PoolEntry<String, Socket> entry = pool.add(conn);
+        final PoolEntry<String, Socket> entry = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry.assignConnection(conn);
         Assert.assertEquals(1, pool.getAllocatedCount());
         Assert.assertEquals(0, pool.getAvailableCount());
         Assert.assertEquals(1, pool.getLeasedCount());
-        Assert.assertEquals(0, pool.getPendingCount());
         Assert.assertNotNull(entry);
         Assert.assertSame(conn, entry.getConnection());
     }
 
     @Test
     public void testLeaseRelease() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         final Socket conn1 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry1 = pool.add(conn1);
+        final PoolEntry<String, Socket> entry1 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry1.assignConnection(conn1);
         final Socket conn2 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry2 = pool.add(conn2);
+        final PoolEntry<String, Socket> entry2 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry2.assignConnection(conn2);
         final Socket conn3 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry3 = pool.add(conn3);
+        final PoolEntry<String, Socket> entry3 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry3.assignConnection(conn3);
 
         Assert.assertNotNull(entry1);
         Assert.assertNotNull(entry2);
@@ -114,7 +81,6 @@ public class TestRouteSpecificPool {
         Assert.assertEquals(3, pool.getAllocatedCount());
         Assert.assertEquals(0, pool.getAvailableCount());
         Assert.assertEquals(3, pool.getLeasedCount());
-        Assert.assertEquals(0, pool.getPendingCount());
 
         pool.free(entry1, true);
         pool.free(entry2, false);
@@ -123,7 +89,6 @@ public class TestRouteSpecificPool {
         Assert.assertEquals(2, pool.getAllocatedCount());
         Assert.assertEquals(2, pool.getAvailableCount());
         Assert.assertEquals(0, pool.getLeasedCount());
-        Assert.assertEquals(0, pool.getPendingCount());
 
         Assert.assertSame(entry1, pool.getLastUsed());
 
@@ -134,18 +99,20 @@ public class TestRouteSpecificPool {
         Assert.assertEquals(2, pool.getAllocatedCount());
         Assert.assertEquals(0, pool.getAvailableCount());
         Assert.assertEquals(2, pool.getLeasedCount());
-        Assert.assertEquals(0, pool.getPendingCount());
     }
 
     @Test
     public void testLeaseOrder() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         final Socket conn1 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry1 = pool.add(conn1);
+        final PoolEntry<String, Socket> entry1 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry1.assignConnection(conn1);
         final Socket conn2 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry2 = pool.add(conn2);
+        final PoolEntry<String, Socket> entry2 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry2.assignConnection(conn2);
         final Socket conn3 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry3 = pool.add(conn3);
+        final PoolEntry<String, Socket> entry3 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry3.assignConnection(conn3);
 
         Assert.assertNotNull(entry1);
         Assert.assertNotNull(entry2);
@@ -154,7 +121,6 @@ public class TestRouteSpecificPool {
         Assert.assertEquals(3, pool.getAllocatedCount());
         Assert.assertEquals(0, pool.getAvailableCount());
         Assert.assertEquals(3, pool.getLeasedCount());
-        Assert.assertEquals(0, pool.getPendingCount());
 
         pool.free(entry1, true);
         pool.free(entry2, true);
@@ -169,13 +135,16 @@ public class TestRouteSpecificPool {
 
     @Test
     public void testLeaseReleaseStateful() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         final Socket conn1 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry1 = pool.add(conn1);
+        final PoolEntry<String, Socket> entry1 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry1.assignConnection(conn1);
         final Socket conn2 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry2 = pool.add(conn2);
+        final PoolEntry<String, Socket> entry2 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry2.assignConnection(conn2);
         final Socket conn3 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry3 = pool.add(conn3);
+        final PoolEntry<String, Socket> entry3 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry3.assignConnection(conn3);
 
         Assert.assertNotNull(entry1);
         Assert.assertNotNull(entry2);
@@ -184,9 +153,8 @@ public class TestRouteSpecificPool {
         Assert.assertEquals(3, pool.getAllocatedCount());
         Assert.assertEquals(0, pool.getAvailableCount());
         Assert.assertEquals(3, pool.getLeasedCount());
-        Assert.assertEquals(0, pool.getPendingCount());
 
-        entry2.setState(Boolean.FALSE);
+        entry2.updateConnection(0, TimeUnit.MILLISECONDS, Boolean.FALSE);
         pool.free(entry1, true);
         pool.free(entry2, true);
         pool.free(entry3, true);
@@ -196,9 +164,9 @@ public class TestRouteSpecificPool {
         Assert.assertSame(entry1, pool.getFree(null));
         Assert.assertSame(null, pool.getFree(null));
 
-        entry1.setState(Boolean.TRUE);
-        entry2.setState(Boolean.FALSE);
-        entry3.setState(Boolean.TRUE);
+        entry1.updateConnection(0, TimeUnit.MILLISECONDS, Boolean.TRUE);
+        entry2.updateConnection(0, TimeUnit.MILLISECONDS, Boolean.FALSE);
+        entry3.updateConnection(0, TimeUnit.MILLISECONDS, Boolean.TRUE);
         pool.free(entry1, true);
         pool.free(entry2, true);
         pool.free(entry3, true);
@@ -213,21 +181,24 @@ public class TestRouteSpecificPool {
 
     @Test(expected=IllegalStateException.class)
     public void testReleaseInvalidEntry() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         final Socket conn = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry = new LocalPoolEntry(ROUTE, conn);
+        final PoolEntry<String, Socket> entry = new PoolEntry<>(ROUTE);
         pool.free(entry, true);
     }
 
     @Test
     public void testRemove() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         final Socket conn1 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry1 = pool.add(conn1);
+        final PoolEntry<String, Socket> entry1 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry1.assignConnection(conn1);
         final Socket conn2 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry2 = pool.add(conn2);
+        final PoolEntry<String, Socket> entry2 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry2.assignConnection(conn2);
         final Socket conn3 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry3 = pool.add(conn3);
+        final PoolEntry<String, Socket> entry3 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry3.assignConnection(conn3);
 
         Assert.assertNotNull(entry1);
         Assert.assertNotNull(entry2);
@@ -236,7 +207,6 @@ public class TestRouteSpecificPool {
         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));
@@ -244,7 +214,6 @@ public class TestRouteSpecificPool {
         Assert.assertEquals(2, pool.getAllocatedCount());
         Assert.assertEquals(0, pool.getAvailableCount());
         Assert.assertEquals(2, pool.getLeasedCount());
-        Assert.assertEquals(0, pool.getPendingCount());
 
         pool.free(entry1, true);
         pool.free(entry3, true);
@@ -252,7 +221,6 @@ public class TestRouteSpecificPool {
         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));
@@ -260,57 +228,29 @@ public class TestRouteSpecificPool {
         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 {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         pool.free(null, true);
     }
 
     @Test(expected=IllegalArgumentException.class)
     public void testRemoveInvalid() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         pool.remove(null);
     }
 
     @Test
-    public void testWaitingThreadQueuing() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
-        @SuppressWarnings("unchecked")
-        final PoolEntryFuture<LocalPoolEntry> future1 = Mockito.mock(PoolEntryFuture.class);
-        @SuppressWarnings("unchecked")
-        final PoolEntryFuture<LocalPoolEntry> future2 = Mockito.mock(PoolEntryFuture.class);
-
-        Assert.assertEquals(0, pool.getPendingCount());
-        pool.queue(future1);
-        Assert.assertEquals(1, pool.getPendingCount());
-        pool.queue(null);
-        Assert.assertEquals(1, pool.getPendingCount());
-        pool.queue(future2);
-        Assert.assertEquals(2, pool.getPendingCount());
-        Assert.assertSame(future1, pool.nextPending());
-        pool.unqueue(future1);
-        Assert.assertEquals(1, pool.getPendingCount());
-        Assert.assertSame(future2, pool.nextPending());
-        pool.unqueue(null);
-        Assert.assertEquals(0, pool.getPendingCount());
-        pool.unqueue(future2);
-        Assert.assertNull(pool.nextPending());
-    }
-
-    @Test
     public void testShutdown() throws Exception {
-        final LocalRoutePool pool = new LocalRoutePool();
+        final RoutePool<String, Socket> pool = new RoutePool<>("whatever");
         final Socket conn1 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry1 = pool.add(conn1);
+        final PoolEntry<String, Socket> entry1 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry1.assignConnection(conn1);
         final Socket conn2 = Mockito.mock(Socket.class);
-        final LocalPoolEntry entry2 = pool.add(conn2);
-
-        @SuppressWarnings("unchecked")
-        final PoolEntryFuture<LocalPoolEntry> future1 = Mockito.mock(PoolEntryFuture.class);
-        pool.queue(future1);
+        final PoolEntry<String, Socket> entry2 = pool.createEntry(0, TimeUnit.MILLISECONDS);
+        entry2.assignConnection(conn2);
 
         Assert.assertNotNull(entry1);
         Assert.assertNotNull(entry2);
@@ -320,16 +260,13 @@ public class TestRouteSpecificPool {
         Assert.assertEquals(2, 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(future1).cancel(true);
         Mockito.verify(conn2).close();
         Mockito.verify(conn1).close();
     }

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java?rev=1772441&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java Sat Dec  3 09:47:13 2016
@@ -0,0 +1,582 @@
+/*
+ * ====================================================================
+ * 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.hc.core5.pool;
+
+import java.util.Collections;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hc.core5.http.HttpConnection;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestStrictConnPool {
+
+    @Test
+    public void testEmptyPool() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10);
+        final PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(0, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+        Assert.assertEquals(10, totals.getMax());
+        Assert.assertEquals(Collections.emptySet(), pool.getRoutes());
+        final PoolStats stats = pool.getStats("somehost");
+        Assert.assertEquals(0, stats.getAvailable());
+        Assert.assertEquals(0, stats.getLeased());
+        Assert.assertEquals(0, stats.getPending());
+        Assert.assertEquals(2, stats.getMax());
+        Assert.assertEquals("[leased: 0][available: 0][pending: 0]", pool.toString());
+    }
+
+    @Test
+    public void testInvalidConstruction() throws Exception {
+        try {
+            new StrictConnPool<String, HttpConnection>(-1, 1);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+        try {
+            new StrictConnPool<String, HttpConnection>(1, -1);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testLeaseRelease() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10);
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("otherhost", null);
+
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        entry1.assignConnection(conn1);
+        final PoolEntry<String, HttpConnection> entry2 = future2.get();
+        Assert.assertNotNull(entry2);
+        entry2.assignConnection(conn2);
+        final PoolEntry<String, HttpConnection> entry3 = future3.get();
+        Assert.assertNotNull(entry3);
+        entry3.assignConnection(conn3);
+
+        pool.release(entry1, true);
+        pool.release(entry2, true);
+        pool.release(entry3, false);
+        Mockito.verify(conn1, Mockito.never()).close();
+        Mockito.verify(conn2, Mockito.never()).close();
+        Mockito.verify(conn3, Mockito.times(1)).close();
+
+        final PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(2, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+    }
+
+    @Test
+    public void testLeaseIllegal() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10);
+        try {
+            pool.lease(null, null, 0, TimeUnit.MILLISECONDS, null);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+        try {
+            pool.lease("somehost", null, 0, null, null);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testReleaseUnknownEntry() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+        pool.release(new PoolEntry<String, HttpConnection>("somehost"), true);
+    }
+
+    @Test
+    public void testMaxLimits() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10);
+        pool.setMaxPerRoute("somehost", 2);
+        pool.setMaxPerRoute("otherhost", 1);
+        pool.setMaxTotal(3);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("otherhost", null);
+
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        entry1.assignConnection(conn1);
+        final PoolEntry<String, HttpConnection> entry2 = future2.get();
+        Assert.assertNotNull(entry2);
+        entry2.assignConnection(conn2);
+        final PoolEntry<String, HttpConnection> entry3 = future3.get();
+        Assert.assertNotNull(entry3);
+        entry3.assignConnection(conn3);
+
+        pool.release(entry1, true);
+        pool.release(entry2, true);
+        pool.release(entry3, true);
+
+        final PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(3, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+
+        final Future<PoolEntry<String, HttpConnection>> future4 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future5 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future6 = pool.lease("otherhost", null);
+        final Future<PoolEntry<String, HttpConnection>> future7 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future8 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future9 = pool.lease("otherhost", null);
+
+        Assert.assertTrue(future4.isDone());
+        final PoolEntry<String, HttpConnection> entry4 = future4.get();
+        Assert.assertNotNull(entry4);
+        Assert.assertSame(conn2, entry4.getConnection());
+
+        Assert.assertTrue(future5.isDone());
+        final PoolEntry<String, HttpConnection> entry5 = future5.get();
+        Assert.assertNotNull(entry5);
+        Assert.assertSame(conn1, entry5.getConnection());
+
+        Assert.assertTrue(future6.isDone());
+        final PoolEntry<String, HttpConnection> entry6 = future6.get();
+        Assert.assertNotNull(entry6);
+        Assert.assertSame(conn3, entry6.getConnection());
+
+        Assert.assertFalse(future7.isDone());
+        Assert.assertFalse(future8.isDone());
+        Assert.assertFalse(future9.isDone());
+
+        pool.release(entry4, true);
+        pool.release(entry5, false);
+        pool.release(entry6, true);
+
+        Assert.assertTrue(future7.isDone());
+        final PoolEntry<String, HttpConnection> entry7 = future7.get();
+        Assert.assertNotNull(entry7);
+        Assert.assertSame(conn2, entry7.getConnection());
+
+        Assert.assertTrue(future8.isDone());
+        final PoolEntry<String, HttpConnection> entry8 = future8.get();
+        Assert.assertNotNull(entry8);
+        Assert.assertEquals(null, entry8.getConnection());
+
+        Assert.assertTrue(future9.isDone());
+        final PoolEntry<String, HttpConnection> entry9 = future9.get();
+        Assert.assertNotNull(entry9);
+        Assert.assertSame(conn3, entry9.getConnection());
+    }
+
+    @Test
+    public void testConnectionRedistributionOnTotalMaxLimit() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn4 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn5 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10);
+        pool.setMaxPerRoute("somehost", 2);
+        pool.setMaxPerRoute("otherhost", 2);
+        pool.setMaxTotal(2);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("otherhost", null);
+        final Future<PoolEntry<String, HttpConnection>> future4 = pool.lease("otherhost", null);
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        Assert.assertFalse(entry1.hasConnection());
+        entry1.assignConnection(conn1);
+        Assert.assertTrue(future2.isDone());
+        final PoolEntry<String, HttpConnection> entry2 = future2.get();
+        Assert.assertNotNull(entry2);
+        Assert.assertFalse(entry2.hasConnection());
+        entry2.assignConnection(conn2);
+
+        Assert.assertFalse(future3.isDone());
+        Assert.assertFalse(future4.isDone());
+
+        PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(0, totals.getAvailable());
+        Assert.assertEquals(2, totals.getLeased());
+        Assert.assertEquals(2, totals.getPending());
+
+        pool.release(entry1, true);
+        pool.release(entry2, true);
+
+        Assert.assertTrue(future3.isDone());
+        final PoolEntry<String, HttpConnection> entry3 = future3.get();
+        Assert.assertNotNull(entry3);
+        Assert.assertFalse(entry3.hasConnection());
+        entry3.assignConnection(conn3);
+        Assert.assertTrue(future4.isDone());
+        final PoolEntry<String, HttpConnection> entry4 = future4.get();
+        Assert.assertNotNull(entry4);
+        Assert.assertFalse(entry4.hasConnection());
+        entry4.assignConnection(conn4);
+
+        totals = pool.getTotalStats();
+        Assert.assertEquals(0, totals.getAvailable());
+        Assert.assertEquals(2, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+
+        final Future<PoolEntry<String, HttpConnection>> future5 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future6 = pool.lease("otherhost", null);
+
+        pool.release(entry3, true);
+        pool.release(entry4, true);
+
+        Assert.assertTrue(future5.isDone());
+        final PoolEntry<String, HttpConnection> entry5 = future5.get();
+        Assert.assertNotNull(entry5);
+        Assert.assertFalse(entry5.hasConnection());
+        entry5.assignConnection(conn5);
+        Assert.assertTrue(future6.isDone());
+        final PoolEntry<String, HttpConnection> entry6 = future6.get();
+        Assert.assertNotNull(entry6);
+        Assert.assertTrue(entry6.hasConnection());
+        Assert.assertSame(conn4, entry6.getConnection());
+
+        totals = pool.getTotalStats();
+        Assert.assertEquals(0, totals.getAvailable());
+        Assert.assertEquals(2, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+
+        pool.release(entry5, true);
+        pool.release(entry6, true);
+
+        totals = pool.getTotalStats();
+        Assert.assertEquals(2, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+    }
+
+    @Test
+    public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10);
+        pool.setMaxPerRoute("somehost", 2);
+        pool.setMaxTotal(2);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        entry1.assignConnection(conn1);
+        Assert.assertNotNull(entry1);
+        Assert.assertTrue(future2.isDone());
+        final PoolEntry<String, HttpConnection> entry2 = future2.get();
+        Assert.assertNotNull(entry2);
+        entry2.assignConnection(conn2);
+
+        PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(0, totals.getAvailable());
+        Assert.assertEquals(2, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+
+        entry1.updateConnection(0, TimeUnit.MILLISECONDS, "some-stuff");
+        pool.release(entry1, true);
+        entry2.updateConnection(0, TimeUnit.MILLISECONDS, "some-stuff");
+        pool.release(entry2, true);
+
+        final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("somehost", "some-stuff");
+        final Future<PoolEntry<String, HttpConnection>> future4 = pool.lease("somehost", "some-stuff");
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry3 = future3.get();
+        Assert.assertNotNull(entry3);
+        Assert.assertSame(conn2, entry3.getConnection());
+        Assert.assertTrue(future4.isDone());
+        final PoolEntry<String, HttpConnection> entry4 = future4.get();
+        Assert.assertNotNull(entry4);
+        Assert.assertSame(conn1, entry4.getConnection());
+
+        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());
+
+        final Future<PoolEntry<String, HttpConnection>> future5 = pool.lease("somehost", "some-other-stuff");
+
+        Assert.assertTrue(future5.isDone());
+
+        Mockito.verify(conn2).close();
+        Mockito.verify(conn1, Mockito.never()).close();
+
+        totals = pool.getTotalStats();
+        Assert.assertEquals(1, totals.getAvailable());
+        Assert.assertEquals(1, totals.getLeased());
+    }
+
+    @Test
+    public void testCreateNewIfExpired() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        entry1.assignConnection(conn1);
+
+        entry1.updateConnection(1, TimeUnit.MILLISECONDS, null);
+        pool.release(entry1, true);
+
+        Thread.sleep(200L);
+
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
+
+        Assert.assertTrue(future2.isDone());
+
+        Mockito.verify(conn1).close();
+
+        final PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(0, totals.getAvailable());
+        Assert.assertEquals(1, totals.getLeased());
+        Assert.assertEquals(Collections.singleton("somehost"), pool.getRoutes());
+        final PoolStats stats = pool.getStats("somehost");
+        Assert.assertEquals(0, stats.getAvailable());
+        Assert.assertEquals(1, stats.getLeased());
+    }
+
+    @Test
+    public void testCloseExpired() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        entry1.assignConnection(conn1);
+        Assert.assertTrue(future2.isDone());
+        final PoolEntry<String, HttpConnection> entry2 = future2.get();
+        Assert.assertNotNull(entry2);
+        entry2.assignConnection(conn2);
+
+        entry1.updateConnection(1, TimeUnit.MILLISECONDS, null);
+        pool.release(entry1, true);
+
+        Thread.sleep(200);
+
+        entry2.updateConnection(1000, TimeUnit.SECONDS, null);
+        pool.release(entry2, true);
+
+        pool.closeExpired();
+
+        Mockito.verify(conn1).close();
+        Mockito.verify(conn2, Mockito.never()).close();
+
+        final PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(1, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+        final PoolStats stats = pool.getStats("somehost");
+        Assert.assertEquals(1, stats.getAvailable());
+        Assert.assertEquals(0, stats.getLeased());
+        Assert.assertEquals(0, stats.getPending());
+    }
+
+    @Test
+    public void testCloseIdle() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+        final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        entry1.assignConnection(conn1);
+        Assert.assertTrue(future2.isDone());
+        final PoolEntry<String, HttpConnection> entry2 = future2.get();
+        Assert.assertNotNull(entry2);
+        entry2.assignConnection(conn2);
+
+        entry1.updateConnection(0, TimeUnit.MILLISECONDS, null);
+        pool.release(entry1, true);
+
+        Thread.sleep(200L);
+
+        entry2.updateConnection(0, TimeUnit.MILLISECONDS, null);
+        pool.release(entry2, true);
+
+        pool.closeIdle(50, TimeUnit.MILLISECONDS);
+
+        Mockito.verify(conn1).close();
+        Mockito.verify(conn2, Mockito.never()).close();
+
+        PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(1, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+        PoolStats stats = pool.getStats("somehost");
+        Assert.assertEquals(1, stats.getAvailable());
+        Assert.assertEquals(0, stats.getLeased());
+        Assert.assertEquals(0, stats.getPending());
+
+        pool.closeIdle(-1, TimeUnit.MILLISECONDS);
+
+        Mockito.verify(conn2).close();
+
+        totals = pool.getTotalStats();
+        Assert.assertEquals(0, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+        Assert.assertEquals(0, totals.getPending());
+        stats = pool.getStats("somehost");
+        Assert.assertEquals(0, stats.getAvailable());
+        Assert.assertEquals(0, stats.getLeased());
+        Assert.assertEquals(0, stats.getPending());
+    }
+
+    @Test
+    public void testLeaseRequestTimeout() throws Exception {
+        final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
+        final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("somehost", null, 10, TimeUnit.MILLISECONDS, null);
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        entry1.assignConnection(conn1);
+        Assert.assertFalse(future2.isDone());
+        Assert.assertFalse(future3.isDone());
+
+        Thread.sleep(100);
+
+        pool.validatePendingRequests();
+
+        Assert.assertFalse(future2.isDone());
+        Assert.assertTrue(future3.isDone());
+    }
+
+    @Test
+    public void testLeaseRequestCanceled() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1);
+
+        final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
+
+        Assert.assertTrue(future1.isDone());
+        final PoolEntry<String, HttpConnection> entry1 = future1.get();
+        Assert.assertNotNull(entry1);
+        entry1.assignConnection(Mockito.mock(HttpConnection.class));
+
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
+        future2.cancel(true);
+
+        pool.release(entry1, true);
+
+        final PoolStats totals = pool.getTotalStats();
+        Assert.assertEquals(1, totals.getAvailable());
+        Assert.assertEquals(0, totals.getLeased());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testCloseIdleInvalid() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+        pool.closeIdle(50, null);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testGetStatsInvalid() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+        pool.getStats(null);
+    }
+
+    @Test
+    public void testSetMaxInvalid() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+        try {
+            pool.setMaxTotal(-1);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+        try {
+            pool.setMaxPerRoute(null, 1);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+        try {
+            pool.setMaxPerRoute("somehost", -1);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+        try {
+            pool.setDefaultMaxPerRoute(-1);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (final IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testShutdown() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
+        pool.shutdown();
+        try {
+            pool.lease("somehost", null);
+            Assert.fail("IllegalStateException should have been thrown");
+        } catch (final IllegalStateException expected) {
+        }
+        // Ignored if shut down
+        pool.release(new PoolEntry<String, HttpConnection>("somehost"), true);
+    }
+
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message