Repository: commons-pool Updated Branches: refs/heads/master a09ca1327 -> 2d42cec48
Revert "Javadoc info" This reverts commit a09ca1327c33159c8fec20f1b10c6660e16a53f3. Project: http://git-wip-us.apache.org/repos/asf/commons-pool/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-pool/commit/2d42cec4 Tree: http://git-wip-us.apache.org/repos/asf/commons-pool/tree/2d42cec4 Diff: http://git-wip-us.apache.org/repos/asf/commons-pool/diff/2d42cec4 Branch: refs/heads/master Commit: 2d42cec48d820f60812628102b104f140ddd1931 Parents: a09ca13 Author: Sebb <s...@apache.org> Authored: Sun May 14 17:25:39 2017 +0100 Committer: Sebb <s...@apache.org> Committed: Sun May 14 17:25:39 2017 +0100 ---------------------------------------------------------------------- .../pool2/impl/GenericKeyedObjectPool.java | 447 ++++++++++--------- 1 file changed, 242 insertions(+), 205 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-pool/blob/2d42cec4/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java index 5de08e0..c4bf1ba 100644 --- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java @@ -52,10 +52,6 @@ import org.apache.commons.pool2.SwallowedExceptionListener; * provided to one of these methods, a sub-new pool is created under the given * key to be managed by the containing <code>GenericKeyedObjectPool.</code> * <p> - * Note that the current implementation uses a ConcurrentHashMap which uses - * equals() to compare keys. - * This means that distinct instance keys must be distinguishable using equals. - * <p> * Optionally, one may configure the pool to examine and possibly evict objects * as they sit idle in the pool and to ensure that a minimum number of idle * objects is maintained for each key. This is performed by an "idle object @@ -75,19 +71,19 @@ import org.apache.commons.pool2.SwallowedExceptionListener; * @param <K> The type of keys maintained by this pool. * @param <T> Type of element pooled in this pool. * - * @version $Revision: 1701122 $ + * @version $Revision$ * * @since 2.0 */ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> - implements KeyedObjectPool<K,T>, GenericKeyedObjectPoolMXBean<K> { +implements KeyedObjectPool<K,T>, GenericKeyedObjectPoolMXBean<K> { /** * Create a new <code>GenericKeyedObjectPool</code> using defaults from * {@link GenericKeyedObjectPoolConfig}. * @param factory the factory to be used to create entries */ - public GenericKeyedObjectPool(KeyedPooledObjectFactory<K,T> factory) { + public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K,T> factory) { this(factory, new GenericKeyedObjectPoolConfig()); } @@ -101,8 +97,8 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * the configuration object will not be reflected in the * pool. */ - public GenericKeyedObjectPool(KeyedPooledObjectFactory<K,T> factory, - GenericKeyedObjectPoolConfig config) { + public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K,T> factory, + final GenericKeyedObjectPoolConfig config) { super(config, ONAME_BASE, config.getJmxNamePrefix()); @@ -141,7 +137,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @see #getMaxTotalPerKey */ - public void setMaxTotalPerKey(int maxTotalPerKey) { + public void setMaxTotalPerKey(final int maxTotalPerKey) { this.maxTotalPerKey = maxTotalPerKey; } @@ -182,7 +178,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @see #getMaxIdlePerKey */ - public void setMaxIdlePerKey(int maxIdlePerKey) { + public void setMaxIdlePerKey(final int maxIdlePerKey) { this.maxIdlePerKey = maxIdlePerKey; } @@ -204,7 +200,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * @see #getMaxIdlePerKey() * @see #setTimeBetweenEvictionRunsMillis */ - public void setMinIdlePerKey(int minIdlePerKey) { + public void setMinIdlePerKey(final int minIdlePerKey) { this.minIdlePerKey = minIdlePerKey; } @@ -226,7 +222,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> */ @Override public int getMinIdlePerKey() { - int maxIdlePerKeySave = getMaxIdlePerKey(); + final int maxIdlePerKeySave = getMaxIdlePerKey(); if (this.minIdlePerKey > maxIdlePerKeySave) { return maxIdlePerKeySave; } @@ -240,7 +236,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @see GenericKeyedObjectPoolConfig */ - public void setConfig(GenericKeyedObjectPoolConfig conf) { + public void setConfig(final GenericKeyedObjectPoolConfig conf) { setLifo(conf.getLifo()); setMaxIdlePerKey(conf.getMaxIdlePerKey()); setMaxTotalPerKey(conf.getMaxTotalPerKey()); @@ -259,6 +255,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> setTimeBetweenEvictionRunsMillis( conf.getTimeBetweenEvictionRunsMillis()); setEvictionPolicyClassName(conf.getEvictionPolicyClassName()); + setEvictorShutdownTimeoutMillis(conf.getEvictorShutdownTimeoutMillis()); } /** @@ -278,7 +275,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * {@inheritDoc} */ @Override - public T borrowObject(K key) throws Exception { + public T borrowObject(final K key) throws Exception { return borrowObject(key, getMaxWaitMillis()); } @@ -338,30 +335,30 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * @throws Exception if a keyed object instance cannot be returned due to an * error */ - public T borrowObject(K key, long borrowMaxWaitMillis) throws Exception { + public T borrowObject(final K key, final long borrowMaxWaitMillis) throws Exception { assertOpen(); PooledObject<T> p = null; // Get local copy of current config so it is consistent for entire // method execution - boolean blockWhenExhausted = getBlockWhenExhausted(); + final boolean blockWhenExhausted = getBlockWhenExhausted(); boolean create; - long waitTime = System.currentTimeMillis(); - ObjectDeque<T> objectDeque = register(key); + final long waitTime = System.currentTimeMillis(); + final ObjectDeque<T> objectDeque = register(key); try { while (p == null) { create = false; - if (blockWhenExhausted) { - p = objectDeque.getIdleObjects().pollFirst(); - if (p == null) { - p = create(key); - if (p != null) { - create = true; - } + p = objectDeque.getIdleObjects().pollFirst(); + if (p == null) { + p = create(key); + if (p != null) { + create = true; } + } + if (blockWhenExhausted) { if (p == null) { if (borrowMaxWaitMillis < 0) { p = objectDeque.getIdleObjects().takeFirst(); @@ -374,37 +371,27 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> throw new NoSuchElementException( "Timeout waiting for idle object"); } - if (!p.allocate()) { - p = null; - } } else { - p = objectDeque.getIdleObjects().pollFirst(); - if (p == null) { - p = create(key); - if (p != null) { - create = true; - } - } if (p == null) { throw new NoSuchElementException("Pool exhausted"); } - if (!p.allocate()) { - p = null; - } + } + if (!p.allocate()) { + p = null; } if (p != null) { try { factory.activateObject(key, p); - } catch (Exception e) { + } catch (final Exception e) { try { destroy(key, p, true); - } catch (Exception e1) { + } catch (final Exception e1) { // Ignore - activation failure is more important } p = null; if (create) { - NoSuchElementException nsee = new NoSuchElementException( + final NoSuchElementException nsee = new NoSuchElementException( "Unable to activate object"); nsee.initCause(e); throw nsee; @@ -415,7 +402,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> Throwable validationThrowable = null; try { validate = factory.validateObject(key, p); - } catch (Throwable t) { + } catch (final Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } @@ -423,12 +410,12 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> try { destroy(key, p, true); destroyedByBorrowValidationCount.incrementAndGet(); - } catch (Exception e) { + } catch (final Exception e) { // Ignore - validation failure is more important } p = null; if (create) { - NoSuchElementException nsee = new NoSuchElementException( + final NoSuchElementException nsee = new NoSuchElementException( "Unable to validate object"); nsee.initCause(validationThrowable); throw nsee; @@ -470,17 +457,17 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * returned to the pool multiple times */ @Override - public void returnObject(K key, T obj) { + public void returnObject(final K key, final T obj) { - ObjectDeque<T> objectDeque = poolMap.get(key); + final ObjectDeque<T> objectDeque = poolMap.get(key); - PooledObject<T> p = objectDeque.getAllObjects().get(new IdentityWrapper<T>(obj)); + final PooledObject<T> p = objectDeque.getAllObjects().get(new IdentityWrapper<T>(obj)); if (p == null) { throw new IllegalStateException( "Returned object not currently part of this pool"); } - + synchronized(p) { final PooledObjectState state = p.getState(); if (state != PooledObjectState.ALLOCATED) { @@ -490,81 +477,80 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> p.markReturning(); // Keep from being marked abandoned (once GKOP does this) } - long activeTime = p.getActiveTimeMillis(); + final long activeTime = p.getActiveTimeMillis(); - if (getTestOnReturn()) { - if (!factory.validateObject(key, p)) { + try { + if (getTestOnReturn()) { + if (!factory.validateObject(key, p)) { + try { + destroy(key, p, true); + } catch (final Exception e) { + swallowException(e); + } + if (objectDeque.idleObjects.hasTakeWaiters()) { + try { + addObject(key); + } catch (final Exception e) { + swallowException(e); + } + } + return; + } + } + + try { + factory.passivateObject(key, p); + } catch (final Exception e1) { + swallowException(e1); try { destroy(key, p, true); - } catch (Exception e) { + } catch (final Exception e) { swallowException(e); } if (objectDeque.idleObjects.hasTakeWaiters()) { try { addObject(key); - } catch (Exception e) { + } catch (final Exception e) { swallowException(e); } } - updateStatsReturn(activeTime); return; } - } - try { - factory.passivateObject(key, p); - } catch (Exception e1) { - swallowException(e1); - try { - destroy(key, p, true); - } catch (Exception e) { - swallowException(e); + if (!p.deallocate()) { + throw new IllegalStateException( + "Object has already been returned to this pool"); } - if (objectDeque.idleObjects.hasTakeWaiters()) { + + final int maxIdle = getMaxIdlePerKey(); + final LinkedBlockingDeque<PooledObject<T>> idleObjects = + objectDeque.getIdleObjects(); + + if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) { try { - addObject(key); - } catch (Exception e) { + destroy(key, p, true); + } catch (final Exception e) { swallowException(e); } - } - updateStatsReturn(activeTime); - return; - } - - if (!p.deallocate()) { - throw new IllegalStateException( - "Object has already been returned to this pool"); - } - - int maxIdle = getMaxIdlePerKey(); - LinkedBlockingDeque<PooledObject<T>> idleObjects = - objectDeque.getIdleObjects(); - - if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) { - try { - destroy(key, p, true); - } catch (Exception e) { - swallowException(e); - } - } else { - if (getLifo()) { - idleObjects.addFirst(p); } else { - idleObjects.addLast(p); + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + if (isClosed()) { + // Pool closed while object was being added to idle objects. + // Make sure the returned object is destroyed rather than left + // in the idle object pool (which would effectively be a leak) + clear(key); + } } - if (isClosed()) { - // Pool closed while object was being added to idle objects. - // Make sure the returned object is destroyed rather than left - // in the idle object pool (which would effectively be a leak) - clear(key); + } finally { + if (hasBorrowWaiters()) { + reuseCapacity(); } + updateStatsReturn(activeTime); } - - if (hasBorrowWaiters()) { - reuseCapacity(); - } - - updateStatsReturn(activeTime); } @@ -583,11 +569,11 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * under the given key */ @Override - public void invalidateObject(K key, T obj) throws Exception { + public void invalidateObject(final K key, final T obj) throws Exception { - ObjectDeque<T> objectDeque = poolMap.get(key); + final ObjectDeque<T> objectDeque = poolMap.get(key); - PooledObject<T> p = objectDeque.getAllObjects().get(new IdentityWrapper<T>(obj)); + final PooledObject<T> p = objectDeque.getAllObjects().get(new IdentityWrapper<T>(obj)); if (p == null) { throw new IllegalStateException( "Object not currently part of this pool"); @@ -623,7 +609,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> */ @Override public void clear() { - Iterator<K> iter = poolMap.keySet().iterator(); + final Iterator<K> iter = poolMap.keySet().iterator(); while (iter.hasNext()) { clear(iter.next()); @@ -640,12 +626,12 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * @param key the key to clear */ @Override - public void clear(K key) { + public void clear(final K key) { - ObjectDeque<T> objectDeque = register(key); + final ObjectDeque<T> objectDeque = register(key); try { - LinkedBlockingDeque<PooledObject<T>> idleObjects = + final LinkedBlockingDeque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects(); PooledObject<T> p = idleObjects.poll(); @@ -653,7 +639,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> while (p != null) { try { destroy(key, p, true); - } catch (Exception e) { + } catch (final Exception e) { swallowException(e); } p = idleObjects.poll(); @@ -672,7 +658,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> @Override public int getNumIdle() { - Iterator<ObjectDeque<T>> iter = poolMap.values().iterator(); + final Iterator<ObjectDeque<T>> iter = poolMap.values().iterator(); int result = 0; while (iter.hasNext()) { @@ -684,7 +670,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> @Override - public int getNumActive(K key) { + public int getNumActive(final K key) { final ObjectDeque<T> objectDeque = poolMap.get(key); if (objectDeque != null) { return objectDeque.getAllObjects().size() - @@ -695,7 +681,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> @Override - public int getNumIdle(K key) { + public int getNumIdle(final K key) { final ObjectDeque<T> objectDeque = poolMap.get(key); return objectDeque != null ? objectDeque.getIdleObjects().size() : 0; } @@ -732,7 +718,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> jmxUnregister(); // Release any threads that were waiting for an object - Iterator<ObjectDeque<T>> iter = poolMap.values().iterator(); + final Iterator<ObjectDeque<T>> iter = poolMap.values().iterator(); while (iter.hasNext()) { iter.next().getIdleObjects().interuptTakeWaiters(); } @@ -752,14 +738,15 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> // build sorted map of idle objects final Map<PooledObject<T>, K> map = new TreeMap<PooledObject<T>, K>(); - for (K k : poolMap.keySet()) { - ObjectDeque<T> queue = poolMap.get(k); + for (final Map.Entry<K, ObjectDeque<T>> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque<T> deque = entry.getValue(); // Protect against possible NPE if key has been removed in another // thread. Not worth locking the keys while this loop completes. - if (queue != null) { + if (deque != null) { final LinkedBlockingDeque<PooledObject<T>> idleObjects = - queue.getIdleObjects(); - for (PooledObject<T> p : idleObjects) { + deque.getIdleObjects(); + for (final 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); @@ -770,22 +757,22 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> // 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(); + final Iterator<Map.Entry<PooledObject<T>, K>> iter = + map.entrySet().iterator(); while (iter.hasNext() && itemsToRemove > 0) { - Map.Entry<PooledObject<T>, K> entry = iter.next(); + final 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(); + final K key = entry.getValue(); + final PooledObject<T> p = entry.getKey(); // Assume the destruction succeeds boolean destroyed = true; try { destroyed = destroy(key, p, false); - } catch (Exception e) { + } catch (final Exception e) { swallowException(e); } if (destroyed) { @@ -814,8 +801,9 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> int maxQueueLength = 0; LinkedBlockingDeque<PooledObject<T>> mostLoaded = null; K loadedKey = null; - for (K k : poolMap.keySet()) { - final ObjectDeque<T> deque = poolMap.get(k); + for (final Map.Entry<K, ObjectDeque<T>> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque<T> deque = entry.getValue(); if (deque != null) { final LinkedBlockingDeque<PooledObject<T>> pool = deque.getIdleObjects(); final int queueLength = pool.getTakeQueueLength(); @@ -831,11 +819,11 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> if (mostLoaded != null) { register(loadedKey); try { - PooledObject<T> p = create(loadedKey); + final PooledObject<T> p = create(loadedKey); if (p != null) { addIdleObject(loadedKey, p); } - } catch (Exception e) { + } catch (final Exception e) { swallowException(e); } finally { deregister(loadedKey); @@ -851,11 +839,11 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * {@code false} */ private boolean hasBorrowWaiters() { - for (K k : poolMap.keySet()) { - final ObjectDeque<T> deque = poolMap.get(k); + for (final Map.Entry<K, ObjectDeque<T>> entry : poolMap.entrySet()) { + final ObjectDeque<T> deque = entry.getValue(); if (deque != null) { final LinkedBlockingDeque<PooledObject<T>> pool = - deque.getIdleObjects(); + deque.getIdleObjects(); if(pool.hasTakeWaiters()) { return true; } @@ -881,22 +869,22 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> } PooledObject<T> underTest = null; - EvictionPolicy<T> evictionPolicy = getEvictionPolicy(); + final EvictionPolicy<T> evictionPolicy = getEvictionPolicy(); synchronized (evictionLock) { - EvictionConfig evictionConfig = new EvictionConfig( + final EvictionConfig evictionConfig = new EvictionConfig( getMinEvictableIdleTimeMillis(), getSoftMinEvictableIdleTimeMillis(), getMinIdlePerKey()); - boolean testWhileIdle = getTestWhileIdle(); + final boolean testWhileIdle = getTestWhileIdle(); 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>(); - Lock readLock = keyLock.readLock(); + final List<K> keyCopy = new ArrayList<K>(); + final Lock readLock = keyLock.readLock(); readLock.lock(); try { keyCopy.addAll(poolKeyList); @@ -907,11 +895,11 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> } while (evictionKeyIterator.hasNext()) { evictionKey = evictionKeyIterator.next(); - ObjectDeque<T> objectDeque = poolMap.get(evictionKey); + final ObjectDeque<T> objectDeque = poolMap.get(evictionKey); if (objectDeque == null) { continue; } - + final Deque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects(); evictionIterator = new EvictionIterator(idleObjects); if (evictionIterator.hasNext()) { @@ -928,7 +916,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> try { underTest = evictionIterator.next(); idleObjects = evictionIterator.getIdleObjects(); - } catch (NoSuchElementException nsee) { + } catch (final NoSuchElementException nsee) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; @@ -950,7 +938,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> try { evict = evictionPolicy.evict(evictionConfig, underTest, poolMap.get(evictionKey).getIdleObjects().size()); - } catch (Throwable t) { + } catch (final Throwable t) { // Slightly convoluted as SwallowedExceptionListener // uses Exception rather than Throwable PoolUtils.checkRethrow(t); @@ -968,7 +956,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> try { factory.activateObject(evictionKey, underTest); active = true; - } catch (Exception e) { + } catch (final Exception e) { destroy(evictionKey, underTest, true); destroyedByEvictorCount.incrementAndGet(); } @@ -979,7 +967,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> } else { try { factory.passivateObject(evictionKey, underTest); - } catch (Exception e) { + } catch (final Exception e) { destroy(evictionKey, underTest, true); destroyedByEvictorCount.incrementAndGet(); } @@ -1004,15 +992,20 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @throws Exception If the objection creation fails */ - private PooledObject<T> create(K key) throws Exception { + private PooledObject<T> create(final K key) throws Exception { int maxTotalPerKeySave = getMaxTotalPerKey(); // Per key - int maxTotal = getMaxTotal(); // All keys + if (maxTotalPerKeySave < 0) { + maxTotalPerKeySave = Integer.MAX_VALUE; + } + final int maxTotal = getMaxTotal(); // All keys + + final ObjectDeque<T> objectDeque = poolMap.get(key); // Check against the overall limit boolean loop = true; while (loop) { - int newNumTotal = numTotal.incrementAndGet(); + final int newNumTotal = numTotal.incrementAndGet(); if (maxTotal > -1 && newNumTotal > maxTotal) { numTotal.decrementAndGet(); if (getNumIdle() == 0) { @@ -1024,25 +1017,58 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> } } - ObjectDeque<T> objectDeque = poolMap.get(key); - long newCreateCount = objectDeque.getCreateCount().incrementAndGet(); + // Flag that indicates if create should: + // - TRUE: call the factory to create an object + // - FALSE: return null + // - null: loop and re-test the condition that determines whether to + // call the factory + Boolean create = null; + while (create == null) { + synchronized (objectDeque.makeObjectCountLock) { + final long newCreateCount = objectDeque.getCreateCount().incrementAndGet(); + // Check against the per key limit + if (newCreateCount > maxTotalPerKeySave) { + // The key is currently at capacity or in the process of + // making enough new objects to take it to capacity. + objectDeque.getCreateCount().decrementAndGet(); + if (objectDeque.makeObjectCount == 0) { + // There are no makeObject() calls in progress for this + // key so the key is at capacity. Do not attempt to + // create a new object. Return and wait for an object to + // be returned. + create = Boolean.FALSE; + } else { + // There are makeObject() calls in progress that might + // bring the pool to capacity. Those calls might also + // fail so wait until they complete and then re-test if + // the pool is at capacity or not. + objectDeque.makeObjectCountLock.wait(); + } + } else { + // The pool is not at capacity. Create a new object. + objectDeque.makeObjectCount++; + create = Boolean.TRUE; + } + } + } - // Check against the per key limit - if (maxTotalPerKeySave > -1 && newCreateCount > maxTotalPerKeySave || - newCreateCount > Integer.MAX_VALUE) { + if (!create.booleanValue()) { numTotal.decrementAndGet(); - objectDeque.getCreateCount().decrementAndGet(); return null; } - PooledObject<T> p = null; try { p = factory.makeObject(key); - } catch (Exception e) { + } catch (final Exception e) { numTotal.decrementAndGet(); objectDeque.getCreateCount().decrementAndGet(); throw e; + } finally { + synchronized (objectDeque.makeObjectCountLock) { + objectDeque.makeObjectCount--; + objectDeque.makeObjectCountLock.notifyAll(); + } } createdCount.incrementAndGet(); @@ -1060,13 +1086,13 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * @return {@code true} if the object was destroyed, otherwise {@code false} * @throws Exception If the object destruction failed */ - private boolean destroy(K key, PooledObject<T> toDestroy, boolean always) + private boolean destroy(final K key, final PooledObject<T> toDestroy, final boolean always) throws Exception { - ObjectDeque<T> objectDeque = register(key); + final ObjectDeque<T> objectDeque = register(key); try { - boolean isIdle = objectDeque.getIdleObjects().remove(toDestroy); + final boolean isIdle = objectDeque.getIdleObjects().remove(toDestroy); if (isIdle || always) { objectDeque.getAllObjects().remove(new IdentityWrapper<T>(toDestroy.getObject())); @@ -1099,7 +1125,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * method returns without throwing an exception then it will never * return null. */ - private ObjectDeque<T> register(K k) { + private ObjectDeque<T> register(final K k) { Lock lock = keyLock.readLock(); ObjectDeque<T> objectDeque = null; try { @@ -1138,14 +1164,14 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @param k The key to de-register */ - private void deregister(K k) { + private void deregister(final K k) { ObjectDeque<T> objectDeque; objectDeque = poolMap.get(k); - long numInterested = objectDeque.getNumInterested().decrementAndGet(); + final long numInterested = objectDeque.getNumInterested().decrementAndGet(); if (numInterested == 0 && objectDeque.getCreateCount().get() == 0) { // Potential to remove key - Lock writeLock = keyLock.writeLock(); + final Lock writeLock = keyLock.writeLock(); writeLock.lock(); try { if (objectDeque.getCreateCount().get() == 0 && @@ -1164,12 +1190,12 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> @Override void ensureMinIdle() throws Exception { - int minIdlePerKeySave = getMinIdlePerKey(); + final int minIdlePerKeySave = getMinIdlePerKey(); if (minIdlePerKeySave < 1) { return; } - for (K k : poolMap.keySet()) { + for (final K k : poolMap.keySet()) { ensureMinIdle(k); } } @@ -1182,7 +1208,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @throws Exception If a new object is required and cannot be created */ - private void ensureMinIdle(K key) throws Exception { + private void ensureMinIdle(final K key) throws Exception { // Calculate current pool objects ObjectDeque<T> objectDeque = poolMap.get(key); @@ -1194,10 +1220,16 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> // as a loop limit and a second time inside the loop // to stop when another thread already returned the // needed objects - int deficit = calculateDeficit(objectDeque); + final int deficit = calculateDeficit(objectDeque); for (int i = 0; i < deficit && calculateDeficit(objectDeque) > 0; i++) { addObject(key); + // If objectDeque was null, it won't be any more. Obtain a reference + // to it so the deficit can be correctly calculated. It needs to + // take account of objects created in other threads. + if (objectDeque == null) { + objectDeque = poolMap.get(key); + } } } @@ -1213,11 +1245,11 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * fails. */ @Override - public void addObject(K key) throws Exception { + public void addObject(final K key) throws Exception { assertOpen(); register(key); try { - PooledObject<T> p = create(key); + final PooledObject<T> p = create(key); addIdleObject(key, p); } finally { deregister(key); @@ -1232,11 +1264,11 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @throws Exception If the associated factory fails to passivate the object */ - private void addIdleObject(K key, PooledObject<T> p) throws Exception { + private void addIdleObject(final K key, final PooledObject<T> p) throws Exception { if (p != null) { factory.passivateObject(key, p); - LinkedBlockingDeque<PooledObject<T>> idleObjects = + final LinkedBlockingDeque<PooledObject<T>> idleObjects = poolMap.get(key).getIdleObjects(); if (getLifo()) { idleObjects.addFirst(p); @@ -1254,8 +1286,8 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @throws Exception If the associated factory throws an exception */ - public void preparePool(K key) throws Exception { - int minIdlePerKeySave = getMinIdlePerKey(); + public void preparePool(final K key) throws Exception { + final int minIdlePerKeySave = getMinIdlePerKey(); if (minIdlePerKeySave < 1) { return; } @@ -1269,8 +1301,8 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * @return The number of objects to test for validity */ private int getNumTests() { - int totalIdle = getNumIdle(); - int numTests = getNumTestsPerEvictionRun(); + final int totalIdle = getNumIdle(); + final int numTests = getNumTestsPerEvictionRun(); if (numTests >= 0) { return Math.min(numTests, totalIdle); } @@ -1286,15 +1318,15 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * * @return The number of new objects to create */ - private int calculateDeficit(ObjectDeque<T> objectDeque) { + private int calculateDeficit(final ObjectDeque<T> objectDeque) { if (objectDeque == null) { return getMinIdlePerKey(); } // Used more than once so keep a local copy so the value is consistent - int maxTotal = getMaxTotal(); - int maxTotalPerKeySave = getMaxTotalPerKey(); + final int maxTotal = getMaxTotal(); + final int maxTotalPerKeySave = getMaxTotalPerKey(); int objectDefecit = 0; @@ -1302,14 +1334,14 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> // the number of pooled objects < maxTotalPerKey(); objectDefecit = getMinIdlePerKey() - objectDeque.getIdleObjects().size(); if (maxTotalPerKeySave > 0) { - int growLimit = Math.max(0, + final int growLimit = Math.max(0, maxTotalPerKeySave - objectDeque.getIdleObjects().size()); objectDefecit = Math.min(objectDefecit, growLimit); } // Take the maxTotal limit into account if (maxTotal > 0) { - int growLimit = Math.max(0, maxTotal - getNumActive() - getNumIdle()); + final int growLimit = Math.max(0, maxTotal - getNumActive() - getNumIdle()); objectDefecit = Math.min(objectDefecit, growLimit); } @@ -1321,14 +1353,14 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> @Override public Map<String,Integer> getNumActivePerKey() { - HashMap<String,Integer> result = new HashMap<String,Integer>(); + final HashMap<String,Integer> result = new HashMap<String,Integer>(); - Iterator<Entry<K,ObjectDeque<T>>> iter = poolMap.entrySet().iterator(); + final Iterator<Entry<K,ObjectDeque<T>>> iter = poolMap.entrySet().iterator(); while (iter.hasNext()) { - Entry<K,ObjectDeque<T>> entry = iter.next(); + final Entry<K,ObjectDeque<T>> entry = iter.next(); if (entry != null) { - K key = entry.getKey(); - ObjectDeque<T> objectDequeue = entry.getValue(); + final K key = entry.getKey(); + final ObjectDeque<T> objectDequeue = entry.getValue(); if (key != null && objectDequeue != null) { result.put(key.toString(), Integer.valueOf( objectDequeue.getAllObjects().size() - @@ -1352,7 +1384,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> int result = 0; if (getBlockWhenExhausted()) { - Iterator<ObjectDeque<T>> iter = poolMap.values().iterator(); + final Iterator<ObjectDeque<T>> iter = poolMap.values().iterator(); while (iter.hasNext()) { // Assume no overflow @@ -1373,16 +1405,17 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> */ @Override public Map<String,Integer> getNumWaitersByKey() { - Map<String,Integer> result = new HashMap<String,Integer>(); + final Map<String,Integer> result = new HashMap<String,Integer>(); - for (K key : poolMap.keySet()) { - ObjectDeque<T> queue = poolMap.get(key); - if (queue != null) { + for (final Map.Entry<K, ObjectDeque<T>> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque<T> deque = entry.getValue(); + if (deque != null) { if (getBlockWhenExhausted()) { - result.put(key.toString(), Integer.valueOf( - queue.getIdleObjects().getTakeQueueLength())); + result.put(k.toString(), Integer.valueOf( + deque.getIdleObjects().getTakeQueueLength())); } else { - result.put(key.toString(), Integer.valueOf(0)); + result.put(k.toString(), Integer.valueOf(0)); } } } @@ -1402,16 +1435,17 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> */ @Override public Map<String,List<DefaultPooledObjectInfo>> listAllObjects() { - Map<String,List<DefaultPooledObjectInfo>> result = + final Map<String,List<DefaultPooledObjectInfo>> result = new HashMap<String,List<DefaultPooledObjectInfo>>(); - for (K key : poolMap.keySet()) { - ObjectDeque<T> queue = poolMap.get(key); - if (queue != null) { - List<DefaultPooledObjectInfo> list = + for (final Map.Entry<K, ObjectDeque<T>> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque<T> deque = entry.getValue(); + if (deque != null) { + final List<DefaultPooledObjectInfo> list = new ArrayList<DefaultPooledObjectInfo>(); - result.put(key.toString(), list); - for (PooledObject<T> p : queue.getAllObjects().values()) { + result.put(k.toString(), list); + for (final PooledObject<T> p : deque.getAllObjects().values()) { list.add(new DefaultPooledObjectInfo(p)); } } @@ -1435,9 +1469,12 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> */ private final AtomicInteger createCount = new AtomicInteger(0); + private long makeObjectCount = 0; + private final Object makeObjectCountLock = new Object(); + /* * The map is keyed on pooled instances, wrapped to ensure that - * they work properly as keys. + * they work properly as keys. */ private final Map<IdentityWrapper<S>, PooledObject<S>> allObjects = new ConcurrentHashMap<IdentityWrapper<S>, PooledObject<S>>(); @@ -1455,7 +1492,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> * @param fairness true means client threads waiting to borrow / return instances * will be served as if waiting in a FIFO queue. */ - public ObjectDeque(boolean fairness) { + public ObjectDeque(final boolean fairness) { idleObjects = new LinkedBlockingDeque<PooledObject<S>>(fairness); } @@ -1498,7 +1535,7 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append("ObjectDeque [idleObjects="); builder.append(idleObjects); builder.append(", createCount="); @@ -1517,9 +1554,9 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> private volatile int maxIdlePerKey = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY; private volatile int minIdlePerKey = - GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; + GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; private volatile int maxTotalPerKey = - GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; + GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; private final KeyedPooledObjectFactory<K,T> factory; private final boolean fairness; @@ -1553,10 +1590,10 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> // JMX specific attributes private static final String ONAME_BASE = - "org.apache.commons.pool2:type=GenericKeyedObjectPool,name="; + "org.apache.commons.pool2:type=GenericKeyedObjectPool,name="; @Override - protected void toStringAppendFields(StringBuilder builder) { + protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); builder.append(", maxIdlePerKey="); builder.append(maxIdlePerKey);