commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject svn commit: r1102500 [2/3] - in /commons/proper/pool/trunk/src: java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java test/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
Date Thu, 12 May 2011 22:50:13 GMT

Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java?rev=1102500&r1=1102499&r2=1102500&view=diff
==============================================================================
--- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java (original)
+++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java Thu May 12 22:50:13 2011
@@ -18,16 +18,16 @@
 package org.apache.commons.pool2.impl;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.NoSuchElementException;
-import java.util.Set;
 import java.util.TimerTask;
 import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.commons.pool2.BaseKeyedObjectPool;
 import org.apache.commons.pool2.KeyedPoolableObjectFactory;
@@ -195,7 +195,7 @@ import org.apache.commons.pool2.PoolUtil
  * @see GenericObjectPool
  *
  * @param <K> The type of keys maintained by this pool.
- * @param <V> Type of element pooled in this pool.
+ * @param <T> Type of element pooled in this pool.
  *
  * @author Rodney Waldhoff
  * @author Dirk Verbeeck
@@ -203,7 +203,7 @@ import org.apache.commons.pool2.PoolUtil
  * @version $Revision$ $Date$
  * @since Pool 1.0
  */
-public class GenericKeyedObjectPool<K,V> extends BaseKeyedObjectPool<K,V>  {
+public class GenericKeyedObjectPool<K,T> extends BaseKeyedObjectPool<K,T>  {
 
     //--- public constants -------------------------------------------
 
@@ -332,7 +332,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy
      * objects if not <code>null</code>
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory) {
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory) {
         this(factory, DEFAULT_MAX_ACTIVE, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE,
                 DEFAULT_TEST_ON_BORROW, DEFAULT_TEST_ON_RETURN, DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                 DEFAULT_NUM_TESTS_PER_EVICTION_RUN, DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
@@ -344,7 +344,7 @@ public class GenericKeyedObjectPool<K,V>
      * if not <code>null</code>
      * @param config a non-<code>null</code> {@link GenericKeyedObjectPool.Config} describing the configuration
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, GenericKeyedObjectPool.Config config) {
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, GenericKeyedObjectPool.Config config) {
         this(factory, config.maxActive, config.whenExhaustedAction, config.maxWait, config.maxIdle, config.maxTotal,
                 config.minIdle, config.testOnBorrow, config.testOnReturn, config.timeBetweenEvictionRunsMillis,
                 config.numTestsPerEvictionRun, config.minEvictableIdleTimeMillis, config.testWhileIdle, config.lifo);
@@ -356,7 +356,7 @@ public class GenericKeyedObjectPool<K,V>
      * if not <code>null</code>
      * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive) {
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive) {
         this(factory,maxActive, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE,
                 DEFAULT_TEST_ON_BORROW, DEFAULT_TEST_ON_RETURN, DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, 
                 DEFAULT_NUM_TESTS_PER_EVICTION_RUN, DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
@@ -371,7 +371,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
      *  <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait) {
         this(factory, maxActive, whenExhaustedAction, maxWait, DEFAULT_MAX_IDLE, DEFAULT_TEST_ON_BORROW,
                 DEFAULT_TEST_ON_RETURN, DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
@@ -391,7 +391,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
      * method (see {@link #setTestOnReturn})
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait, boolean testOnBorrow, boolean testOnReturn) {
         this(factory, maxActive, whenExhaustedAction, maxWait, DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,
                 DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
@@ -409,7 +409,7 @@ public class GenericKeyedObjectPool<K,V>
      * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
      * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait, int maxIdle) {
         this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, DEFAULT_TEST_ON_BORROW, DEFAULT_TEST_ON_RETURN,
                 DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
@@ -431,7 +431,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
      * method (see {@link #setTestOnReturn})
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
         this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, testOnBorrow, testOnReturn,
                 DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
@@ -462,7 +462,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
      * (see {@link #setTestWhileIdle})
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
             int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
         this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,
@@ -494,7 +494,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
      * (see {@link #setTestWhileIdle})
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait, int maxIdle, int maxTotal, boolean testOnBorrow, boolean testOnReturn,
             long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
             boolean testWhileIdle) {
@@ -529,7 +529,7 @@ public class GenericKeyedObjectPool<K,V>
      * (see {@link #setTestWhileIdle})
      * @since Pool 1.3
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn,
             long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
             boolean testWhileIdle) {
@@ -565,7 +565,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param lifo whether or not the pools behave as LIFO (last in first out) queues (see {@link #setLifo})
      * @since Pool 1.4
      */
-    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,T> factory, int maxActive, WhenExhaustedAction whenExhaustedAction,
             long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn,
             long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
             boolean testWhileIdle, boolean lifo) {
@@ -584,9 +584,6 @@ public class GenericKeyedObjectPool<K,V>
         _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
         _testWhileIdle = testWhileIdle;
 
-        _poolMap = new HashMap<K,ObjectQueue>();
-        _poolList = new CursorableLinkedList<K>();
-
         startEvictor(_timeBetweenEvictionRunsMillis);
     }
 
@@ -602,7 +599,7 @@ public class GenericKeyedObjectPool<K,V>
      * @return the cap on the number of active instances per key.
      * @see #setMaxActive
      */
-    public synchronized int getMaxActive() {
+    public int getMaxActive() {
         return _maxActive;
     }
 
@@ -614,10 +611,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #getMaxActive
      */
     public void setMaxActive(int maxActive) {
-        synchronized(this) {
-            _maxActive = maxActive;
-        }
-        allocate();
+        _maxActive = maxActive;
     }
 
     /**
@@ -626,7 +620,7 @@ public class GenericKeyedObjectPool<K,V>
      * @return the maximum number of instances in circulation at one time.
      * @see #setMaxTotal
      */
-    public synchronized int getMaxTotal() {
+    public int getMaxTotal() {
         return _maxTotal;
     }
 
@@ -643,10 +637,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #getMaxTotal
      */
     public void setMaxTotal(int maxTotal) {
-        synchronized(this) {
-            _maxTotal = maxTotal;
-        }
-        allocate();
+        _maxTotal = maxTotal;
     }
 
     /**
@@ -657,7 +648,7 @@ public class GenericKeyedObjectPool<K,V>
      * @return the action to take when exhausted
      * @see #setWhenExhaustedAction
      */
-    public synchronized WhenExhaustedAction getWhenExhaustedAction() {
+    public WhenExhaustedAction getWhenExhaustedAction() {
         return _whenExhaustedAction;
     }
 
@@ -670,10 +661,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #getWhenExhaustedAction
      */
     public void setWhenExhaustedAction(WhenExhaustedAction whenExhaustedAction) {
-        synchronized(this) {
-            _whenExhaustedAction = whenExhaustedAction;
-        }
-        allocate();
+        _whenExhaustedAction = whenExhaustedAction;
     }
 
 
@@ -692,7 +680,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #setWhenExhaustedAction
      * @see #WHEN_EXHAUSTED_BLOCK
      */
-    public synchronized long getMaxWait() {
+    public long getMaxWait() {
         return _maxWait;
     }
 
@@ -712,10 +700,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #WHEN_EXHAUSTED_BLOCK
      */
     public void setMaxWait(long maxWait) {
-        synchronized(this) {
-            _maxWait = maxWait;
-        }
-        allocate();
+        _maxWait = maxWait;
     }
 
     /**
@@ -724,7 +709,7 @@ public class GenericKeyedObjectPool<K,V>
      * in a given keyed pool.
      * @see #setMaxIdle
      */
-    public synchronized int getMaxIdle() {
+    public int getMaxIdle() {
         return _maxIdle;
     }
 
@@ -743,10 +728,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #DEFAULT_MAX_IDLE
      */
     public void setMaxIdle(int maxIdle) {
-        synchronized(this) {
-            _maxIdle = maxIdle;
-        }
-        allocate();
+        _maxIdle = maxIdle;
     }
 
     /**
@@ -843,7 +825,7 @@ public class GenericKeyedObjectPool<K,V>
      * @return milliseconds to sleep between evictor runs.
      * @see #setTimeBetweenEvictionRunsMillis
      */
-    public synchronized long getTimeBetweenEvictionRunsMillis() {
+    public long getTimeBetweenEvictionRunsMillis() {
         return _timeBetweenEvictionRunsMillis;
     }
 
@@ -856,7 +838,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param timeBetweenEvictionRunsMillis milliseconds to sleep between evictor runs.
      * @see #getTimeBetweenEvictionRunsMillis
      */
-    public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
+    public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
         _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
         startEvictor(_timeBetweenEvictionRunsMillis);
     }
@@ -869,7 +851,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #setNumTestsPerEvictionRun
      * @see #setTimeBetweenEvictionRunsMillis
      */
-    public synchronized int getNumTestsPerEvictionRun() {
+    public int getNumTestsPerEvictionRun() {
         return _numTestsPerEvictionRun;
     }
 
@@ -888,7 +870,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #setNumTestsPerEvictionRun
      * @see #setTimeBetweenEvictionRunsMillis
      */
-    public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
+    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
         _numTestsPerEvictionRun = numTestsPerEvictionRun;
     }
 
@@ -901,7 +883,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #setMinEvictableIdleTimeMillis
      * @see #setTimeBetweenEvictionRunsMillis
      */
-    public synchronized long getMinEvictableIdleTimeMillis() {
+    public long getMinEvictableIdleTimeMillis() {
         return _minEvictableIdleTimeMillis;
     }
 
@@ -917,7 +899,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #getMinEvictableIdleTimeMillis
      * @see #setTimeBetweenEvictionRunsMillis
      */
-    public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
+    public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
         _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
     }
 
@@ -931,7 +913,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #setTestWhileIdle
      * @see #setTimeBetweenEvictionRunsMillis
      */
-    public synchronized boolean getTestWhileIdle() {
+    public boolean getTestWhileIdle() {
         return _testWhileIdle;
     }
 
@@ -945,7 +927,7 @@ public class GenericKeyedObjectPool<K,V>
      * @see #getTestWhileIdle
      * @see #setTimeBetweenEvictionRunsMillis
      */
-    public synchronized void setTestWhileIdle(boolean testWhileIdle) {
+    public void setTestWhileIdle(boolean testWhileIdle) {
         _testWhileIdle = testWhileIdle;
     }
 
@@ -954,7 +936,7 @@ public class GenericKeyedObjectPool<K,V>
      * @param conf the new configuration to use.
      * @see GenericKeyedObjectPool.Config
      */
-    public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {
+    public void setConfig(GenericKeyedObjectPool.Config conf) {
         setMaxIdle(conf.maxIdle);
         setMaxActive(conf.maxActive);
         setMaxTotal(conf.maxTotal);
@@ -979,7 +961,7 @@ public class GenericKeyedObjectPool<K,V>
      * @return <code>true</code> if the pools are configured to act as LIFO queues
      * @since 1.4
      */
-     public synchronized boolean getLifo() {
+     public boolean getLifo() {
          return _lifo;
      }
 
@@ -993,7 +975,7 @@ public class GenericKeyedObjectPool<K,V>
       * @param lifo the new value for the lifo property
       * @since 1.4
       */
-     public synchronized void setLifo(boolean lifo) {
+     public void setLifo(boolean lifo) {
          this._lifo = lifo;
      }
 
@@ -1031,961 +1013,623 @@ public class GenericKeyedObjectPool<K,V>
      * @throws NoSuchElementException if a keyed object instance cannot be returned.
      */
      @Override
-    public V borrowObject(K key) throws Exception {
-        long starttime = System.currentTimeMillis();
-        Latch latch = new Latch(key);
-        WhenExhaustedAction whenExhaustedAction;
-        long maxWait;
-        synchronized (this) {
-            // Get local copy of current config. Can't sync when used later as
-            // it can result in a deadlock. Has the added advantage that config
-            // is consistent for entire method execution
-            whenExhaustedAction = _whenExhaustedAction;
-            maxWait = _maxWait;
-
-            // Add this request to the queue
-            _allocationQueue.add(latch);
-        }
-        // Work the allocation queue, allocating idle instances and
-        // instance creation permits in request arrival order
-        allocate();
-
-        for(;;) {
-            synchronized (this) {
-                assertOpen();
-            }
-            // If no object was allocated
-            if (null == latch.getPair()) {
-                // Check to see if we were allowed to create one
-                if (latch.mayCreate()) {
-                    // allow new object to be created
-                } else {
-                    // the pool is exhausted
-                    switch(whenExhaustedAction) {
-                        case GROW:
-                            // allow new object to be created
-                            synchronized (this) {
-                                // Make sure another thread didn't allocate us an object
-                                // or permit a new object to be created
-                                if (latch.getPair() == null && !latch.mayCreate()) {
-                                    _allocationQueue.remove(latch);
-                                    latch.getPool().incrementInternalProcessingCount();
-                                }
-                            }
-                        break;
-                        case FAIL:
-                            synchronized (this) {
-                                // Make sure allocate hasn't already assigned an object
-                                // in a different thread or permitted a new object to be created
-                                if (latch.getPair() != null || latch.mayCreate()) {
-                                    break;
-                                }
-                                _allocationQueue.remove(latch);
-                            }
-                            throw new NoSuchElementException("Pool exhausted");
-                        case BLOCK:
-                            try {
-                                synchronized (latch) {
-                                    // Before we wait, make sure another thread didn't allocate us an object
-                                    // or permit a new object to be created
-                                    if (latch.getPair() == null && !latch.mayCreate()) {
-                                        if (maxWait <= 0) {
-                                            latch.wait();
-                                        } else {
-                                            // this code may be executed again after a notify then continue cycle
-                                            // so, need to calculate the amount of time to wait
-                                            final long elapsed = (System.currentTimeMillis() - starttime);
-                                            final long waitTime = maxWait - elapsed;
-                                            if (waitTime > 0)
-                                            {
-                                                latch.wait(waitTime);
-                                            }
-                                        }
-                                    } else {
-                                        break;
-                                    }
-                                }
-                            } catch(InterruptedException e) {
-                                boolean doAllocate = false;
-                                synchronized (this) {
-                                    // Need to handle the all three possibilities
-                                    if (latch.getPair() == null && !latch.mayCreate()) {
-                                        // Case 1: latch still in allocation queue
-                                        // Remove latch from the allocation queue
-                                        _allocationQueue.remove(latch);
-                                    } else if (latch.getPair() == null && latch.mayCreate()) {
-                                        // Case 2: latch has been given permission to create
-                                        //         a new object
-                                        latch.getPool().decrementInternalProcessingCount();
-                                        doAllocate = true;
-                                    } else {
-                                        // Case 3: An object has been allocated
-                                        latch.getPool().decrementInternalProcessingCount();
-                                        latch.getPool().incrementActiveCount();
-                                        returnObject(latch.getkey(), latch.getPair().getValue());
-                                    }
-                                }
-                                if (doAllocate) {
-                                    allocate();
-                                }
-                                Thread.currentThread().interrupt();
-                                throw e;
-                            }
-                            if (maxWait > 0 && ((System.currentTimeMillis() - starttime) >= maxWait)) {
-                                synchronized (this) {
-                                    // Make sure allocate hasn't already assigned an object
-                                    // in a different thread or permitted a new object to be created
-                                    if (latch.getPair() == null && !latch.mayCreate()) {
-                                        _allocationQueue.remove(latch);
-                                    } else {
-                                        break;
-                                    }
-                                }
-                                throw new NoSuchElementException("Timeout waiting for idle object");
-                            }
-                            continue; // keep looping
-                        default:
-                            throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction +
-                                    " not recognized.");
-                    }
-                }
-            }
+    public T borrowObject(K key) throws Exception {
 
-            boolean newlyCreated = false;
-            if (null == latch.getPair()) {
-                try {
-                    V obj = _factory.makeObject(key);
-                    latch.setPair(new ObjectTimestampPair<V>(obj));
-                    newlyCreated = true;
-                } finally {
-                    if (!newlyCreated) {
-                        // object cannot be created
-                        synchronized (this) {
-                            latch.getPool().decrementInternalProcessingCount();
-                            // No need to reset latch - about to throw exception
-                        }
-                        allocate();
-                    }
-                }
-            }
+        assertOpen();
 
-            // activate & validate the object
-            try {
-                _factory.activateObject(key, latch.getPair().getValue());
-                if (_testOnBorrow && !_factory.validateObject(key, latch.getPair().getValue())) {
-                    throw new Exception("ValidateObject failed");
-                }
-                synchronized (this) {
-                    latch.getPool().decrementInternalProcessingCount();
-                    latch.getPool().incrementActiveCount();
-                }
-                return latch.getPair().getValue();
-            } catch (Throwable e) {
-                PoolUtils.checkRethrow(e);
-                // object cannot be activated or is invalid
-                try {
-                    _factory.destroyObject(key, latch.getPair().getValue());
-                } catch (Throwable e2) {
-                    PoolUtils.checkRethrow(e2);
-                    // cannot destroy broken object
-                }
-                synchronized (this) {
-                    latch.getPool().decrementInternalProcessingCount();
-                    if (!newlyCreated) {
-                        latch.reset();
-                        _allocationQueue.add(0, latch);
-                    }
-                }
-                allocate();
-                if (newlyCreated) {
-                    throw new NoSuchElementException(
-                       "Could not create a validated object, cause: " +
-                            e.getMessage());
-                }
-                continue; // keep looping
-            }
-        }
-    }
+        PooledObject<T> p = null;
 
-    /**
-     * Allocate available instances to latches in the allocation queue.  Then
-     * set _mayCreate to true for as many additional latches remaining in queue
-     * as _maxActive allows for each key. This method <b>MUST NOT</b> be called
-     * from inside a sync block.
-     */
-    private void allocate() {
-        boolean clearOldest = false;
-
-        synchronized (this) {
-            if (isClosed()) return;
-
-            Iterator<Latch> allocationQueueIter = _allocationQueue.iterator();
-
-            while (allocationQueueIter.hasNext()) {
-                // First use any objects in the pool to clear the queue
-                Latch latch = allocationQueueIter.next();
-                ObjectQueue pool = _poolMap.get(latch.getkey());
-                if (null == pool) {
-                    pool = new ObjectQueue();
-                    _poolMap.put(latch.getkey(), pool);
-                    _poolList.add(latch.getkey());
-                }
-                latch.setPool(pool);
-                if (!pool.queue.isEmpty()) {
-                    allocationQueueIter.remove();
-                    latch.setPair(pool.queue.removeFirst());
-                    pool.incrementInternalProcessingCount();
-                    _totalIdle--;
-                    synchronized (latch) {
-                        latch.notify();
+        // Get local copy of current config so it is consistent for entire
+        // method execution
+        WhenExhaustedAction whenExhaustedAction = _whenExhaustedAction;
+        long maxWait = _maxWait;
+
+        boolean create;
+        ObjectDeque<T> objectDeque = poolMap.get(key);
+        
+        while (p == null) {
+            create = false;
+            if (whenExhaustedAction == WhenExhaustedAction.FAIL) {
+                if (objectDeque != null) {
+                    p = objectDeque.getIdleObjects().pollFirst();
+                }
+                if (p == null) {
+                    create = true;
+                    p = create(key, false);
+                }
+                if (p == null) {
+                    throw new NoSuchElementException("Pool exhausted");
+                }
+                if (!p.allocate()) {
+                    p = null;
+                }
+            } else if (whenExhaustedAction == WhenExhaustedAction.BLOCK) {
+                if (objectDeque != null) {
+                    p = objectDeque.getIdleObjects().pollFirst();
+                }
+                if (p == null) {
+                    create = true;
+                    p = create(key, false);
+                }
+                if (p == null && objectDeque != null) {
+                    if (maxWait < 1) {
+                        p = objectDeque.getIdleObjects().takeFirst();
+                    } else {
+                        p = objectDeque.getIdleObjects().pollFirst(maxWait,
+                                TimeUnit.MILLISECONDS);
                     }
-                    // Next item in queue
-                    continue;
                 }
-
-                // If there is a totalMaxActive and we are at the limit then
-                // we have to make room
-                if ((_maxTotal > 0) &&
-                        (_totalActive + _totalIdle + _totalInternalProcessing >= _maxTotal)) {
-                    clearOldest = true;
-                    break;
+                if (p == null) {
+                    throw new NoSuchElementException(
+                            "Timeout waiting for idle object");
                 }
-
-                // Second utilise any spare capacity to create new objects
-                if ((_maxActive < 0 || pool.activeCount + pool.internalProcessingCount < _maxActive) &&
-                        (_maxTotal < 0 || _totalActive + _totalIdle + _totalInternalProcessing < _maxTotal)) {
-                    // allow new object to be created
-                    allocationQueueIter.remove();
-                    latch.setMayCreate(true);
-                    pool.incrementInternalProcessingCount();
-                    synchronized (latch) {
-                        latch.notify();
-                    }
-                    // Next item in queue
-                    continue;
+                if (!p.allocate()) {
+                    p = null;
                 }
-
-                // If there is no per-key limit and we reach this point we
-                // must have allocated all the objects we possibly can and there
-                // is no point looking at the rest of the allocation queue
-                if (_maxActive < 0) {
-                    break;
+            } else if (whenExhaustedAction == WhenExhaustedAction.GROW) {
+                if (objectDeque != null) {
+                    p = objectDeque.getIdleObjects().pollFirst();
                 }
-            }
-        }
-        
-        if (clearOldest) {
-            /* Clear oldest calls factory methods so it must be called from
-             * outside the sync block.
-             * It also needs to be outside the sync block as it calls
-             * allocate(). If called inside the sync block, the call to
-             * allocate() would be able to enter the sync block (since the
-             * thread already has the lock) which may have unexpected,
-             * unpleasant results.
-             */
-            clearOldest();
-        }
-    }
-    
-    /**
-     * Clears any objects sitting idle in the pool by removing them from the
-     * idle instance pool and then invoking the configured PoolableObjectFactory's
-     * {@link KeyedPoolableObjectFactory#destroyObject(Object, Object)} method on
-     * each idle instance.
-     *  
-     * <p> Implementation notes:
-     * <ul><li>This method does not destroy or effect in any way instances that are
-     * checked out when it is invoked.</li>
-     * <li>Invoking this method does not prevent objects being
-     * returned to the idle instance pool, even during its execution. It locks
-     * the pool only during instance removal. Additional instances may be returned
-     * while removed items are being destroyed.</li>
-     * <li>Exceptions encountered destroying idle instances are swallowed.</li></ul></p>
-     */
-    @Override
-    public void clear() {
-        Map<K,List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K,List<ObjectTimestampPair<V>>>();
-        synchronized (this) {
-            for (Iterator<K> it = _poolMap.keySet().iterator(); it.hasNext();) {
-                K key = it.next();
-                ObjectQueue pool = _poolMap.get(key);
-                // Copy objects to new list so pool.queue can be cleared inside
-                // the sync
-                List<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>();
-                objects.addAll(pool.queue);
-                toDestroy.put(key, objects);
-                it.remove();
-                _poolList.remove(key);
-                _totalIdle = _totalIdle - pool.queue.size();
-                _totalInternalProcessing =
-                    _totalInternalProcessing + pool.queue.size();
-                pool.queue.clear();
-            }
-        }
-        destroy(toDestroy, _factory);
-    }
-
-    /**
-     * Clears oldest 15% of objects in pool.  The method sorts the
-     * objects into a TreeMap and then iterates the first 15% for removal.
-     * 
-     * @since Pool 1.3
-     */
-    public void clearOldest() {
-        // Map of objects to destroy my key
-        final Map<K, List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K, List<ObjectTimestampPair<V>>>();
-
-        // build sorted map of idle objects
-        final Map<ObjectTimestampPair<V>, K> map = new TreeMap<ObjectTimestampPair<V>, K>();
-        synchronized (this) {
-            for (Iterator<K> keyiter = _poolMap.keySet().iterator(); keyiter.hasNext();) {
-                final K key = keyiter.next();
-                final CursorableLinkedList<ObjectTimestampPair<V>> list = _poolMap.get(key).queue;
-                for (Iterator<ObjectTimestampPair<V>> it = list.iterator(); it.hasNext();) {
-                    // each item into the map uses the objectimestamppair object
-                    // as the key.  It then gets sorted based on the timstamp field
-                    // each value in the map is the parent list it belongs in.
-                    map.put(it.next(), key);
+                if (p == null) {
+                    create = true;
+                    p = create(key, true);
                 }
-            }
-
-            // Now iterate created map and kill the first 15% plus one to account for zero
-            Set<Map.Entry<ObjectTimestampPair<V>, K>> setPairKeys = map.entrySet();
-            int itemsToRemove = ((int) (map.size() * 0.15)) + 1;
-
-            Iterator<Map.Entry<ObjectTimestampPair<V>, K>> iter = setPairKeys.iterator();
-            while (iter.hasNext() && itemsToRemove > 0) {
-                Map.Entry<ObjectTimestampPair<V>, K> entry = iter.next();
-                // kind of backwards on naming.  In the map, each key is the objecttimestamppair
-                // because it has the ordering with the timestamp value.  Each value that the
-                // key references is the key of the list it belongs to.
-                K key = entry.getValue();
-                ObjectTimestampPair<V> pairTimeStamp = entry.getKey();
-                ObjectQueue objectQueue = _poolMap.get(key);
-                final CursorableLinkedList<ObjectTimestampPair<V>> list = objectQueue.queue;
-                list.remove(pairTimeStamp);
-
-                if (toDestroy.containsKey(key)) {
-                    toDestroy.get(key).add(pairTimeStamp);
-                } else {
-                    List<ObjectTimestampPair<V>> listForKey = new ArrayList<ObjectTimestampPair<V>>();
-                    listForKey.add(pairTimeStamp);
-                    toDestroy.put(key, listForKey);
+                if (!p.allocate()) {
+                    p = null;
                 }
-                objectQueue.incrementInternalProcessingCount();
-                _totalIdle--;
-                itemsToRemove--;
-            }
-
-        }
-        destroy(toDestroy, _factory);
-    }
-
-    /**
-     * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
-     *
-     * @param key the key to clear
-     */
-    @Override
-    public void clear(K key) {
-        Map<K, List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K, List<ObjectTimestampPair<V>>>();
-
-        final ObjectQueue pool;
-        synchronized (this) {
-            pool = _poolMap.remove(key);
-            if (pool == null) {
-                return;
             }
-            _poolList.remove(key);
-            // Copy objects to new list so pool.queue can be cleared inside
-            // the sync
-            List<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>();
-            objects.addAll(pool.queue);
-            toDestroy.put(key, objects);
-            _totalIdle = _totalIdle - pool.queue.size();
-            _totalInternalProcessing =
-                _totalInternalProcessing + pool.queue.size();
-            pool.queue.clear();
-        }
-        destroy(toDestroy, _factory);
-    }
 
-    /**
-     * Assuming Map<Object,Collection<ObjectTimestampPair>>, destroy all
-     * ObjectTimestampPair.value using the supplied factory.
-     * 
-     * @param m Map containing keyed pools to clear
-     * @param factory KeyedPoolableObjectFactory used to destroy the objects
-     */
-    private void destroy(Map<K,List<ObjectTimestampPair<V>>> m, KeyedPoolableObjectFactory<K,V> factory) {
-        for (Iterator<Map.Entry<K,List<ObjectTimestampPair<V>>>> entries = m.entrySet().iterator(); entries.hasNext();) {
-            Map.Entry<K,List<ObjectTimestampPair<V>>> entry = entries.next();
-            K key = entry.getKey();
-            Collection<ObjectTimestampPair<V>> c = entry.getValue();
-            for (Iterator<ObjectTimestampPair<V>> it = c.iterator(); it.hasNext();) {
+            if (p != null) {
                 try {
-                    factory.destroyObject(key, it.next().getValue());
+                    _factory.activateObject(key, p.getObject());
                 } catch (Exception e) {
-                    // ignore error, keep destroying the rest
-                } finally {
-                    synchronized(this) {
-                        ObjectQueue objectQueue = _poolMap.get(key);
-                        if (objectQueue != null) {
-                            objectQueue.decrementInternalProcessingCount();
-                            if (objectQueue.internalProcessingCount == 0 &&
-                                    objectQueue.activeCount == 0 &&
-                                    objectQueue.queue.isEmpty()) {
-                                _poolMap.remove(key);
-                                _poolList.remove(key);
-                            }
+                    try {
+                        destroy(key, p);
+                    } catch (Exception e1) {
+                        // Ignore - activation failure is more important
+                    }
+                    p = null;
+                    if (create) {
+                        NoSuchElementException nsee = new NoSuchElementException(
+                                "Unable to activate object");
+                        nsee.initCause(e);
+                        throw nsee;
+                    }
+                }
+                if (p != null && getTestOnBorrow()) {
+                    boolean validate = false;
+                    Throwable validationThrowable = null;
+                    try {
+                        validate = _factory.validateObject(key, p.getObject());
+                    } catch (Throwable t) {
+                        PoolUtils.checkRethrow(t);
+                    }
+                    if (!validate) {
+                        try {
+                            destroy(key, p);
+                        } catch (Exception e) {
+                            // Ignore - validation failure is more important
+                        }
+                        p = null;
+                        if (create) {
+                            NoSuchElementException nsee = new NoSuchElementException(
+                                    "Unable to validate object");
+                            nsee.initCause(validationThrowable);
+                            throw nsee;
                         }
                     }
-                    allocate();
                 }
             }
-
         }
-    }
 
-    /**
-     * Returns the total number of instances current borrowed from this pool but not yet returned.
-     *
-     * @return the total number of instances currently borrowed from this pool
-     */
-    @Override
-    public synchronized int getNumActive() {
-        return _totalActive;
+        return p.getObject();
     }
 
-    /**
-     * Returns the total number of instances currently idle in this pool.
-     *
-     * @return the total number of instances currently idle in this pool
-     */
-    @Override
-    public synchronized int getNumIdle() {
-        return _totalIdle;
-    }
 
-    /**
-     * Returns the number of instances currently borrowed from but not yet returned
-     * to the pool corresponding to the given <code>key</code>.
-     *
-     * @param key the key to query
-     * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
-     */
-    @Override
-    public synchronized int getNumActive(K key) {
-        final ObjectQueue pool = (_poolMap.get(key));
-        return pool != null ? pool.activeCount : 0;
-    }
+     /**
+      * <p>Returns an object to a keyed pool.</p>
+      * 
+      * <p>For the pool to function correctly, the object instance <strong>must</strong> have been borrowed
+      * from the pool (under the same key) and not yet returned. Repeated <code>returnObject</code> calls on
+      * the same object/key pair (with no <code>borrowObject</code> calls in between) will result in multiple
+      * references to the object in the idle instance pool.</p>
+      * 
+      * <p>If {@link #getMaxIdle() maxIdle} is set to a positive value and the number of idle instances under the given
+      * key has reached this value, the returning instance is destroyed.</p>
+      * 
+      * <p>If {@link #getTestOnReturn() testOnReturn} == true, the returning instance is validated before being returned
+      * to the idle instance pool under the given key.  In this case, if validation fails, the instance is destroyed.</p>
+      * 
+      * @param key pool key
+      * @param obj instance to return to the keyed pool
+      * @throws Exception
+      */
+     @Override
+     public void returnObject(K key, T t) throws Exception {
+         
+         ObjectDeque<T> objectDeque = poolMap.get(key);
+         
+         PooledObject<T> p = objectDeque.getAllObjects().get(t);
+         
+         if (p == null) {
+             throw new IllegalStateException(
+                     "Returned object not currently part of this pool");
+         }
+
+         if (getTestOnReturn()) {
+             if (!_factory.validateObject(key, t)) {
+                 try {
+                     destroy(key, p);
+                 } catch (Exception e) {
+                     // TODO - Ignore?
+                 }
+                 return;
+             }
+         }
+
+         try {
+             _factory.passivateObject(key, t);
+         } catch (Exception e1) {
+             try {
+                 destroy(key, p);
+             } catch (Exception e) {
+                 // TODO - Ignore?
+             }
+             return;
+         }
+
+         if (!p.deallocate()) {
+             // TODO - Should not happen;
+         }
+
+         int maxIdle = getMaxIdle();
+         LinkedBlockingDeque<PooledObject<T>> idleObjects =
+             objectDeque.getIdleObjects();
+
+         if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) {
+             try {
+                 destroy(key, p);
+             } catch (Exception e) {
+                 // TODO - Ignore?
+             }
+         } else {
+             if (getLifo()) {
+                 idleObjects.addFirst(p);
+             } else {
+                 idleObjects.addLast(p);
+             }
+         }
+     }
 
-    /**
-     * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
-     *
-     * @param key the key to query
-     * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
-     */
-    @Override
-    public synchronized int getNumIdle(K key) {
-        final ObjectQueue pool = (_poolMap.get(key));
-        return pool != null ? pool.queue.size() : 0;
-    }
 
-    /**
-     * <p>Returns an object to a keyed pool.</p>
-     * 
-     * <p>For the pool to function correctly, the object instance <strong>must</strong> have been borrowed
-     * from the pool (under the same key) and not yet returned. Repeated <code>returnObject</code> calls on
-     * the same object/key pair (with no <code>borrowObject</code> calls in between) will result in multiple
-     * references to the object in the idle instance pool.</p>
-     * 
-     * <p>If {@link #getMaxIdle() maxIdle} is set to a positive value and the number of idle instances under the given
-     * key has reached this value, the returning instance is destroyed.</p>
-     * 
-     * <p>If {@link #getTestOnReturn() testOnReturn} == true, the returning instance is validated before being returned
-     * to the idle instance pool under the given key.  In this case, if validation fails, the instance is destroyed.</p>
-     * 
-     * @param key pool key
-     * @param obj instance to return to the keyed pool
-     * @throws Exception
-     */
-    @Override
-    public void returnObject(K key, V obj) throws Exception {
-        try {
-            addObjectToPool(key, obj, true);
-        } catch (Exception e) {
-            if (_factory != null) {
-                try {
-                    _factory.destroyObject(key, obj);
-                } catch (Exception e2) {
-                    // swallowed
-                }
-                // TODO: Correctness here depends on control in addObjectToPool.
-                // These two methods should be refactored, removing the
-                // "behavior flag", decrementNumActive, from addObjectToPool.
-                ObjectQueue pool = _poolMap.get(key);
-                if (pool != null) {
-                    synchronized(this) {
-                        pool.decrementActiveCount();
-                        if (pool.queue.isEmpty() &&
-                                pool.activeCount == 0 &&
-                                pool.internalProcessingCount == 0) {
-                            _poolMap.remove(key);
-                            _poolList.remove(key);
-                        }
-                    }
-                    allocate();
-                }
-            }
-        }
-    }
+     /**
+      * {@inheritDoc}
+      * <p>Activation of this method decrements the active count associated with the given keyed pool 
+      * and attempts to destroy <code>obj.</code></p>
+      * 
+      * @param key pool key
+      * @param obj instance to invalidate
+      * @throws Exception if an exception occurs destroying the object
+      */
+     @Override
+     public void invalidateObject(K key, T obj) throws Exception {
+         
+         ObjectDeque<T> objectDeque = poolMap.get(key);
+         
+         PooledObject<T> p = objectDeque.getAllObjects().get(obj);
+         if (p == null) {
+             throw new IllegalStateException(
+                     "Object not currently part of this pool");
+         }
+         destroy(key, p);
+     }
 
-    /**
-     * <p>Adds an object to the keyed pool.</p>
-     * 
-     * <p>Validates the object if testOnReturn == true and passivates it before returning it to the pool.
-     * if validation or passivation fails, or maxIdle is set and there is no room in the pool, the instance
-     * is destroyed.</p>
-     * 
-     * <p>Calls {@link #allocate()} on successful completion</p>
-     * 
-     * @param key pool key
-     * @param obj instance to add to the keyed pool
-     * @param decrementNumActive whether or not to decrement the active count associated with the keyed pool
-     * @throws Exception
-     */
-    private void addObjectToPool(K key, V obj,
-            boolean decrementNumActive) throws Exception {
 
-        // if we need to validate this object, do so
-        boolean success = true; // whether or not this object passed validation
-        if (_testOnReturn && !_factory.validateObject(key, obj)) {
-            success = false;
-        } else {
-            _factory.passivateObject(key, obj);
-        }
-
-        boolean shouldDestroy = !success;
-        ObjectQueue pool;
-
-        // Add instance to pool if there is room and it has passed validation
-        // (if testOnreturn is set)
-        boolean doAllocate = false;
-        synchronized (this) {
-            // grab the pool (list) of objects associated with the given key
-            pool = _poolMap.get(key);
-            // if it doesn't exist, create it
-            if (null == pool) {
-                pool = new ObjectQueue();
-                _poolMap.put(key, pool);
-                _poolList.add(key);
-            }
-            if (isClosed()) {
-                shouldDestroy = true;
-            } else {
-                // if there's no space in the pool, flag the object for destruction
-                // else if we passivated successfully, return it to the pool
-                if (_maxIdle >= 0 && (pool.queue.size() >= _maxIdle)) {
-                    shouldDestroy = true;
-                } else if (success) {
-                    // borrowObject always takes the first element from the queue,
-                    // so for LIFO, push on top, FIFO add to end
-                    if (_lifo) {
-                        pool.queue.addFirst(new ObjectTimestampPair<V>(obj));
-                    } else {
-                        pool.queue.addLast(new ObjectTimestampPair<V>(obj));
-                    }
-                    _totalIdle++;
-                    if (decrementNumActive) {
-                        pool.decrementActiveCount();
-                    }
-                    doAllocate = true;
-                }
-            }
-        }
-        if (doAllocate) {
-            allocate();
-        }
+     /**
+      * Clears any objects sitting idle in the pool by removing them from the
+      * idle instance pool and then invoking the configured PoolableObjectFactory's
+      * {@link KeyedPoolableObjectFactory#destroyObject(Object, Object)} method on
+      * each idle instance.
+      *  
+      * <p> Implementation notes:
+      * <ul><li>This method does not destroy or effect in any way instances that are
+      * checked out when it is invoked.</li>
+      * <li>Invoking this method does not prevent objects being
+      * returned to the idle instance pool, even during its execution. It locks
+      * the pool only during instance removal. Additional instances may be returned
+      * while removed items are being destroyed.</li>
+      * <li>Exceptions encountered destroying idle instances are swallowed.</li></ul></p>
+      */
+     @Override
+     public void clear() {
+         Iterator<K> iter = poolMap.keySet().iterator();
+         
+         while (iter.hasNext()) {
+             clear(iter.next());
+         }
+     }
 
-        // Destroy the instance if necessary
-        if (shouldDestroy) {
-            try {
-                _factory.destroyObject(key, obj);
-            } catch(Exception e) {
-                // ignored?
-            }
-            // Decrement active count *after* destroy if applicable
-            if (decrementNumActive) {
-                synchronized(this) {
-                    pool.decrementActiveCount();
-                    if (pool.queue.isEmpty() &&
-                            pool.activeCount == 0 &&
-                            pool.internalProcessingCount == 0) {
-                        _poolMap.remove(key);
-                        _poolList.remove(key);
-                    }
-                }
-                allocate();
-            }
-        }
-    }
 
-    /**
-     * {@inheritDoc}
-     * <p>Activation of this method decrements the active count associated with the given keyed pool 
-     * and attempts to destroy <code>obj.</code></p>
-     * 
-     * @param key pool key
-     * @param obj instance to invalidate
-     * @throws Exception if an exception occurs destroying the object
-     */
-    @Override
-    public void invalidateObject(K key, V obj) throws Exception {
-        try {
-            _factory.destroyObject(key, obj);
-        } finally {
-            synchronized (this) {
-                ObjectQueue pool = _poolMap.get(key);
-                if (null == pool) {
-                    pool = new ObjectQueue();
-                    _poolMap.put(key, pool);
-                    _poolList.add(key);
-                }
-                pool.decrementActiveCount();
-            }
-            allocate(); // _totalActive has changed
-        }
-    }
+     /**
+      * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
+      *
+      * @param key the key to clear
+      */
+     @Override
+     public void clear(K key) {
+         
+         ObjectDeque<T> objectDeque = poolMap.get(key);
+         if (objectDeque == null) {
+             return;
+         }
+         LinkedBlockingDeque<PooledObject<T>> idleObjects =
+                 objectDeque.getIdleObjects();
+         
+         PooledObject<T> p = idleObjects.poll();
+
+         while (p != null) {
+             try {
+                 destroy(key, p);
+             } catch (Exception e) {
+                 // TODO - Ignore?
+             }
+             p = idleObjects.poll();
+         }
+     }
 
-    /**
-     * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
-     * passivate it, and then place it in the idle object pool.
-     * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
-     *
-     * @param key the key a new instance should be added to
-     * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
-     * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been
-     * called on this pool.
-     */
-    @Override
-    public void addObject(K key) throws Exception {
-        assertOpen();
-        if (_factory == null) {
-            throw new IllegalStateException("Cannot add objects without a factory.");
-        }
-        V obj = _factory.makeObject(key);
-        try {
-            assertOpen();
-            addObjectToPool(key, obj, false);
-        } catch (IllegalStateException ex) { // Pool closed
-            try {
-                _factory.destroyObject(key, obj);
-            } catch (Exception ex2) {
-                // swallow
-            }
-            throw ex;
-        }
-    }
 
-    /**
-     * Registers a key for pool control.
-     *
-     * If <code>populateImmediately</code> is <code>true</code> and
-     * <code>minIdle > 0,</code> the pool under the given key will be
-     * populated immediately with <code>minIdle</code> idle instances.
-     *
-     * @param key - The key to register for pool control.
-     * @param populateImmediately - If this is <code>true</code>, the pool
-     * will be populated immediately.
-     * @since Pool 1.3
-     */
-    public synchronized void preparePool(K key, boolean populateImmediately) {
-        ObjectQueue pool = (_poolMap.get(key));
-        if (null == pool) {
-            pool = new ObjectQueue();
-            _poolMap.put(key,pool);
-            _poolList.add(key);
-        }
+     /**
+      * Returns the total number of instances current borrowed from this pool but not yet returned.
+      *
+      * @return the total number of instances currently borrowed from this pool
+      */
+     @Override
+     public int getNumActive() {
+         return numTotal.get() - getNumIdle();
+     }
 
-        if (populateImmediately) {
-            try {
-                // Create the pooled objects
-                ensureMinIdle(key);
-            }
-            catch (Exception e) {
-                //Do nothing
-            }
-        }
-    }
+     /**
+      * Returns the total number of instances currently idle in this pool.
+      *
+      * @return the total number of instances currently idle in this pool
+      */
+     @Override
+     public int getNumIdle() {
+         Iterator<ObjectDeque<T>> iter = poolMap.values().iterator();
+         int result = 0;
+         
+         while (iter.hasNext()) {
+             result += iter.next().getIdleObjects().size();
+         }
 
-    /**
-     * <p>Closes the keyed object pool.  Once the pool is closed, {@link #borrowObject(Object)}
-     * will fail with IllegalStateException, but {@link #returnObject(Object, Object)} and
-     * {@link #invalidateObject(Object, Object)} will continue to work, with returned objects
-     * destroyed on return.</p>
-     * 
-     * <p>Destroys idle instances in the pool by invoking {@link #clear()}.</p> 
-     * 
-     * @throws Exception
-     */
-    @Override
-    public void close() throws Exception {
-        super.close();
-        synchronized (this) {
-            clear();
-            if (null != _evictionCursor) {
-                _evictionCursor.close();
-                _evictionCursor = null;
-            }
-            if (null != _evictionKeyCursor) {
-                _evictionKeyCursor.close();
-                _evictionKeyCursor = null;
-            }
-            startEvictor(-1L);
-        }
-    }
+         return result;
+     }
 
-    /**
-     * <p>Sets the keyed poolable object factory associated with this pool.</p>
-     * 
-     * <p>If this method is called when objects are checked out of any of the keyed pools,
-     * an IllegalStateException is thrown.  Calling this method also has the side effect of
-     * destroying any idle instances in existing keyed pools, using the original factory.</p>
-     * 
-     * @param factory KeyedPoolableObjectFactory to use when creating keyed object pool instances
-     * @throws IllegalStateException if there are active (checked out) instances associated with this keyed object pool
-     * @deprecated to be removed in version 2.0
-     */
-    @Override
-    @Deprecated
-    public void setFactory(KeyedPoolableObjectFactory<K,V> factory) throws IllegalStateException {
-        Map<K,List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K, List<ObjectTimestampPair<V>>>();
-        final KeyedPoolableObjectFactory<K,V> oldFactory = _factory;
-        synchronized (this) {
-            assertOpen();
-            if (0 < getNumActive()) {
-                throw new IllegalStateException("Objects are already active");
-            }
-            for (Iterator<K> it = _poolMap.keySet().iterator(); it.hasNext();) {
-                K key = it.next();
-                ObjectQueue pool = _poolMap.get(key);
-                if (pool != null) {
-                    // Copy objects to new list so pool.queue can be cleared
-                    // inside the sync
-                    List<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>();
-                    objects.addAll(pool.queue);
-                    toDestroy.put(key, objects);
-                    it.remove();
-                    _poolList.remove(key);
-                    _totalIdle = _totalIdle - pool.queue.size();
-                    _totalInternalProcessing =
-                        _totalInternalProcessing + pool.queue.size();
-                    pool.queue.clear();
-                }
-            }
-            _factory = factory;
-        }
-        destroy(toDestroy, oldFactory);
-    }
+     /**
+      * Returns the number of instances currently borrowed from but not yet returned
+      * to the pool corresponding to the given <code>key</code>.
+      *
+      * @param key the key to query
+      * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
+      */
+     @Override
+     public int getNumActive(K key) {
+         final ObjectDeque<T> objectDeque = poolMap.get(key);
+         if (objectDeque != null) {
+             return objectDeque.getNumActive().get() -
+                     objectDeque.getIdleObjects().size();
+         } else {
+             return 0;
+         }
+     }
 
-    /**
-     * <p>Perform <code>numTests</code> idle object eviction tests, evicting
-     * examined objects that meet the criteria for eviction. If
-     * <code>testWhileIdle</code> is true, examined objects are validated
-     * when visited (and removed if invalid); otherwise only objects that
-     * have been idle for more than <code>minEvicableIdletimeMillis</code>
-     * are removed.</p>
-     *
-     * <p>Successive activations of this method examine objects in keyed pools
-     * in sequence, cycling through the keys and examining objects in
-     * oldest-to-youngest order within the keyed pools.</p>
-     *
-     * @throws Exception when there is a problem evicting idle objects.
-     */
-    public void evict() throws Exception {
-        K key = null;
-        boolean testWhileIdle;
-        long minEvictableIdleTimeMillis;
+     /**
+      * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
+      *
+      * @param key the key to query
+      * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
+      */
+     @Override
+     public synchronized int getNumIdle(K key) {
+         final ObjectDeque<T> objectDeque = poolMap.get(key);
+         return objectDeque != null ? objectDeque.getIdleObjects().size() : 0;
+     }
 
-        synchronized (this) {
-            // Get local copy of current config. Can't sync when used later as
-            // it can result in a deadlock. Has the added advantage that config
-            // is consistent for entire method execution
-            testWhileIdle = _testWhileIdle;
-            minEvictableIdleTimeMillis = _minEvictableIdleTimeMillis;
 
-            // Initialize key to last key value
-            if (_evictionKeyCursor != null &&
-                    _evictionKeyCursor._lastReturned != null) {
-                key = _evictionKeyCursor._lastReturned.value();
-            }
-        }
+     /**
+      * <p>Closes the keyed object pool.  Once the pool is closed, {@link #borrowObject(Object)}
+      * will fail with IllegalStateException, but {@link #returnObject(Object, Object)} and
+      * {@link #invalidateObject(Object, Object)} will continue to work, with returned objects
+      * destroyed on return.</p>
+      * 
+      * <p>Destroys idle instances in the pool by invoking {@link #clear()}.</p> 
+      * 
+      * @throws Exception
+      */
+     @Override
+     public void close() throws Exception {
+         super.close();
+         clear();
+         evictionIterator = null;
+         evictionKeyIterator = null;
+         startEvictor(-1L);
+     }
 
-        for (int i=0, m=getNumTests(); i<m; i++) {
-            final ObjectTimestampPair<V> pair;
-            synchronized (this) {
-                // make sure pool map is not empty; otherwise do nothing
-                if (_poolMap == null || _poolMap.size() == 0) {
-                    continue;
-                }
 
-                // if we don't have a key cursor, then create one
-                if (null == _evictionKeyCursor) {
-                    resetEvictionKeyCursor();
-                    key = null;
-                }
+     /**
+      * <p>Sets the keyed poolable object factory associated with this pool.</p>
+      * 
+      * <p>If this method is called when objects are checked out of any of the keyed pools,
+      * an IllegalStateException is thrown.  Calling this method also has the side effect of
+      * destroying any idle instances in existing keyed pools, using the original factory.</p>
+      * 
+      * @param factory KeyedPoolableObjectFactory to use when creating keyed object pool instances
+      * @throws IllegalStateException if there are active (checked out) instances associated with this keyed object pool
+      * @deprecated to be removed in version 2.0
+      */
+     @Override
+     @Deprecated
+     public void setFactory(KeyedPoolableObjectFactory<K,T> factory) throws IllegalStateException {
+         assertOpen();
+         if (0 < getNumActive()) {
+             throw new IllegalStateException("Objects are already active");
+         }
+         clear();
+         _factory = factory;
+     }
 
-                // if we don't have an object cursor, create one
-                if (null == _evictionCursor) {
-                    // if the _evictionKeyCursor has a next value, use this key
-                    if (_evictionKeyCursor.hasNext()) {
-                        key = _evictionKeyCursor.next();
-                        resetEvictionObjectCursor(key);
-                    } else {
-                        // Reset the key cursor and try again
-                        resetEvictionKeyCursor();
-                        if (_evictionKeyCursor != null) {
-                            if (_evictionKeyCursor.hasNext()) {
-                                key = _evictionKeyCursor.next();
-                                resetEvictionObjectCursor(key);
-                            }
-                        }
-                    }
-                }
+     
+     /**
+      * Clears oldest 15% of objects in pool.  The method sorts the
+      * objects into a TreeMap and then iterates the first 15% for removal.
+      * 
+      * @since Pool 1.3
+      */
+     public void clearOldest() {
 
-                if (_evictionCursor == null) {
-                    continue; // should never happen; do nothing
-                }
+         // build sorted map of idle objects
+         final Map<PooledObject<T>, K> map = new TreeMap<PooledObject<T>, K>();
 
-                // If eviction cursor is exhausted, try to move
-                // to the next key and reset
-                if ((_lifo && !_evictionCursor.hasPrevious()) ||
-                        (!_lifo && !_evictionCursor.hasNext())) {
-                    if (_evictionKeyCursor != null) {
-                        if (_evictionKeyCursor.hasNext()) {
-                            key = _evictionKeyCursor.next();
-                            resetEvictionObjectCursor(key);
-                        } else { // Need to reset Key cursor
-                            resetEvictionKeyCursor();
-                            if (_evictionKeyCursor != null) {
-                                if (_evictionKeyCursor.hasNext()) {
-                                    key = _evictionKeyCursor.next();
-                                    resetEvictionObjectCursor(key);
-                                }
-                            }
-                        }
-                    }
-                }
+         for (K k : poolMap.keySet()) {
+             final LinkedBlockingDeque<PooledObject<T>> idleObjects =
+                 poolMap.get(k).getIdleObjects();
+             for (PooledObject<T> p : idleObjects) {
+                 // each item into the map using the PooledObject object as the
+                 // key. It then gets sorted based on the idle time
+                 map.put(p, k);
+             }
+         }
+
+         // Now iterate created map and kill the first 15% plus one to account
+         // for zero
+         int itemsToRemove = ((int) (map.size() * 0.15)) + 1;
+         Iterator<Map.Entry<PooledObject<T>, K>> iter =
+             map.entrySet().iterator();
+
+         while (iter.hasNext() && itemsToRemove > 0) {
+             Map.Entry<PooledObject<T>, K> entry = iter.next();
+             // kind of backwards on naming.  In the map, each key is the
+             // PooledObject because it has the ordering with the timestamp
+             // value.  Each value that the key references is the key of the
+             // list it belongs to.
+             K key = entry.getValue();
+             PooledObject<T> p = entry.getKey();
+             try {
+                destroy(key, p);
+            } catch (Exception e) {
+                // TODO - Ignore?
+            }
+             itemsToRemove--;
+         }
+     }
 
-                if ((_lifo && !_evictionCursor.hasPrevious()) ||
-                        (!_lifo && !_evictionCursor.hasNext())) {
-                    continue; // reset failed, do nothing
-                }
 
-                // if LIFO and the _evictionCursor has a previous object,
-                // or FIFO and _evictionCursor has a next object, test it
-                pair = _lifo ?
-                        _evictionCursor.previous() :
-                        _evictionCursor.next();
-                _evictionCursor.remove();
-                ObjectQueue objectQueue = _poolMap.get(key);
-                objectQueue.incrementInternalProcessingCount();
-                _totalIdle--;
-            }
+     /**
+      * <p>Perform <code>numTests</code> idle object eviction tests, evicting
+      * examined objects that meet the criteria for eviction. If
+      * <code>testWhileIdle</code> is true, examined objects are validated
+      * when visited (and removed if invalid); otherwise only objects that
+      * have been idle for more than <code>minEvicableIdletimeMillis</code>
+      * are removed.</p>
+      *
+      * <p>Successive activations of this method examine objects in keyed pools
+      * in sequence, cycling through the keys and examining objects in
+      * oldest-to-youngest order within the keyed pools.</p>
+      *
+      * @throws Exception when there is a problem evicting idle objects.
+      */
+     public void evict() throws Exception {
+         assertOpen();
 
-            boolean removeObject=false;
-            if ((minEvictableIdleTimeMillis > 0) &&
-               (System.currentTimeMillis() - pair.getTstamp() >
-               minEvictableIdleTimeMillis)) {
-                removeObject=true;
-            }
-            if (testWhileIdle && removeObject == false) {
-                boolean active = false;
-                try {
-                    _factory.activateObject(key,pair.getValue());
-                    active = true;
-                } catch(Exception e) {
-                    removeObject=true;
-                }
-                if (active) {
-                    if (!_factory.validateObject(key,pair.getValue())) {
-                        removeObject=true;
-                    } else {
-                        try {
-                            _factory.passivateObject(key,pair.getValue());
-                        } catch(Exception e) {
-                            removeObject=true;
-                        }
-                    }
-                }
-            }
+         if (getNumIdle() == 0) {
+             return;
+         }
+
+         boolean testWhileIdle = _testWhileIdle;
+         long idleEvictTime = Long.MAX_VALUE;
+         
+         if (getMinEvictableIdleTimeMillis() > 0) {
+             idleEvictTime = getMinEvictableIdleTimeMillis();
+         }
+
+         PooledObject<T> underTest = null;
+         LinkedBlockingDeque<PooledObject<T>> idleObjects = null;
+         
+         for (int i = 0, m = getNumTests(); i < m; i++) {
+             if(evictionIterator == null || !evictionIterator.hasNext()) {
+                 if (evictionKeyIterator == null ||
+                         !evictionKeyIterator.hasNext()) {
+                     List<K> keyCopy = new ArrayList<K>();
+                     keyCopy.addAll(poolKeyList);
+                     evictionKeyIterator = keyCopy.iterator();
+                 }
+                 while (evictionKeyIterator.hasNext()) {
+                     evictionKey = evictionKeyIterator.next();
+                     ObjectDeque<T> objectDeque = poolMap.get(evictionKey);
+                     if (objectDeque == null) {
+                         continue;
+                     }
+                     idleObjects = objectDeque.getIdleObjects();
+                     
+                     if (getLifo()) {
+                         evictionIterator = idleObjects.descendingIterator();
+                     } else {
+                         evictionIterator = idleObjects.iterator();
+                     }
+                     if (evictionIterator.hasNext()) {
+                         break;
+                     }
+                     evictionIterator = null;
+                 }
+             }
+             if (evictionIterator == null) {
+                 // Pools exhausted
+                 return;
+             }
+             try {
+                 underTest = evictionIterator.next();
+             } catch (NoSuchElementException nsee) {
+                 // Object was borrowed in another thread
+                 // Don't count this as an eviction test so reduce i;
+                 i--;
+                 evictionIterator = null;
+                 continue;
+             }
+
+             if (!underTest.startEvictionTest()) {
+                 // Object was borrowed in another thread
+                 // Don't count this as an eviction test so reduce i;
+                 i--;
+                 continue;
+             }
+
+             if (idleEvictTime < underTest.getIdleTimeMillis()) {
+                 destroy(evictionKey, underTest);
+             } else {
+                 if (testWhileIdle) {
+                     boolean active = false;
+                     try {
+                         _factory.activateObject(evictionKey, 
+                                 underTest.getObject());
+                         active = true;
+                     } catch (Exception e) {
+                         destroy(evictionKey, underTest);
+                     }
+                     if (active) {
+                         if (!_factory.validateObject(evictionKey,
+                                 underTest.getObject())) {
+                             destroy(evictionKey, underTest);
+                         } else {
+                             try {
+                                 _factory.passivateObject(evictionKey,
+                                         underTest.getObject());
+                             } catch (Exception e) {
+                                 destroy(evictionKey, underTest);
+                             }
+                         }
+                     }
+                 }
+                 if (!underTest.endEvictionTest(idleObjects)) {
+                     // TODO - May need to add code here once additional states
+                     // are used
+                 }
+             }
+         }
+     }
 
-            if (removeObject) {
-                try {
-                    _factory.destroyObject(key, pair.getValue());
-                } catch(Exception e) {
-                    // ignored
-                }
-            }
-            synchronized (this) {
-                ObjectQueue objectQueue =
-                    _poolMap.get(key);
-                objectQueue.decrementInternalProcessingCount();
-                if (removeObject) {
-                    if (objectQueue.queue.isEmpty() &&
-                            objectQueue.activeCount == 0 &&
-                            objectQueue.internalProcessingCount == 0) {
-                        _poolMap.remove(key);
-                        _poolList.remove(key);
-                    }
+     
+    /**
+     * TODO: Remove the force parameters along with support for when exhausted
+     * grow.
+     */
+    private PooledObject<T> create(K key, boolean force) throws Exception {
+        int maxActive = getMaxActive(); // Per key
+        int maxTotal = getMaxTotal();   // All keys
+
+        // Check against the overall limit
+        boolean loop = true;
+        
+        while (loop) {
+            int newNumTotal = numTotal.incrementAndGet();
+            if (!force && maxTotal > -1 && newNumTotal > maxTotal) {
+                numTotal.decrementAndGet();
+                if (getNumIdle() == 0) {
+                    return null;
                 } else {
-                    _evictionCursor.add(pair);
-                    _totalIdle++;
-                    if (_lifo) {
-                        // Skip over the element we just added back
-                        _evictionCursor.previous();
-                    }
+                    clearOldest();
                 }
+            } else {
+                loop = false;
+            }
+        }
+         
+        // Make sure the key exists in the poolMap
+        ObjectDeque<T> objectDeque;
+        int newNumActive;
+        synchronized (poolMap) {
+            // This all has to be in the sync block to ensure that the key is
+            // not removed by destroy
+            objectDeque = poolMap.get(key);
+            if (objectDeque == null) {
+                objectDeque = new ObjectDeque<T>();
+                newNumActive = objectDeque.getNumActive().incrementAndGet();
+                poolMap.put(key, objectDeque);
+                poolKeyList.add(key);
+            } else {
+                newNumActive = objectDeque.getNumActive().incrementAndGet();
             }
         }
-        allocate();
-    }
 
-    /**
-     * Resets the eviction key cursor and closes any
-     * associated eviction object cursor
-     */
-    private void resetEvictionKeyCursor() {
-        if (_evictionKeyCursor != null) {
-            _evictionKeyCursor.close();
+        // Check against the per key limit
+        if (!force && maxActive > -1 && newNumActive > maxActive) {
+            cleanObjectDeque(key, objectDeque);
+            numTotal.decrementAndGet();
+            return null;
         }
-        _evictionKeyCursor = _poolList.cursor();
-        if (null != _evictionCursor) {
-            _evictionCursor.close();
-            _evictionCursor = null;
+         
+
+        T t = null;
+        try {
+            t = _factory.makeObject(key);
+        } catch (Exception e) {
+            cleanObjectDeque(key, objectDeque);
+            numTotal.decrementAndGet();
+            throw e;
         }
+
+        PooledObject<T> p = new PooledObject<T>(t);
+        objectDeque.getAllObjects().put(t, p);
+        return p;
     }
 
-    /**
-     * Resets the eviction object cursor for the given key
-     *
-     * @param key eviction key
-     */
-    private void resetEvictionObjectCursor(K key) {
-        if (_evictionCursor != null) {
-            _evictionCursor.close();
-        }
-        if (_poolMap == null) {
-            return;
+    private void destroy(K key, PooledObject<T> toDestory) throws Exception {
+         
+        ObjectDeque<T> objectDeque = poolMap.get(key);
+        objectDeque.getIdleObjects().remove(toDestory);
+        objectDeque.getAllObjects().remove(toDestory.getObject());
+
+        try {
+            _factory.destroyObject(key, toDestory.getObject());
+        } finally {
+            cleanObjectDeque(key, objectDeque);
+            numTotal.decrementAndGet();
         }
-        ObjectQueue pool = (_poolMap.get(key));
-        if (pool != null) {
-            CursorableLinkedList<ObjectTimestampPair<V>> queue = pool.queue;
-            _evictionCursor = queue.cursor(_lifo ? queue.size() : 0);
+    }
+
+    private void cleanObjectDeque(K key, ObjectDeque<T> objectDeque) {
+        int newNumActive = objectDeque.getNumActive().decrementAndGet();
+        if (newNumActive == 0) {
+            synchronized (poolMap) {
+                newNumActive = objectDeque.getNumActive().get();
+                if (newNumActive == 0) {
+                    poolMap.remove(key);
+                    poolKeyList.remove(key);
+                }
+            }
         }
     }
 
+    
     /**
      * Iterates through all the known keys and creates any necessary objects to maintain
      * the minimum level of pooled objects.
@@ -1993,26 +1637,18 @@ public class GenericKeyedObjectPool<K,V>
      * @see #setMinIdle
      * @throws Exception If there was an error whilst creating the pooled objects.
      */
-    @SuppressWarnings("unchecked") // OK, see (1)
     private void ensureMinIdle() throws Exception {
-        //Check if should sustain the pool
-        if (_minIdle > 0) {
-            K[] keysCopy;
-            synchronized(this) {
-                // Get the current set of keys
-                keysCopy = (K[]) _poolMap.keySet().toArray(); // (1) keySet() is of type Set<T>
-            }
+        int minIdle = getMinIdle();
+        if (minIdle < 1) {
+            return;
+        }
 
-            // Loop through all elements in _poolList
-            // Find out the total number of max active and max idle for that class
-            // If the number is less than the minIdle, do creation loop to boost numbers
-            for (int i=0; i < keysCopy.length; i++) {
-                //Get the next key to process
-                ensureMinIdle(keysCopy[i]);
-            }
+        for (K k : poolMap.keySet()) {
+            ensureMinIdle(k);
         }
     }
 
+
     /**
      * Re-creates any needed objects to maintain the minimum levels of
      * pooled objects for the specified key.
@@ -2025,34 +1661,86 @@ public class GenericKeyedObjectPool<K,V>
      * @throws Exception If there was an error whilst creating the pooled objects
      */
     private void ensureMinIdle(K key) throws Exception {
-        // Calculate current pool objects
-        ObjectQueue pool;
-        synchronized(this) {
-            pool = _poolMap.get(key);
-        }
-        if (pool == null) {
+        int minIdle = getMinIdle();
+        if (minIdle < 1) {
             return;
         }
 
+        // Calculate current pool objects
+        ObjectDeque<T> objectDeque = poolMap.get(key);
+
         // this method isn't synchronized so the
         // calculateDeficit is done at the beginning
         // as a loop limit and a second time inside the loop
         // to stop when another thread already returned the
         // needed objects
-        int objectDeficit = calculateDeficit(pool, false);
+        int deficit = calculateDeficit(objectDeque);
 
-        for (int i = 0; i < objectDeficit && calculateDeficit(pool, true) > 0; i++) {
-            try {
-                addObject(key);
-            } finally {
-                synchronized (this) {
-                    pool.decrementInternalProcessingCount();
-                }
-                allocate();
+        for (int i = 0; i < deficit && calculateDeficit(objectDeque) > 0; i++) {
+            addObject(key);
+        }
+    }
+
+    
+    /**
+     * <p>Adds an object to the keyed pool.</p>
+     * 
+     * <p>Validates the object if testOnReturn == true and passivates it before returning it to the pool.
+     * if validation or passivation fails, or maxIdle is set and there is no room in the pool, the instance
+     * is destroyed.</p>
+     * 
+     * <p>Calls {@link #allocate()} on successful completion</p>
+     * 
+     * @param key pool key
+     * @param obj instance to add to the keyed pool
+     * @param decrementNumActive whether or not to decrement the active count associated with the keyed pool
+     * @throws Exception
+     */
+    private void addIdleObject(K key, PooledObject<T> p) throws Exception {
+
+        if (p != null) {
+            _factory.passivateObject(key, p.getObject());
+            LinkedBlockingDeque<PooledObject<T>> idleObjects =
+                    poolMap.get(key).getIdleObjects();
+            if (getLifo()) {
+                idleObjects.addFirst(p);
+            } else {
+                idleObjects.addLast(p);
             }
         }
     }
 
+    /**
+     * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
+     * passivate it, and then place it in the idle object pool.
+     * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
+     *
+     * @param key the key a new instance should be added to
+     * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
+     * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been
+     * called on this pool.
+     */
+    @Override
+    public void addObject(K key) throws Exception {
+        assertOpen();
+        if (_factory == null) {
+            throw new IllegalStateException("Cannot add objects without a factory.");
+        }
+        PooledObject<T> p = create(key, false);
+        addIdleObject(key, p);
+    }
+
+    /**
+     * Registers a key for pool control and ensures that {@link #getMinIdle()}
+     * idle instances are created.
+     *
+     * @param key - The key to register for pool control.
+     * @since Pool 1.3
+     */
+    public void preparePool(K key) throws Exception {
+        ensureMinIdle(key);
+    }
+
     //--- non-public methods ----------------------------------------
 
     /**
@@ -2080,11 +1768,14 @@ public class GenericKeyedObjectPool<K,V>
      * @return string containing debug information
      */
     synchronized String debugInfo() {
-        StringBuffer buf = new StringBuffer();
+        StringBuilder buf = new StringBuilder();
         buf.append("Active: ").append(getNumActive()).append("\n");
         buf.append("Idle: ").append(getNumIdle()).append("\n");
-        for (K key : _poolMap.keySet()) {
-            buf.append("\t").append(key).append(" ").append(_poolMap.get(key)).append("\n");
+        for (Entry<K,ObjectDeque<T>> entry : poolMap.entrySet()) {
+            buf.append(entry.getKey());
+            buf.append(": ");
+            buf.append(entry.getValue());
+            buf.append("\n");
         }
         return buf.toString();
     }
@@ -2098,10 +1789,11 @@ public class GenericKeyedObjectPool<K,V>
      * @return the number of tests for the Evictor to run
      */
     private synchronized int getNumTests() {
+        int totalIdle = getNumIdle();
         if (_numTestsPerEvictionRun >= 0) {
-            return Math.min(_numTestsPerEvictionRun, _totalIdle);
+            return Math.min(_numTestsPerEvictionRun, totalIdle);
         }
-        return(int)(Math.ceil(_totalIdle/Math.abs((double)_numTestsPerEvictionRun)));
+        return(int)(Math.ceil(totalIdle/Math.abs((double)_numTestsPerEvictionRun)));
     }
 
     /**
@@ -2110,161 +1802,57 @@ public class GenericKeyedObjectPool<K,V>
      * instances is maintained without going past the maxActive value.
      * 
      * @param pool the ObjectPool to calculate the deficit for
-     * @param incrementInternal - Should the count of objects currently under
-     *                            some form of internal processing be
-     *                            incremented?
      * @return The number of objects to be created
      */
-    private synchronized int calculateDeficit(ObjectQueue pool,
-            boolean incrementInternal) {
+    private synchronized int calculateDeficit(ObjectDeque<T> objectDeque) {
+        
+        if (objectDeque == null) {
+            return getMinIdle();
+        }
         int objectDefecit = 0;
-
+        
         //Calculate no of objects needed to be created, in order to have
         //the number of pooled objects < maxActive();
-        objectDefecit = getMinIdle() - pool.queue.size();
+        objectDefecit = getMinIdle() - objectDeque.getIdleObjects().size();
         if (getMaxActive() > 0) {
-            int growLimit = Math.max(0, getMaxActive() - pool.activeCount - pool.queue.size() - pool.internalProcessingCount);
+            int growLimit = Math.max(0,
+                    getMaxActive() - objectDeque.getIdleObjects().size());
             objectDefecit = Math.min(objectDefecit, growLimit);
         }
 
         // Take the maxTotal limit into account
         if (getMaxTotal() > 0) {
-            int growLimit = Math.max(0, getMaxTotal() - getNumActive() - getNumIdle() - _totalInternalProcessing);
+            int growLimit = Math.max(0, getMaxTotal() - getNumActive() - getNumIdle());
             objectDefecit = Math.min(objectDefecit, growLimit);
         }
 

[... 333 lines stripped ...]


Mime
View raw message