This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-pool.git
commit 7a203b4975d09f38a738e53d163ae7ac52ea2b01 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Mon Feb 15 16:09:25 2021 -0500 Sort members. --- .../pool2/BaseKeyedPooledObjectFactory.java | 70 +- .../org/apache/commons/pool2/BaseObjectPool.java | 84 +- .../commons/pool2/BasePooledObjectFactory.java | 50 +- .../commons/pool2/KeyedPooledObjectFactory.java | 50 +- .../java/org/apache/commons/pool2/PoolUtils.java | 2324 ++++++++++---------- .../org/apache/commons/pool2/PooledObject.java | 154 +- .../apache/commons/pool2/PooledObjectFactory.java | 44 +- 7 files changed, 1388 insertions(+), 1388 deletions(-) diff --git a/src/main/java/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java b/src/main/java/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java index 0c7dfd0..b375ce2 100644 --- a/src/main/java/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java +++ b/src/main/java/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java @@ -36,6 +36,21 @@ public abstract class BaseKeyedPooledObjectFactory<K, V> extends BaseObject implements KeyedPooledObjectFactory<K, V> { /** + * Reinitialize an instance to be returned by the pool. + * <p> + * The default implementation is a no-op. + * </p> + * + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be activated + */ + @Override + public void activateObject(final K key, final PooledObject<V> p) + throws Exception { + // The default implementation is a no-op. + } + + /** * Create an instance that can be served by the pool. * * @param key the key used when constructing the object @@ -48,14 +63,19 @@ public abstract class BaseKeyedPooledObjectFactory<K, V> extends BaseObject throws Exception; /** - * Wrap the provided instance with an implementation of - * {@link PooledObject}. - * - * @param value the instance to wrap + * Destroy an instance no longer needed by the pool. + * <p> + * The default implementation is a no-op. + * </p> * - * @return The provided instance, wrapped by a {@link PooledObject} + * @param key the key used when selecting the instance + * @param p a {@code PooledObject} wrapping the instance to be destroyed */ - public abstract PooledObject<V> wrap(V value); + @Override + public void destroyObject(final K key, final PooledObject<V> p) + throws Exception { + // The default implementation is a no-op. + } @Override public PooledObject<V> makeObject(final K key) throws Exception { @@ -63,16 +83,16 @@ public abstract class BaseKeyedPooledObjectFactory<K, V> extends BaseObject } /** - * Destroy an instance no longer needed by the pool. + * Uninitialize an instance to be returned to the idle object pool. * <p> * The default implementation is a no-op. * </p> * - * @param key the key used when selecting the instance - * @param p a {@code PooledObject} wrapping the instance to be destroyed + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be passivated */ @Override - public void destroyObject(final K key, final PooledObject<V> p) + public void passivateObject(final K key, final PooledObject<V> p) throws Exception { // The default implementation is a no-op. } @@ -93,32 +113,12 @@ public abstract class BaseKeyedPooledObjectFactory<K, V> extends BaseObject } /** - * Reinitialize an instance to be returned by the pool. - * <p> - * The default implementation is a no-op. - * </p> + * Wrap the provided instance with an implementation of + * {@link PooledObject}. * - * @param key the key used when selecting the object - * @param p a {@code PooledObject} wrapping the instance to be activated - */ - @Override - public void activateObject(final K key, final PooledObject<V> p) - throws Exception { - // The default implementation is a no-op. - } - - /** - * Uninitialize an instance to be returned to the idle object pool. - * <p> - * The default implementation is a no-op. - * </p> + * @param value the instance to wrap * - * @param key the key used when selecting the object - * @param p a {@code PooledObject} wrapping the instance to be passivated + * @return The provided instance, wrapped by a {@link PooledObject} */ - @Override - public void passivateObject(final K key, final PooledObject<V> p) - throws Exception { - // The default implementation is a no-op. - } + public abstract PooledObject<V> wrap(V value); } diff --git a/src/main/java/org/apache/commons/pool2/BaseObjectPool.java b/src/main/java/org/apache/commons/pool2/BaseObjectPool.java index c20b1d7..291173d 100644 --- a/src/main/java/org/apache/commons/pool2/BaseObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/BaseObjectPool.java @@ -30,55 +30,45 @@ package org.apache.commons.pool2; */ public abstract class BaseObjectPool<T> extends BaseObject implements ObjectPool<T> { - @Override - public abstract T borrowObject() throws Exception; - - @Override - public abstract void returnObject(T obj) throws Exception; - - @Override - public abstract void invalidateObject(T obj) throws Exception; + private volatile boolean closed = false; /** - * Not supported in this base implementation. + * Not supported in this base implementation. Subclasses should override + * this behavior. * - * @return a negative value. + * @throws UnsupportedOperationException if the pool does not implement this + * method */ @Override - public int getNumIdle() { - return -1; + public void addObject() throws Exception, UnsupportedOperationException { + throw new UnsupportedOperationException(); } /** - * Not supported in this base implementation. + * Throws an {@code IllegalStateException} when this pool has been + * closed. * - * @return a negative value. + * @throws IllegalStateException when this pool has been closed. + * + * @see #isClosed() */ - @Override - public int getNumActive() { - return -1; + protected final void assertOpen() throws IllegalStateException { + if (isClosed()) { + throw new IllegalStateException("Pool not open"); + } } - /** - * Not supported in this base implementation. - * - * @throws UnsupportedOperationException if the pool does not implement this - * method - */ @Override - public void clear() throws Exception, UnsupportedOperationException { - throw new UnsupportedOperationException(); - } + public abstract T borrowObject() throws Exception; /** - * Not supported in this base implementation. Subclasses should override - * this behavior. + * Not supported in this base implementation. * * @throws UnsupportedOperationException if the pool does not implement this * method */ @Override - public void addObject() throws Exception, UnsupportedOperationException { + public void clear() throws Exception, UnsupportedOperationException { throw new UnsupportedOperationException(); } @@ -95,29 +85,39 @@ public abstract class BaseObjectPool<T> extends BaseObject implements ObjectPool } /** - * Has this pool instance been closed. + * Not supported in this base implementation. * - * @return {@code true} when this pool has been closed. + * @return a negative value. */ - public final boolean isClosed() { - return closed; + @Override + public int getNumActive() { + return -1; } /** - * Throws an {@code IllegalStateException} when this pool has been - * closed. + * Not supported in this base implementation. * - * @throws IllegalStateException when this pool has been closed. + * @return a negative value. + */ + @Override + public int getNumIdle() { + return -1; + } + + @Override + public abstract void invalidateObject(T obj) throws Exception; + + /** + * Has this pool instance been closed. * - * @see #isClosed() + * @return {@code true} when this pool has been closed. */ - protected final void assertOpen() throws IllegalStateException { - if (isClosed()) { - throw new IllegalStateException("Pool not open"); - } + public final boolean isClosed() { + return closed; } - private volatile boolean closed = false; + @Override + public abstract void returnObject(T obj) throws Exception; @Override protected void toStringAppendFields(final StringBuilder builder) { diff --git a/src/main/java/org/apache/commons/pool2/BasePooledObjectFactory.java b/src/main/java/org/apache/commons/pool2/BasePooledObjectFactory.java index 18338f6..d65f01c 100644 --- a/src/main/java/org/apache/commons/pool2/BasePooledObjectFactory.java +++ b/src/main/java/org/apache/commons/pool2/BasePooledObjectFactory.java @@ -32,6 +32,16 @@ package org.apache.commons.pool2; */ public abstract class BasePooledObjectFactory<T> extends BaseObject implements PooledObjectFactory<T> { /** + * No-op. + * + * @param p ignored + */ + @Override + public void activateObject(final PooledObject<T> p) throws Exception { + // The default implementation is a no-op. + } + + /** * Creates an object instance, to be wrapped in a {@link PooledObject}. * <p>This method <strong>must</strong> support concurrent, multi-threaded * activation.</p> @@ -44,14 +54,15 @@ public abstract class BasePooledObjectFactory<T> extends BaseObject implements P public abstract T create() throws Exception; /** - * Wrap the provided instance with an implementation of - * {@link PooledObject}. - * - * @param obj the instance to wrap + * No-op. * - * @return The provided instance, wrapped by a {@link PooledObject} + * @param p ignored */ - public abstract PooledObject<T> wrap(T obj); + @Override + public void destroyObject(final PooledObject<T> p) + throws Exception { + // The default implementation is a no-op. + } @Override public PooledObject<T> makeObject() throws Exception { @@ -61,11 +72,11 @@ public abstract class BasePooledObjectFactory<T> extends BaseObject implements P /** * No-op. * - * @param p ignored + * @param p ignored */ @Override - public void destroyObject(final PooledObject<T> p) - throws Exception { + public void passivateObject(final PooledObject<T> p) + throws Exception { // The default implementation is a no-op. } @@ -82,23 +93,12 @@ public abstract class BasePooledObjectFactory<T> extends BaseObject implements P } /** - * No-op. + * Wrap the provided instance with an implementation of + * {@link PooledObject}. * - * @param p ignored - */ - @Override - public void activateObject(final PooledObject<T> p) throws Exception { - // The default implementation is a no-op. - } - - /** - * No-op. + * @param obj the instance to wrap * - * @param p ignored + * @return The provided instance, wrapped by a {@link PooledObject} */ - @Override - public void passivateObject(final PooledObject<T> p) - throws Exception { - // The default implementation is a no-op. - } + public abstract PooledObject<T> wrap(T obj); } diff --git a/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java b/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java index ee822ab..9f4d734 100644 --- a/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java +++ b/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java @@ -77,18 +77,17 @@ package org.apache.commons.pool2; public interface KeyedPooledObjectFactory<K, V> { /** - * Create an instance that can be served by the pool and - * wrap it in a {@link PooledObject} to be managed by the pool. + * Reinitialize an instance to be returned by the pool. * - * @param key the key used when constructing the object + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be activated * - * @return a {@code PooledObject} wrapping an instance that can - * be served by the pool. + * @throws Exception if there is a problem activating {@code obj}, + * this exception may be swallowed by the pool. * - * @throws Exception if there is a problem creating a new instance, - * this will be propagated to the code requesting an object. + * @see #destroyObject */ - PooledObject<V> makeObject(K key) throws Exception; + void activateObject(K key, PooledObject<V> p) throws Exception; /** * Destroy an instance no longer needed by the pool. @@ -134,40 +133,41 @@ public interface KeyedPooledObjectFactory<K, V> { } /** - * Ensures that the instance is safe to be returned by the pool. + * Create an instance that can be served by the pool and + * wrap it in a {@link PooledObject} to be managed by the pool. * - * @param key the key used when selecting the object - * @param p a {@code PooledObject} wrapping the instance to be validated + * @param key the key used when constructing the object * - * @return {@code false} if {@code obj} is not valid and should - * be dropped from the pool, {@code true} otherwise. + * @return a {@code PooledObject} wrapping an instance that can + * be served by the pool. + * + * @throws Exception if there is a problem creating a new instance, + * this will be propagated to the code requesting an object. */ - boolean validateObject(K key, PooledObject<V> p); + PooledObject<V> makeObject(K key) throws Exception; /** - * Reinitialize an instance to be returned by the pool. + * Uninitialize an instance to be returned to the idle object pool. * * @param key the key used when selecting the object - * @param p a {@code PooledObject} wrapping the instance to be activated + * @param p a {@code PooledObject} wrapping the instance to be passivated * - * @throws Exception if there is a problem activating {@code obj}, + * @throws Exception if there is a problem passivating {@code obj}, * this exception may be swallowed by the pool. * * @see #destroyObject */ - void activateObject(K key, PooledObject<V> p) throws Exception; + void passivateObject(K key, PooledObject<V> p) throws Exception; /** - * Uninitialize an instance to be returned to the idle object pool. + * Ensures that the instance is safe to be returned by the pool. * * @param key the key used when selecting the object - * @param p a {@code PooledObject} wrapping the instance to be passivated - * - * @throws Exception if there is a problem passivating {@code obj}, - * this exception may be swallowed by the pool. + * @param p a {@code PooledObject} wrapping the instance to be validated * - * @see #destroyObject + * @return {@code false} if {@code obj} is not valid and should + * be dropped from the pool, {@code true} otherwise. */ - void passivateObject(K key, PooledObject<V> p) throws Exception; + boolean validateObject(K key, PooledObject<V> p); } diff --git a/src/main/java/org/apache/commons/pool2/PoolUtils.java b/src/main/java/org/apache/commons/pool2/PoolUtils.java index b4e614f..70206b8 100644 --- a/src/main/java/org/apache/commons/pool2/PoolUtils.java +++ b/src/main/java/org/apache/commons/pool2/PoolUtils.java @@ -36,597 +36,422 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; */ public final class PoolUtils { - private static final String MSG_FACTOR_NEGATIVE = "factor must be positive."; - private static final String MSG_MIN_IDLE = "minIdle must be non-negative."; - static final String MSG_NULL_KEY = "key must not be null."; - private static final String MSG_NULL_KEYED_POOL = "keyedPool must not be null."; - static final String MSG_NULL_KEYS = "keys must not be null."; - private static final String MSG_NULL_POOL = "pool must not be null."; - /** - * Timer used to periodically check pools idle object count. Because a - * {@link Timer} creates a {@link Thread}, an IODH is used. + * Encapsulate the logic for when the next poolable object should be + * discarded. Each time update is called, the next time to shrink is + * recomputed, based on the float factor, number of idle instances in the + * pool and high water mark. Float factor is assumed to be between 0 and 1. + * Values closer to 1 cause less frequent erosion events. Erosion event + * timing also depends on numIdle. When this value is relatively high (close + * to previously established high water mark), erosion occurs more + * frequently. */ - static class TimerHolder { - static final Timer MIN_IDLE_TIMER = new Timer(true); - } + private static final class ErodingFactor { + /** Determines frequency of "erosion" events */ + private final float factor; - /** - * PoolUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);. - * This constructor is public to permit tools that require a JavaBean - * instance to operate. - */ - public PoolUtils() { - } + /** Time of next shrink event */ + private transient volatile long nextShrinkMillis; - /** - * Should the supplied Throwable be re-thrown (eg if it is an instance of - * one of the Throwables that should never be swallowed). Used by the pool - * error handling for operations that throw exceptions that normally need to - * be ignored. - * - * @param t - * The Throwable to check - * @throws ThreadDeath - * if that is passed in - * @throws VirtualMachineError - * if that is passed in - */ - public static void checkRethrow(final Throwable t) { - if (t instanceof ThreadDeath) { - throw (ThreadDeath) t; + /** High water mark - largest numIdle encountered */ + private transient volatile int idleHighWaterMark; + + /** + * Creates a new ErodingFactor with the given erosion factor. + * + * @param factor + * erosion factor + */ + public ErodingFactor(final float factor) { + this.factor = factor; + nextShrinkMillis = System.currentTimeMillis() + (long) (900000 * factor); // now + // + + // 15 + // min + // * + // factor + idleHighWaterMark = 1; } - if (t instanceof VirtualMachineError) { - throw (VirtualMachineError) t; + + /** + * Returns the time of the next erosion event. + * + * @return next shrink time + */ + public long getNextShrink() { + return nextShrinkMillis; } - // All other instances of Throwable will be silently swallowed - } - /** - * Periodically check the idle object count for the pool. At most one idle - * object will be added per period. If there is an exception when calling - * {@link ObjectPool#addObject()} then no more checks will be performed. - * - * @param pool - * the pool to check periodically. - * @param minIdle - * if the {@link ObjectPool#getNumIdle()} is less than this then - * add an idle object. - * @param period - * the frequency to check the number of idle objects in a pool, - * see {@link Timer#schedule(TimerTask, long, long)}. - * @param <T> the type of objects in the pool - * @return the {@link TimerTask} that will periodically check the pools idle - * object count. - * @throws IllegalArgumentException - * when {@code pool} is {@code null} or when {@code minIdle} is - * negative or when {@code period} isn't valid for - * {@link Timer#schedule(TimerTask, long, long)} - */ - public static <T> TimerTask checkMinIdle(final ObjectPool<T> pool, - final int minIdle, final long period) - throws IllegalArgumentException { - if (pool == null) { - throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "ErodingFactor{" + "factor=" + factor + + ", idleHighWaterMark=" + idleHighWaterMark + '}'; } - if (minIdle < 0) { - throw new IllegalArgumentException(MSG_MIN_IDLE); + + /** + * Updates internal state using the supplied time and numIdle. + * + * @param nowMillis + * current time + * @param numIdle + * number of idle elements in the pool + */ + public void update(final long nowMillis, final int numIdle) { + final int idle = Math.max(0, numIdle); + idleHighWaterMark = Math.max(idle, idleHighWaterMark); + final float maxInterval = 15f; + final float minutes = maxInterval + + ((1f - maxInterval) / idleHighWaterMark) * idle; + nextShrinkMillis = nowMillis + (long) (minutes * 60000f * factor); } - final TimerTask task = new ObjectPoolMinIdleTimerTask<>(pool, minIdle); - getMinIdleTimer().schedule(task, 0L, period); - return task; } - /** - * Periodically check the idle object count for the key in the keyedPool. At - * most one idle object will be added per period. If there is an exception - * when calling {@link KeyedObjectPool#addObject(Object)} then no more - * checks for that key will be performed. + * Decorates a keyed object pool, adding "eroding" behavior. Based on the + * configured erosion factor, objects returning to the pool + * may be invalidated instead of being added to idle capacity. * - * @param keyedPool - * the keyedPool to check periodically. - * @param key - * the key to check the idle count of. - * @param minIdle - * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than - * this then add an idle object. - * @param period - * the frequency to check the number of idle objects in a - * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @return the {@link TimerTask} that will periodically check the pools idle - * object count. - * @throws IllegalArgumentException - * when {@code keyedPool}, {@code key} is {@code null} or - * when {@code minIdle} is negative or when {@code period} isn't - * valid for {@link Timer#schedule(TimerTask, long, long)}. + * @param <K> object pool key type + * @param <V> object pool value type */ - public static <K, V> TimerTask checkMinIdle( - final KeyedObjectPool<K, V> keyedPool, final K key, - final int minIdle, final long period) - throws IllegalArgumentException { - if (keyedPool == null) { - throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); + private static class ErodingKeyedObjectPool<K, V> implements + KeyedObjectPool<K, V> { + + /** Underlying pool */ + private final KeyedObjectPool<K, V> keyedPool; + + /** Erosion factor */ + private final ErodingFactor erodingFactor; + + /** + * Creates an ErodingObjectPool wrapping the given pool using the + * specified erosion factor. + * + * @param keyedPool + * underlying pool - must not be null + * @param erodingFactor + * erosion factor - determines the frequency of erosion + * events + * @see #erodingFactor + */ + protected ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, + final ErodingFactor erodingFactor) { + if (keyedPool == null) { + throw new IllegalArgumentException( + MSG_NULL_KEYED_POOL); + } + this.keyedPool = keyedPool; + this.erodingFactor = erodingFactor; } - if (key == null) { - throw new IllegalArgumentException(MSG_NULL_KEY); + + /** + * Creates an ErodingObjectPool wrapping the given pool using the + * specified erosion factor. + * + * @param keyedPool + * underlying pool + * @param factor + * erosion factor - determines the frequency of erosion + * events + * @see #erodingFactor + */ + public ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, + final float factor) { + this(keyedPool, new ErodingFactor(factor)); } - if (minIdle < 0) { - throw new IllegalArgumentException(MSG_MIN_IDLE); + + /** + * {@inheritDoc} + */ + @Override + public void addObject(final K key) throws Exception, + IllegalStateException, UnsupportedOperationException { + keyedPool.addObject(key); } - final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<>( - keyedPool, key, minIdle); - getMinIdleTimer().schedule(task, 0L, period); - return task; - } - /** - * Periodically check the idle object count for each key in the - * {@code Collection keys} in the keyedPool. At most one idle object will be - * added per period. - * - * @param keyedPool - * the keyedPool to check periodically. - * @param keys - * a collection of keys to check the idle object count. - * @param minIdle - * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than - * this then add an idle object. - * @param period - * the frequency to check the number of idle objects in a - * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @return a {@link Map} of key and {@link TimerTask} pairs that will - * periodically check the pools idle object count. - * @throws IllegalArgumentException - * when {@code keyedPool}, {@code keys}, or any of the values in - * the collection is {@code null} or when {@code minIdle} is - * negative or when {@code period} isn't valid for - * {@link Timer#schedule(TimerTask, long, long)}. - * @see #checkMinIdle(KeyedObjectPool, Object, int, long) - */ - public static <K, V> Map<K, TimerTask> checkMinIdle( - final KeyedObjectPool<K, V> keyedPool, final Collection<K> keys, - final int minIdle, final long period) - throws IllegalArgumentException { - if (keys == null) { - throw new IllegalArgumentException(MSG_NULL_KEYS); - } - final Map<K, TimerTask> tasks = new HashMap<>(keys.size()); - final Iterator<K> iter = keys.iterator(); - while (iter.hasNext()) { - final K key = iter.next(); - final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period); - tasks.put(key, task); + /** + * {@inheritDoc} + */ + @Override + public V borrowObject(final K key) throws Exception, + NoSuchElementException, IllegalStateException { + return keyedPool.borrowObject(key); } - return tasks; - } - /** - * Calls {@link ObjectPool#addObject()} on {@code pool} {@code count} number - * of times. - * - * @param pool - * the pool to prefill. - * @param count - * the number of idle objects to add. - * @param <T> the type of objects in the pool - * @throws Exception - * when {@link ObjectPool#addObject()} fails. - * @throws IllegalArgumentException - * when {@code pool} is {@code null}. - * @deprecated Use {@link ObjectPool#addObjects(int)}. - */ - @Deprecated - public static <T> void prefill(final ObjectPool<T> pool, final int count) - throws Exception, IllegalArgumentException { - if (pool == null) { - throw new IllegalArgumentException(MSG_NULL_POOL); + /** + * {@inheritDoc} + */ + @Override + public void clear() throws Exception, UnsupportedOperationException { + keyedPool.clear(); } - pool.addObjects(count); - } - /** - * Calls {@link KeyedObjectPool#addObject(Object)} on {@code keyedPool} with - * {@code key} {@code count} number of times. - * - * @param keyedPool - * the keyedPool to prefill. - * @param key - * the key to add objects for. - * @param count - * the number of idle objects to add for {@code key}. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @throws Exception - * when {@link KeyedObjectPool#addObject(Object)} fails. - * @throws IllegalArgumentException - * when {@code keyedPool} or {@code key} is {@code null}. - * @deprecated Use {@link KeyedObjectPool#addObjects(Object, int)}. - */ - @Deprecated - public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, - final K key, final int count) throws Exception, - IllegalArgumentException { - if (keyedPool == null) { - throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); + /** + * {@inheritDoc} + */ + @Override + public void clear(final K key) throws Exception, + UnsupportedOperationException { + keyedPool.clear(key); } - keyedPool.addObjects(key, count); - } - /** - * Calls {@link KeyedObjectPool#addObject(Object)} on {@code keyedPool} with - * each key in {@code keys} for {@code count} number of times. This has - * the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)} - * for each key in the {@code keys} collection. - * - * @param keyedPool - * the keyedPool to prefill. - * @param keys - * {@link Collection} of keys to add objects for. - * @param count - * the number of idle objects to add for each {@code key}. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @throws Exception - * when {@link KeyedObjectPool#addObject(Object)} fails. - * @throws IllegalArgumentException - * when {@code keyedPool}, {@code keys}, or any value in - * {@code keys} is {@code null}. - * @see #prefill(KeyedObjectPool, Object, int) - * @deprecated Use {@link KeyedObjectPool#addObjects(Collection, int)}. - */ - @Deprecated - public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, - final Collection<K> keys, final int count) throws Exception, - IllegalArgumentException { - if (keys == null) { - throw new IllegalArgumentException(MSG_NULL_KEYS); + /** + * {@inheritDoc} + */ + @Override + public void close() { + try { + keyedPool.close(); + } catch (final Exception e) { + // swallowed + } } - keyedPool.addObjects(keys, count); - } - /** - * Returns a synchronized (thread-safe) ObjectPool backed by the specified - * ObjectPool. - * <p> - * <b>Note:</b> This should not be used on pool implementations that already - * provide proper synchronization such as the pools provided in the Commons - * Pool library. Wrapping a pool that {@link #wait() waits} for poolable - * objects to be returned before allowing another one to be borrowed with - * another layer of synchronization will cause liveliness issues or a - * deadlock. - * </p> - * - * @param pool - * the ObjectPool to be "wrapped" in a synchronized ObjectPool. - * @param <T> the type of objects in the pool - * @throws IllegalArgumentException - * when {@code pool} is {@code null}. - * @return a synchronized view of the specified ObjectPool. - */ - public static <T> ObjectPool<T> synchronizedPool(final ObjectPool<T> pool) { - if (pool == null) { - throw new IllegalArgumentException(MSG_NULL_POOL); + /** + * Returns the eroding factor for the given key + * + * @param key + * key + * @return eroding factor for the given keyed pool + */ + protected ErodingFactor getErodingFactor(final K key) { + return erodingFactor; } - /* - * assert !(pool instanceof GenericObjectPool) : - * "GenericObjectPool is already thread-safe"; assert !(pool instanceof - * SoftReferenceObjectPool) : - * "SoftReferenceObjectPool is already thread-safe"; assert !(pool - * instanceof StackObjectPool) : - * "StackObjectPool is already thread-safe"; assert - * !"org.apache.commons.pool.composite.CompositeObjectPool" - * .equals(pool.getClass().getName()) : - * "CompositeObjectPools are already thread-safe"; + + /** + * Returns the underlying pool + * + * @return the keyed pool that this ErodingKeyedObjectPool wraps */ - return new SynchronizedObjectPool<>(pool); - } + protected KeyedObjectPool<K, V> getKeyedPool() { + return keyedPool; + } - /** - * Returns a synchronized (thread-safe) KeyedObjectPool backed by the - * specified KeyedObjectPool. - * <p> - * <b>Note:</b> This should not be used on pool implementations that already - * provide proper synchronization such as the pools provided in the Commons - * Pool library. Wrapping a pool that {@link #wait() waits} for poolable - * objects to be returned before allowing another one to be borrowed with - * another layer of synchronization will cause liveliness issues or a - * deadlock. - * </p> - * - * @param keyedPool - * the KeyedObjectPool to be "wrapped" in a synchronized - * KeyedObjectPool. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @return a synchronized view of the specified KeyedObjectPool. - */ - public static <K, V> KeyedObjectPool<K, V> synchronizedPool( - final KeyedObjectPool<K, V> keyedPool) { - /* - * assert !(keyedPool instanceof GenericKeyedObjectPool) : - * "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool - * instanceof StackKeyedObjectPool) : - * "StackKeyedObjectPool is already thread-safe"; assert - * !"org.apache.commons.pool.composite.CompositeKeyedObjectPool" - * .equals(keyedPool.getClass().getName()) : - * "CompositeKeyedObjectPools are already thread-safe"; + /** + * {@inheritDoc} */ - return new SynchronizedKeyedObjectPool<>(keyedPool); - } + @Override + public int getNumActive() { + return keyedPool.getNumActive(); + } - /** - * Returns a synchronized (thread-safe) PooledObjectFactory backed by the - * specified PooledObjectFactory. - * - * @param factory - * the PooledObjectFactory to be "wrapped" in a synchronized - * PooledObjectFactory. - * @param <T> the type of objects in the pool - * @return a synchronized view of the specified PooledObjectFactory. - */ - public static <T> PooledObjectFactory<T> synchronizedPooledFactory( - final PooledObjectFactory<T> factory) { - return new SynchronizedPooledObjectFactory<>(factory); - } + /** + * {@inheritDoc} + */ + @Override + public int getNumActive(final K key) { + return keyedPool.getNumActive(key); + } - /** - * Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by - * the specified KeyedPoolableObjectFactory. - * - * @param keyedFactory - * the KeyedPooledObjectFactory to be "wrapped" in a - * synchronized KeyedPooledObjectFactory. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @return a synchronized view of the specified KeyedPooledObjectFactory. - */ - public static <K, V> KeyedPooledObjectFactory<K, V> synchronizedKeyedPooledFactory( - final KeyedPooledObjectFactory<K, V> keyedFactory) { - return new SynchronizedKeyedPooledObjectFactory<>(keyedFactory); - } + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle() { + return keyedPool.getNumIdle(); + } - /** - * Returns a pool that adaptively decreases its size when idle objects are - * no longer needed. This is intended as an always thread-safe alternative - * to using an idle object evictor provided by many pool implementations. - * This is also an effective way to shrink FIFO ordered pools that - * experience load spikes. - * - * @param pool - * the ObjectPool to be decorated so it shrinks its idle count - * when possible. - * @param <T> the type of objects in the pool - * @throws IllegalArgumentException - * when {@code pool} is {@code null}. - * @return a pool that adaptively decreases its size when idle objects are - * no longer needed. - * @see #erodingPool(ObjectPool, float) - */ - public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool) { - return erodingPool(pool, 1f); - } - - /** - * Returns a pool that adaptively decreases its size when idle objects are - * no longer needed. This is intended as an always thread-safe alternative - * to using an idle object evictor provided by many pool implementations. - * This is also an effective way to shrink FIFO ordered pools that - * experience load spikes. - * <p> - * The factor parameter provides a mechanism to tweak the rate at which the - * pool tries to shrink its size. Values between 0 and 1 cause the pool to - * try to shrink its size more often. Values greater than 1 cause the pool - * to less frequently try to shrink its size. - * </p> - * - * @param pool - * the ObjectPool to be decorated so it shrinks its idle count - * when possible. - * @param factor - * a positive value to scale the rate at which the pool tries to - * reduce its size. If 0 < factor < 1 then the pool - * shrinks more aggressively. If 1 < factor then the pool - * shrinks less aggressively. - * @param <T> the type of objects in the pool - * @throws IllegalArgumentException - * when {@code pool} is {@code null} or when {@code factor} is - * not positive. - * @return a pool that adaptively decreases its size when idle objects are - * no longer needed. - * @see #erodingPool(ObjectPool) - */ - public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool, - final float factor) { - if (pool == null) { - throw new IllegalArgumentException(MSG_NULL_POOL); - } - if (factor <= 0f) { - throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle(final K key) { + return keyedPool.getNumIdle(key); } - return new ErodingObjectPool<>(pool, factor); - } - /** - * Returns a pool that adaptively decreases its size when idle objects are - * no longer needed. This is intended as an always thread-safe alternative - * to using an idle object evictor provided by many pool implementations. - * This is also an effective way to shrink FIFO ordered pools that - * experience load spikes. - * - * @param keyedPool - * the KeyedObjectPool to be decorated so it shrinks its idle - * count when possible. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @throws IllegalArgumentException - * when {@code keyedPool} is {@code null}. - * @return a pool that adaptively decreases its size when idle objects are - * no longer needed. - * @see #erodingPool(KeyedObjectPool, float) - * @see #erodingPool(KeyedObjectPool, float, boolean) - */ - public static <K, V> KeyedObjectPool<K, V> erodingPool( - final KeyedObjectPool<K, V> keyedPool) { - return erodingPool(keyedPool, 1f); - } + /** + * {@inheritDoc} + */ + @Override + public void invalidateObject(final K key, final V obj) { + try { + keyedPool.invalidateObject(key, obj); + } catch (final Exception e) { + // swallowed + } + } - /** - * Returns a pool that adaptively decreases its size when idle objects are - * no longer needed. This is intended as an always thread-safe alternative - * to using an idle object evictor provided by many pool implementations. - * This is also an effective way to shrink FIFO ordered pools that - * experience load spikes. - * <p> - * The factor parameter provides a mechanism to tweak the rate at which the - * pool tries to shrink its size. Values between 0 and 1 cause the pool to - * try to shrink its size more often. Values greater than 1 cause the pool - * to less frequently try to shrink its size. - * </p> - * - * @param keyedPool - * the KeyedObjectPool to be decorated so it shrinks its idle - * count when possible. - * @param factor - * a positive value to scale the rate at which the pool tries to - * reduce its size. If 0 < factor < 1 then the pool - * shrinks more aggressively. If 1 < factor then the pool - * shrinks less aggressively. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @throws IllegalArgumentException - * when {@code keyedPool} is {@code null} or when {@code factor} - * is not positive. - * @return a pool that adaptively decreases its size when idle objects are - * no longer needed. - * @see #erodingPool(KeyedObjectPool, float, boolean) - */ - public static <K, V> KeyedObjectPool<K, V> erodingPool( - final KeyedObjectPool<K, V> keyedPool, final float factor) { - return erodingPool(keyedPool, factor, false); - } + /** + * Returns obj to the pool, unless erosion is triggered, in which case + * obj is invalidated. Erosion is triggered when there are idle + * instances in the pool associated with the given key and more than the + * configured {@link #erodingFactor erosion factor} time has elapsed + * since the last returnObject activation. + * + * @param obj + * object to return or invalidate + * @param key + * key + * @see #erodingFactor + */ + @Override + public void returnObject(final K key, final V obj) throws Exception { + boolean discard = false; + final long nowMillis = System.currentTimeMillis(); + final ErodingFactor factor = getErodingFactor(key); + synchronized (keyedPool) { + if (factor.getNextShrink() < nowMillis) { + final int numIdle = getNumIdle(key); + if (numIdle > 0) { + discard = true; + } - /** - * Returns a pool that adaptively decreases its size when idle objects are - * no longer needed. This is intended as an always thread-safe alternative - * to using an idle object evictor provided by many pool implementations. - * This is also an effective way to shrink FIFO ordered pools that - * experience load spikes. - * <p> - * The factor parameter provides a mechanism to tweak the rate at which the - * pool tries to shrink its size. Values between 0 and 1 cause the pool to - * try to shrink its size more often. Values greater than 1 cause the pool - * to less frequently try to shrink its size. - * </p> - * <p> - * The perKey parameter determines if the pool shrinks on a whole pool basis - * or a per key basis. When perKey is false, the keys do not have an effect - * on the rate at which the pool tries to shrink its size. When perKey is - * true, each key is shrunk independently. - * </p> - * - * @param keyedPool - * the KeyedObjectPool to be decorated so it shrinks its idle - * count when possible. - * @param factor - * a positive value to scale the rate at which the pool tries to - * reduce its size. If 0 < factor < 1 then the pool - * shrinks more aggressively. If 1 < factor then the pool - * shrinks less aggressively. - * @param perKey - * when true, each key is treated independently. - * @param <K> the type of the pool key - * @param <V> the type of pool entries - * @throws IllegalArgumentException - * when {@code keyedPool} is {@code null} or when {@code factor} - * is not positive. - * @return a pool that adaptively decreases its size when idle objects are - * no longer needed. - * @see #erodingPool(KeyedObjectPool) - * @see #erodingPool(KeyedObjectPool, float) - */ - public static <K, V> KeyedObjectPool<K, V> erodingPool( - final KeyedObjectPool<K, V> keyedPool, final float factor, - final boolean perKey) { - if (keyedPool == null) { - throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); - } - if (factor <= 0f) { - throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); - } - if (perKey) { - return new ErodingPerKeyKeyedObjectPool<>(keyedPool, factor); + factor.update(nowMillis, numIdle); + } + } + try { + if (discard) { + keyedPool.invalidateObject(key, obj); + } else { + keyedPool.returnObject(key, obj); + } + } catch (final Exception e) { + // swallowed + } } - return new ErodingKeyedObjectPool<>(keyedPool, factor); - } - /** - * Gets the {@code Timer} for checking keyedPool's idle count. - * - * @return the {@link Timer} for checking keyedPool's idle count. - */ - private static Timer getMinIdleTimer() { - return TimerHolder.MIN_IDLE_TIMER; + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "ErodingKeyedObjectPool{" + "factor=" + + erodingFactor + ", keyedPool=" + keyedPool + '}'; + } } - /** - * Timer task that adds objects to the pool until the number of idle - * instances reaches the configured minIdle. Note that this is not the same - * as the pool's minIdle setting. + * Decorates an object pool, adding "eroding" behavior. Based on the + * configured {@link #factor erosion factor}, objects returning to the pool + * may be invalidated instead of being added to idle capacity. * * @param <T> type of objects in the pool */ - private static final class ObjectPoolMinIdleTimerTask<T> extends TimerTask { - - /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ - private final int minIdle; + private static class ErodingObjectPool<T> implements ObjectPool<T> { - /** Object pool */ + /** Underlying object pool */ private final ObjectPool<T> pool; + /** Erosion factor */ + private final ErodingFactor factor; + /** - * Create a new ObjectPoolMinIdleTimerTask for the given pool with the - * given minIdle setting. + * Creates an ErodingObjectPool wrapping the given pool using the + * specified erosion factor. * * @param pool - * object pool - * @param minIdle - * number of idle instances to maintain - * @throws IllegalArgumentException - * if the pool is null + * underlying pool + * @param factor + * erosion factor - determines the frequency of erosion + * events + * @see #factor */ - ObjectPoolMinIdleTimerTask(final ObjectPool<T> pool, final int minIdle) - throws IllegalArgumentException { - if (pool == null) { - throw new IllegalArgumentException(MSG_NULL_POOL); - } + public ErodingObjectPool(final ObjectPool<T> pool, final float factor) { this.pool = pool; - this.minIdle = minIdle; + this.factor = new ErodingFactor(factor); } /** * {@inheritDoc} */ @Override - public void run() { - boolean success = false; + public void addObject() throws Exception, IllegalStateException, + UnsupportedOperationException { + pool.addObject(); + } + + /** + * {@inheritDoc} + */ + @Override + public T borrowObject() throws Exception, NoSuchElementException, + IllegalStateException { + return pool.borrowObject(); + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() throws Exception, UnsupportedOperationException { + pool.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public void close() { try { - if (pool.getNumIdle() < minIdle) { - pool.addObject(); - } - success = true; + pool.close(); + } catch (final Exception e) { + // swallowed + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumActive() { + return pool.getNumActive(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle() { + return pool.getNumIdle(); + } + /** + * {@inheritDoc} + */ + @Override + public void invalidateObject(final T obj) { + try { + pool.invalidateObject(obj); } catch (final Exception e) { - cancel(); - } finally { - // detect other types of Throwable and cancel this Timer - if (!success) { - cancel(); + // swallowed + } + } + + /** + * Returns obj to the pool, unless erosion is triggered, in which case + * obj is invalidated. Erosion is triggered when there are idle + * instances in the pool and more than the {@link #factor erosion + * factor}-determined time has elapsed since the last returnObject + * activation. + * + * @param obj + * object to return or invalidate + * @see #factor + */ + @Override + public void returnObject(final T obj) { + boolean discard = false; + final long nowMillis = System.currentTimeMillis(); + synchronized (pool) { + if (factor.getNextShrink() < nowMillis) { // XXX: Pool 3: move test + // out of sync block + final int numIdle = pool.getNumIdle(); + if (numIdle > 0) { + discard = true; + } + + factor.update(nowMillis, numIdle); + } + } + try { + if (discard) { + pool.invalidateObject(obj); + } else { + pool.returnObject(obj); } + } catch (final Exception e) { + // swallowed } } @@ -635,15 +460,67 @@ public final class PoolUtils { */ @Override public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("ObjectPoolMinIdleTimerTask"); - sb.append("{minIdle=").append(minIdle); - sb.append(", pool=").append(pool); - sb.append('}'); - return sb.toString(); + return "ErodingObjectPool{" + "factor=" + factor + ", pool=" + + pool + '}'; } } + /** + * Extends ErodingKeyedObjectPool to allow erosion to take place on a + * per-key basis. Timing of erosion events is tracked separately for + * separate keyed pools. + * + * @param <K> object pool key type + * @param <V> object pool value type + */ + private static final class ErodingPerKeyKeyedObjectPool<K, V> extends + ErodingKeyedObjectPool<K, V> { + + /** Erosion factor - same for all pools */ + private final float factor; + + /** Map of ErodingFactor instances keyed on pool keys */ + private final Map<K, ErodingFactor> factors = Collections.synchronizedMap(new HashMap<K, ErodingFactor>()); + + /** + * Creates a new ErordingPerKeyKeyedObjectPool decorating the given keyed + * pool with the specified erosion factor. + * + * @param keyedPool + * underlying keyed pool + * @param factor + * erosion factor + */ + public ErodingPerKeyKeyedObjectPool( + final KeyedObjectPool<K, V> keyedPool, final float factor) { + super(keyedPool, null); + this.factor = factor; + } + + /** + * {@inheritDoc} + */ + @Override + protected ErodingFactor getErodingFactor(final K key) { + ErodingFactor eFactor = factors.get(key); + // this may result in two ErodingFactors being created for a key + // since they are small and cheap this is okay. + if (eFactor == null) { + eFactor = new ErodingFactor(this.factor); + factors.put(key, eFactor); + } + return eFactor; + } + /** + * {@inheritDoc} + */ + @SuppressWarnings("resource") // getKeyedPool(): ivar access + @Override + public String toString() { + return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor + + ", keyedPool=" + getKeyedPool() + '}'; + } + } /** * Timer task that adds objects to the pool until the number of idle * instances for the given key reaches the configured minIdle. Note that @@ -724,166 +601,60 @@ public final class PoolUtils { return sb.toString(); } } - /** - * A synchronized (thread-safe) ObjectPool backed by the specified - * ObjectPool. - * <p> - * <b>Note:</b> This should not be used on pool implementations that already - * provide proper synchronization such as the pools provided in the Commons - * Pool library. Wrapping a pool that {@link #wait() waits} for poolable - * objects to be returned before allowing another one to be borrowed with - * another layer of synchronization will cause liveliness issues or a - * deadlock. - * </p> + * Timer task that adds objects to the pool until the number of idle + * instances reaches the configured minIdle. Note that this is not the same + * as the pool's minIdle setting. * * @param <T> type of objects in the pool */ - private static final class SynchronizedObjectPool<T> implements ObjectPool<T> { + private static final class ObjectPoolMinIdleTimerTask<T> extends TimerTask { - /** - * Object whose monitor is used to synchronize methods on the wrapped - * pool. - */ - private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ + private final int minIdle; - /** the underlying object pool */ + /** Object pool */ private final ObjectPool<T> pool; /** - * Creates a new SynchronizedObjectPool wrapping the given pool. + * Create a new ObjectPoolMinIdleTimerTask for the given pool with the + * given minIdle setting. * * @param pool - * the ObjectPool to be "wrapped" in a synchronized - * ObjectPool. + * object pool + * @param minIdle + * number of idle instances to maintain * @throws IllegalArgumentException * if the pool is null */ - SynchronizedObjectPool(final ObjectPool<T> pool) + ObjectPoolMinIdleTimerTask(final ObjectPool<T> pool, final int minIdle) throws IllegalArgumentException { if (pool == null) { throw new IllegalArgumentException(MSG_NULL_POOL); } this.pool = pool; + this.minIdle = minIdle; } /** * {@inheritDoc} */ @Override - public T borrowObject() throws Exception, NoSuchElementException, - IllegalStateException { - final WriteLock writeLock = readWriteLock.writeLock(); - writeLock.lock(); + public void run() { + boolean success = false; try { - return pool.borrowObject(); - } finally { - writeLock.unlock(); - } - } + if (pool.getNumIdle() < minIdle) { + pool.addObject(); + } + success = true; - /** - * {@inheritDoc} - */ - @Override - public void returnObject(final T obj) { - final WriteLock writeLock = readWriteLock.writeLock(); - writeLock.lock(); - try { - pool.returnObject(obj); } catch (final Exception e) { - // swallowed as of Pool 2 + cancel(); } finally { - writeLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void invalidateObject(final T obj) { - final WriteLock writeLock = readWriteLock.writeLock(); - writeLock.lock(); - try { - pool.invalidateObject(obj); - } catch (final Exception e) { - // swallowed as of Pool 2 - } finally { - writeLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void addObject() throws Exception, IllegalStateException, - UnsupportedOperationException { - final WriteLock writeLock = readWriteLock.writeLock(); - writeLock.lock(); - try { - pool.addObject(); - } finally { - writeLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getNumIdle() { - final ReadLock readLock = readWriteLock.readLock(); - readLock.lock(); - try { - return pool.getNumIdle(); - } finally { - readLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getNumActive() { - final ReadLock readLock = readWriteLock.readLock(); - readLock.lock(); - try { - return pool.getNumActive(); - } finally { - readLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void clear() throws Exception, UnsupportedOperationException { - final WriteLock writeLock = readWriteLock.writeLock(); - writeLock.lock(); - try { - pool.clear(); - } finally { - writeLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void close() { - final WriteLock writeLock = readWriteLock.writeLock(); - writeLock.lock(); - try { - pool.close(); - } catch (final Exception e) { - // swallowed as of Pool 2 - } finally { - writeLock.unlock(); + // detect other types of Throwable and cancel this Timer + if (!success) { + cancel(); + } } } @@ -893,8 +664,9 @@ public final class PoolUtils { @Override public String toString() { final StringBuilder sb = new StringBuilder(); - sb.append("SynchronizedObjectPool"); - sb.append("{pool=").append(pool); + sb.append("ObjectPoolMinIdleTimerTask"); + sb.append("{minIdle=").append(minIdle); + sb.append(", pool=").append(pool); sb.append('}'); return sb.toString(); } @@ -948,6 +720,21 @@ public final class PoolUtils { * {@inheritDoc} */ @Override + public void addObject(final K key) throws Exception, + IllegalStateException, UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + keyedPool.addObject(key); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException { final WriteLock writeLock = readWriteLock.writeLock(); @@ -963,13 +750,11 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void returnObject(final K key, final V obj) { + public void clear() throws Exception, UnsupportedOperationException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - keyedPool.returnObject(key, obj); - } catch (final Exception e) { - // swallowed + keyedPool.clear(); } finally { writeLock.unlock(); } @@ -979,13 +764,12 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void invalidateObject(final K key, final V obj) { + public void clear(final K key) throws Exception, + UnsupportedOperationException { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - keyedPool.invalidateObject(key, obj); - } catch (final Exception e) { - // swallowed as of Pool 2 + keyedPool.clear(key); } finally { writeLock.unlock(); } @@ -995,12 +779,13 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void addObject(final K key) throws Exception, - IllegalStateException, UnsupportedOperationException { + public void close() { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - keyedPool.addObject(key); + keyedPool.close(); + } catch (final Exception e) { + // swallowed as of Pool 2 } finally { writeLock.unlock(); } @@ -1010,11 +795,11 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public int getNumIdle(final K key) { + public int getNumActive() { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { - return keyedPool.getNumIdle(key); + return keyedPool.getNumActive(); } finally { readLock.unlock(); } @@ -1052,11 +837,11 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public int getNumActive() { + public int getNumIdle(final K key) { final ReadLock readLock = readWriteLock.readLock(); readLock.lock(); try { - return keyedPool.getNumActive(); + return keyedPool.getNumIdle(key); } finally { readLock.unlock(); } @@ -1066,26 +851,13 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void clear() throws Exception, UnsupportedOperationException { - final WriteLock writeLock = readWriteLock.writeLock(); - writeLock.lock(); - try { - keyedPool.clear(); - } finally { - writeLock.unlock(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void clear(final K key) throws Exception, - UnsupportedOperationException { + public void invalidateObject(final K key, final V obj) { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - keyedPool.clear(key); + keyedPool.invalidateObject(key, obj); + } catch (final Exception e) { + // swallowed as of Pool 2 } finally { writeLock.unlock(); } @@ -1095,13 +867,13 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void close() { + public void returnObject(final K key, final V obj) { final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - keyedPool.close(); + keyedPool.returnObject(key, obj); } catch (final Exception e) { - // swallowed as of Pool 2 + // swallowed } finally { writeLock.unlock(); } @@ -1121,8 +893,8 @@ public final class PoolUtils { } /** - * A fully synchronized PooledObjectFactory that wraps a - * PooledObjectFactory and synchronizes access to the wrapped factory + * A fully synchronized KeyedPooledObjectFactory that wraps a + * KeyedPooledObjectFactory and synchronizes access to the wrapped factory * methods. * <p> * <b>Note:</b> This should not be used on pool implementations that already @@ -1130,42 +902,45 @@ public final class PoolUtils { * Pool library. * </p> * - * @param <T> pooled object factory type + * @param <K> pooled object factory key type + * @param <V> pooled object factory key value */ - private static final class SynchronizedPooledObjectFactory<T> implements - PooledObjectFactory<T> { + private static final class SynchronizedKeyedPooledObjectFactory<K, V> + implements KeyedPooledObjectFactory<K, V> { /** Synchronization lock */ private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); /** Wrapped factory */ - private final PooledObjectFactory<T> factory; + private final KeyedPooledObjectFactory<K, V> keyedFactory; /** - * Creates a SynchronizedPoolableObjectFactory wrapping the given + * Creates a SynchronizedKeyedPoolableObjectFactory wrapping the given * factory. * - * @param factory + * @param keyedFactory * underlying factory to wrap * @throws IllegalArgumentException * if the factory is null */ - SynchronizedPooledObjectFactory(final PooledObjectFactory<T> factory) + SynchronizedKeyedPooledObjectFactory( + final KeyedPooledObjectFactory<K, V> keyedFactory) throws IllegalArgumentException { - if (factory == null) { - throw new IllegalArgumentException("factory must not be null."); + if (keyedFactory == null) { + throw new IllegalArgumentException( + "keyedFactory must not be null."); } - this.factory = factory; + this.keyedFactory = keyedFactory; } /** * {@inheritDoc} */ @Override - public PooledObject<T> makeObject() throws Exception { + public void activateObject(final K key, final PooledObject<V> p) throws Exception { writeLock.lock(); try { - return factory.makeObject(); + keyedFactory.activateObject(key, p); } finally { writeLock.unlock(); } @@ -1175,10 +950,10 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void destroyObject(final PooledObject<T> p) throws Exception { + public void destroyObject(final K key, final PooledObject<V> p) throws Exception { writeLock.lock(); try { - factory.destroyObject(p); + keyedFactory.destroyObject(key, p); } finally { writeLock.unlock(); } @@ -1188,10 +963,10 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public boolean validateObject(final PooledObject<T> p) { + public PooledObject<V> makeObject(final K key) throws Exception { writeLock.lock(); try { - return factory.validateObject(p); + return keyedFactory.makeObject(key); } finally { writeLock.unlock(); } @@ -1201,10 +976,10 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void activateObject(final PooledObject<T> p) throws Exception { + public void passivateObject(final K key, final PooledObject<V> p) throws Exception { writeLock.lock(); try { - factory.activateObject(p); + keyedFactory.passivateObject(key, p); } finally { writeLock.unlock(); } @@ -1214,77 +989,80 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void passivateObject(final PooledObject<T> p) throws Exception { - writeLock.lock(); - try { - factory.passivateObject(p); - } finally { - writeLock.unlock(); - } + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("SynchronizedKeyedPoolableObjectFactory"); + sb.append("{keyedFactory=").append(keyedFactory); + sb.append('}'); + return sb.toString(); } /** * {@inheritDoc} */ @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("SynchronizedPoolableObjectFactory"); - sb.append("{factory=").append(factory); - sb.append('}'); - return sb.toString(); + public boolean validateObject(final K key, final PooledObject<V> p) { + writeLock.lock(); + try { + return keyedFactory.validateObject(key, p); + } finally { + writeLock.unlock(); + } } } /** - * A fully synchronized KeyedPooledObjectFactory that wraps a - * KeyedPooledObjectFactory and synchronizes access to the wrapped factory - * methods. + * A synchronized (thread-safe) ObjectPool backed by the specified + * ObjectPool. * <p> * <b>Note:</b> This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons - * Pool library. + * Pool library. Wrapping a pool that {@link #wait() waits} for poolable + * objects to be returned before allowing another one to be borrowed with + * another layer of synchronization will cause liveliness issues or a + * deadlock. * </p> * - * @param <K> pooled object factory key type - * @param <V> pooled object factory key value + * @param <T> type of objects in the pool */ - private static final class SynchronizedKeyedPooledObjectFactory<K, V> - implements KeyedPooledObjectFactory<K, V> { + private static final class SynchronizedObjectPool<T> implements ObjectPool<T> { - /** Synchronization lock */ - private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); + /** + * Object whose monitor is used to synchronize methods on the wrapped + * pool. + */ + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - /** Wrapped factory */ - private final KeyedPooledObjectFactory<K, V> keyedFactory; + /** the underlying object pool */ + private final ObjectPool<T> pool; /** - * Creates a SynchronizedKeyedPoolableObjectFactory wrapping the given - * factory. + * Creates a new SynchronizedObjectPool wrapping the given pool. * - * @param keyedFactory - * underlying factory to wrap + * @param pool + * the ObjectPool to be "wrapped" in a synchronized + * ObjectPool. * @throws IllegalArgumentException - * if the factory is null + * if the pool is null */ - SynchronizedKeyedPooledObjectFactory( - final KeyedPooledObjectFactory<K, V> keyedFactory) + SynchronizedObjectPool(final ObjectPool<T> pool) throws IllegalArgumentException { - if (keyedFactory == null) { - throw new IllegalArgumentException( - "keyedFactory must not be null."); + if (pool == null) { + throw new IllegalArgumentException(MSG_NULL_POOL); } - this.keyedFactory = keyedFactory; + this.pool = pool; } /** * {@inheritDoc} */ @Override - public PooledObject<V> makeObject(final K key) throws Exception { + public void addObject() throws Exception, IllegalStateException, + UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - return keyedFactory.makeObject(key); + pool.addObject(); } finally { writeLock.unlock(); } @@ -1294,10 +1072,12 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void destroyObject(final K key, final PooledObject<V> p) throws Exception { + public T borrowObject() throws Exception, NoSuchElementException, + IllegalStateException { + final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - keyedFactory.destroyObject(key, p); + return pool.borrowObject(); } finally { writeLock.unlock(); } @@ -1307,10 +1087,11 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public boolean validateObject(final K key, final PooledObject<V> p) { + public void clear() throws Exception, UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - return keyedFactory.validateObject(key, p); + pool.clear(); } finally { writeLock.unlock(); } @@ -1320,10 +1101,13 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void activateObject(final K key, final PooledObject<V> p) throws Exception { + public void close() { + final WriteLock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { - keyedFactory.activateObject(key, p); + pool.close(); + } catch (final Exception e) { + // swallowed as of Pool 2 } finally { writeLock.unlock(); } @@ -1333,12 +1117,13 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void passivateObject(final K key, final PooledObject<V> p) throws Exception { - writeLock.lock(); + public int getNumActive() { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); try { - keyedFactory.passivateObject(key, p); + return pool.getNumActive(); } finally { - writeLock.unlock(); + readLock.unlock(); } } @@ -1346,76 +1131,46 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("SynchronizedKeyedPoolableObjectFactory"); - sb.append("{keyedFactory=").append(keyedFactory); - sb.append('}'); - return sb.toString(); - } - } - - /** - * Encapsulate the logic for when the next poolable object should be - * discarded. Each time update is called, the next time to shrink is - * recomputed, based on the float factor, number of idle instances in the - * pool and high water mark. Float factor is assumed to be between 0 and 1. - * Values closer to 1 cause less frequent erosion events. Erosion event - * timing also depends on numIdle. When this value is relatively high (close - * to previously established high water mark), erosion occurs more - * frequently. - */ - private static final class ErodingFactor { - /** Determines frequency of "erosion" events */ - private final float factor; - - /** Time of next shrink event */ - private transient volatile long nextShrinkMillis; - - /** High water mark - largest numIdle encountered */ - private transient volatile int idleHighWaterMark; - - /** - * Creates a new ErodingFactor with the given erosion factor. - * - * @param factor - * erosion factor - */ - public ErodingFactor(final float factor) { - this.factor = factor; - nextShrinkMillis = System.currentTimeMillis() + (long) (900000 * factor); // now - // + - // 15 - // min - // * - // factor - idleHighWaterMark = 1; + public int getNumIdle() { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); + try { + return pool.getNumIdle(); + } finally { + readLock.unlock(); + } } /** - * Updates internal state using the supplied time and numIdle. - * - * @param nowMillis - * current time - * @param numIdle - * number of idle elements in the pool + * {@inheritDoc} */ - public void update(final long nowMillis, final int numIdle) { - final int idle = Math.max(0, numIdle); - idleHighWaterMark = Math.max(idle, idleHighWaterMark); - final float maxInterval = 15f; - final float minutes = maxInterval + - ((1f - maxInterval) / idleHighWaterMark) * idle; - nextShrinkMillis = nowMillis + (long) (minutes * 60000f * factor); + @Override + public void invalidateObject(final T obj) { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + pool.invalidateObject(obj); + } catch (final Exception e) { + // swallowed as of Pool 2 + } finally { + writeLock.unlock(); + } } /** - * Returns the time of the next erosion event. - * - * @return next shrink time + * {@inheritDoc} */ - public long getNextShrink() { - return nextShrinkMillis; + @Override + public void returnObject(final T obj) { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + pool.returnObject(obj); + } catch (final Exception e) { + // swallowed as of Pool 2 + } finally { + writeLock.unlock(); + } } /** @@ -1423,85 +1178,75 @@ public final class PoolUtils { */ @Override public String toString() { - return "ErodingFactor{" + "factor=" + factor + - ", idleHighWaterMark=" + idleHighWaterMark + '}'; + final StringBuilder sb = new StringBuilder(); + sb.append("SynchronizedObjectPool"); + sb.append("{pool=").append(pool); + sb.append('}'); + return sb.toString(); } } /** - * Decorates an object pool, adding "eroding" behavior. Based on the - * configured {@link #factor erosion factor}, objects returning to the pool - * may be invalidated instead of being added to idle capacity. + * A fully synchronized PooledObjectFactory that wraps a + * PooledObjectFactory and synchronizes access to the wrapped factory + * methods. + * <p> + * <b>Note:</b> This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. + * </p> * - * @param <T> type of objects in the pool + * @param <T> pooled object factory type */ - private static class ErodingObjectPool<T> implements ObjectPool<T> { + private static final class SynchronizedPooledObjectFactory<T> implements + PooledObjectFactory<T> { - /** Underlying object pool */ - private final ObjectPool<T> pool; + /** Synchronization lock */ + private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); - /** Erosion factor */ - private final ErodingFactor factor; + /** Wrapped factory */ + private final PooledObjectFactory<T> factory; /** - * Creates an ErodingObjectPool wrapping the given pool using the - * specified erosion factor. + * Creates a SynchronizedPoolableObjectFactory wrapping the given + * factory. * - * @param pool - * underlying pool - * @param factor - * erosion factor - determines the frequency of erosion - * events - * @see #factor + * @param factory + * underlying factory to wrap + * @throws IllegalArgumentException + * if the factory is null */ - public ErodingObjectPool(final ObjectPool<T> pool, final float factor) { - this.pool = pool; - this.factor = new ErodingFactor(factor); + SynchronizedPooledObjectFactory(final PooledObjectFactory<T> factory) + throws IllegalArgumentException { + if (factory == null) { + throw new IllegalArgumentException("factory must not be null."); + } + this.factory = factory; } /** * {@inheritDoc} */ @Override - public T borrowObject() throws Exception, NoSuchElementException, - IllegalStateException { - return pool.borrowObject(); + public void activateObject(final PooledObject<T> p) throws Exception { + writeLock.lock(); + try { + factory.activateObject(p); + } finally { + writeLock.unlock(); + } } /** - * Returns obj to the pool, unless erosion is triggered, in which case - * obj is invalidated. Erosion is triggered when there are idle - * instances in the pool and more than the {@link #factor erosion - * factor}-determined time has elapsed since the last returnObject - * activation. - * - * @param obj - * object to return or invalidate - * @see #factor + * {@inheritDoc} */ @Override - public void returnObject(final T obj) { - boolean discard = false; - final long nowMillis = System.currentTimeMillis(); - synchronized (pool) { - if (factor.getNextShrink() < nowMillis) { // XXX: Pool 3: move test - // out of sync block - final int numIdle = pool.getNumIdle(); - if (numIdle > 0) { - discard = true; - } - - factor.update(nowMillis, numIdle); - } - } + public void destroyObject(final PooledObject<T> p) throws Exception { + writeLock.lock(); try { - if (discard) { - pool.invalidateObject(obj); - } else { - pool.returnObject(obj); - } - } catch (final Exception e) { - // swallowed + factory.destroyObject(p); + } finally { + writeLock.unlock(); } } @@ -1509,11 +1254,12 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void invalidateObject(final T obj) { + public PooledObject<T> makeObject() throws Exception { + writeLock.lock(); try { - pool.invalidateObject(obj); - } catch (final Exception e) { - // swallowed + return factory.makeObject(); + } finally { + writeLock.unlock(); } } @@ -1521,326 +1267,580 @@ public final class PoolUtils { * {@inheritDoc} */ @Override - public void addObject() throws Exception, IllegalStateException, - UnsupportedOperationException { - pool.addObject(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getNumIdle() { - return pool.getNumIdle(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getNumActive() { - return pool.getNumActive(); + public void passivateObject(final PooledObject<T> p) throws Exception { + writeLock.lock(); + try { + factory.passivateObject(p); + } finally { + writeLock.unlock(); + } } /** * {@inheritDoc} */ @Override - public void clear() throws Exception, UnsupportedOperationException { - pool.clear(); + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("SynchronizedPoolableObjectFactory"); + sb.append("{factory=").append(factory); + sb.append('}'); + return sb.toString(); } /** * {@inheritDoc} */ @Override - public void close() { + public boolean validateObject(final PooledObject<T> p) { + writeLock.lock(); try { - pool.close(); - } catch (final Exception e) { - // swallowed + return factory.validateObject(p); + } finally { + writeLock.unlock(); } } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "ErodingObjectPool{" + "factor=" + factor + ", pool=" + - pool + '}'; - } } /** - * Decorates a keyed object pool, adding "eroding" behavior. Based on the - * configured erosion factor, objects returning to the pool - * may be invalidated instead of being added to idle capacity. - * - * @param <K> object pool key type - * @param <V> object pool value type + * Timer used to periodically check pools idle object count. Because a + * {@link Timer} creates a {@link Thread}, an IODH is used. */ - private static class ErodingKeyedObjectPool<K, V> implements - KeyedObjectPool<K, V> { + static class TimerHolder { + static final Timer MIN_IDLE_TIMER = new Timer(true); + } - /** Underlying pool */ - private final KeyedObjectPool<K, V> keyedPool; + private static final String MSG_FACTOR_NEGATIVE = "factor must be positive."; - /** Erosion factor */ - private final ErodingFactor erodingFactor; + private static final String MSG_MIN_IDLE = "minIdle must be non-negative."; - /** - * Creates an ErodingObjectPool wrapping the given pool using the - * specified erosion factor. - * - * @param keyedPool - * underlying pool - * @param factor - * erosion factor - determines the frequency of erosion - * events - * @see #erodingFactor - */ - public ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, - final float factor) { - this(keyedPool, new ErodingFactor(factor)); - } + static final String MSG_NULL_KEY = "key must not be null."; - /** - * Creates an ErodingObjectPool wrapping the given pool using the - * specified erosion factor. - * - * @param keyedPool - * underlying pool - must not be null - * @param erodingFactor - * erosion factor - determines the frequency of erosion - * events - * @see #erodingFactor - */ - protected ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, - final ErodingFactor erodingFactor) { - if (keyedPool == null) { - throw new IllegalArgumentException( - MSG_NULL_KEYED_POOL); - } - this.keyedPool = keyedPool; - this.erodingFactor = erodingFactor; - } + private static final String MSG_NULL_KEYED_POOL = "keyedPool must not be null."; - /** - * {@inheritDoc} - */ - @Override - public V borrowObject(final K key) throws Exception, - NoSuchElementException, IllegalStateException { - return keyedPool.borrowObject(key); - } + static final String MSG_NULL_KEYS = "keys must not be null."; - /** - * Returns obj to the pool, unless erosion is triggered, in which case - * obj is invalidated. Erosion is triggered when there are idle - * instances in the pool associated with the given key and more than the - * configured {@link #erodingFactor erosion factor} time has elapsed - * since the last returnObject activation. - * - * @param obj - * object to return or invalidate - * @param key - * key - * @see #erodingFactor - */ - @Override - public void returnObject(final K key, final V obj) throws Exception { - boolean discard = false; - final long nowMillis = System.currentTimeMillis(); - final ErodingFactor factor = getErodingFactor(key); - synchronized (keyedPool) { - if (factor.getNextShrink() < nowMillis) { - final int numIdle = getNumIdle(key); - if (numIdle > 0) { - discard = true; - } + private static final String MSG_NULL_POOL = "pool must not be null."; - factor.update(nowMillis, numIdle); - } - } - try { - if (discard) { - keyedPool.invalidateObject(key, obj); - } else { - keyedPool.returnObject(key, obj); - } - } catch (final Exception e) { - // swallowed - } + /** + * Periodically check the idle object count for each key in the + * {@code Collection keys} in the keyedPool. At most one idle object will be + * added per period. + * + * @param keyedPool + * the keyedPool to check periodically. + * @param keys + * a collection of keys to check the idle object count. + * @param minIdle + * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than + * this then add an idle object. + * @param period + * the frequency to check the number of idle objects in a + * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @return a {@link Map} of key and {@link TimerTask} pairs that will + * periodically check the pools idle object count. + * @throws IllegalArgumentException + * when {@code keyedPool}, {@code keys}, or any of the values in + * the collection is {@code null} or when {@code minIdle} is + * negative or when {@code period} isn't valid for + * {@link Timer#schedule(TimerTask, long, long)}. + * @see #checkMinIdle(KeyedObjectPool, Object, int, long) + */ + public static <K, V> Map<K, TimerTask> checkMinIdle( + final KeyedObjectPool<K, V> keyedPool, final Collection<K> keys, + final int minIdle, final long period) + throws IllegalArgumentException { + if (keys == null) { + throw new IllegalArgumentException(MSG_NULL_KEYS); } - - /** - * Returns the eroding factor for the given key - * - * @param key - * key - * @return eroding factor for the given keyed pool - */ - protected ErodingFactor getErodingFactor(final K key) { - return erodingFactor; + final Map<K, TimerTask> tasks = new HashMap<>(keys.size()); + final Iterator<K> iter = keys.iterator(); + while (iter.hasNext()) { + final K key = iter.next(); + final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period); + tasks.put(key, task); } + return tasks; + } - /** - * {@inheritDoc} - */ - @Override - public void invalidateObject(final K key, final V obj) { - try { - keyedPool.invalidateObject(key, obj); - } catch (final Exception e) { - // swallowed - } + /** + * Periodically check the idle object count for the key in the keyedPool. At + * most one idle object will be added per period. If there is an exception + * when calling {@link KeyedObjectPool#addObject(Object)} then no more + * checks for that key will be performed. + * + * @param keyedPool + * the keyedPool to check periodically. + * @param key + * the key to check the idle count of. + * @param minIdle + * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than + * this then add an idle object. + * @param period + * the frequency to check the number of idle objects in a + * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @return the {@link TimerTask} that will periodically check the pools idle + * object count. + * @throws IllegalArgumentException + * when {@code keyedPool}, {@code key} is {@code null} or + * when {@code minIdle} is negative or when {@code period} isn't + * valid for {@link Timer#schedule(TimerTask, long, long)}. + */ + public static <K, V> TimerTask checkMinIdle( + final KeyedObjectPool<K, V> keyedPool, final K key, + final int minIdle, final long period) + throws IllegalArgumentException { + if (keyedPool == null) { + throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); } - - /** - * {@inheritDoc} - */ - @Override - public void addObject(final K key) throws Exception, - IllegalStateException, UnsupportedOperationException { - keyedPool.addObject(key); + if (key == null) { + throw new IllegalArgumentException(MSG_NULL_KEY); } - - /** - * {@inheritDoc} - */ - @Override - public int getNumIdle() { - return keyedPool.getNumIdle(); + if (minIdle < 0) { + throw new IllegalArgumentException(MSG_MIN_IDLE); } + final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<>( + keyedPool, key, minIdle); + getMinIdleTimer().schedule(task, 0L, period); + return task; + } - /** - * {@inheritDoc} - */ - @Override - public int getNumIdle(final K key) { - return keyedPool.getNumIdle(key); + /** + * Periodically check the idle object count for the pool. At most one idle + * object will be added per period. If there is an exception when calling + * {@link ObjectPool#addObject()} then no more checks will be performed. + * + * @param pool + * the pool to check periodically. + * @param minIdle + * if the {@link ObjectPool#getNumIdle()} is less than this then + * add an idle object. + * @param period + * the frequency to check the number of idle objects in a pool, + * see {@link Timer#schedule(TimerTask, long, long)}. + * @param <T> the type of objects in the pool + * @return the {@link TimerTask} that will periodically check the pools idle + * object count. + * @throws IllegalArgumentException + * when {@code pool} is {@code null} or when {@code minIdle} is + * negative or when {@code period} isn't valid for + * {@link Timer#schedule(TimerTask, long, long)} + */ + public static <T> TimerTask checkMinIdle(final ObjectPool<T> pool, + final int minIdle, final long period) + throws IllegalArgumentException { + if (pool == null) { + throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); + } + if (minIdle < 0) { + throw new IllegalArgumentException(MSG_MIN_IDLE); } + final TimerTask task = new ObjectPoolMinIdleTimerTask<>(pool, minIdle); + getMinIdleTimer().schedule(task, 0L, period); + return task; + } - /** - * {@inheritDoc} - */ - @Override - public int getNumActive() { - return keyedPool.getNumActive(); + /** + * Should the supplied Throwable be re-thrown (eg if it is an instance of + * one of the Throwables that should never be swallowed). Used by the pool + * error handling for operations that throw exceptions that normally need to + * be ignored. + * + * @param t + * The Throwable to check + * @throws ThreadDeath + * if that is passed in + * @throws VirtualMachineError + * if that is passed in + */ + public static void checkRethrow(final Throwable t) { + if (t instanceof ThreadDeath) { + throw (ThreadDeath) t; + } + if (t instanceof VirtualMachineError) { + throw (VirtualMachineError) t; + } + // All other instances of Throwable will be silently swallowed + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + * + * @param keyedPool + * the KeyedObjectPool to be decorated so it shrinks its idle + * count when possible. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @throws IllegalArgumentException + * when {@code keyedPool} is {@code null}. + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(KeyedObjectPool, float) + * @see #erodingPool(KeyedObjectPool, float, boolean) + */ + public static <K, V> KeyedObjectPool<K, V> erodingPool( + final KeyedObjectPool<K, V> keyedPool) { + return erodingPool(keyedPool, 1f); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + * <p> + * The factor parameter provides a mechanism to tweak the rate at which the + * pool tries to shrink its size. Values between 0 and 1 cause the pool to + * try to shrink its size more often. Values greater than 1 cause the pool + * to less frequently try to shrink its size. + * </p> + * + * @param keyedPool + * the KeyedObjectPool to be decorated so it shrinks its idle + * count when possible. + * @param factor + * a positive value to scale the rate at which the pool tries to + * reduce its size. If 0 < factor < 1 then the pool + * shrinks more aggressively. If 1 < factor then the pool + * shrinks less aggressively. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @throws IllegalArgumentException + * when {@code keyedPool} is {@code null} or when {@code factor} + * is not positive. + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(KeyedObjectPool, float, boolean) + */ + public static <K, V> KeyedObjectPool<K, V> erodingPool( + final KeyedObjectPool<K, V> keyedPool, final float factor) { + return erodingPool(keyedPool, factor, false); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + * <p> + * The factor parameter provides a mechanism to tweak the rate at which the + * pool tries to shrink its size. Values between 0 and 1 cause the pool to + * try to shrink its size more often. Values greater than 1 cause the pool + * to less frequently try to shrink its size. + * </p> + * <p> + * The perKey parameter determines if the pool shrinks on a whole pool basis + * or a per key basis. When perKey is false, the keys do not have an effect + * on the rate at which the pool tries to shrink its size. When perKey is + * true, each key is shrunk independently. + * </p> + * + * @param keyedPool + * the KeyedObjectPool to be decorated so it shrinks its idle + * count when possible. + * @param factor + * a positive value to scale the rate at which the pool tries to + * reduce its size. If 0 < factor < 1 then the pool + * shrinks more aggressively. If 1 < factor then the pool + * shrinks less aggressively. + * @param perKey + * when true, each key is treated independently. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @throws IllegalArgumentException + * when {@code keyedPool} is {@code null} or when {@code factor} + * is not positive. + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(KeyedObjectPool) + * @see #erodingPool(KeyedObjectPool, float) + */ + public static <K, V> KeyedObjectPool<K, V> erodingPool( + final KeyedObjectPool<K, V> keyedPool, final float factor, + final boolean perKey) { + if (keyedPool == null) { + throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); + } + if (factor <= 0f) { + throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); + } + if (perKey) { + return new ErodingPerKeyKeyedObjectPool<>(keyedPool, factor); + } + return new ErodingKeyedObjectPool<>(keyedPool, factor); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + * + * @param pool + * the ObjectPool to be decorated so it shrinks its idle count + * when possible. + * @param <T> the type of objects in the pool + * @throws IllegalArgumentException + * when {@code pool} is {@code null}. + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(ObjectPool, float) + */ + public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool) { + return erodingPool(pool, 1f); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + * <p> + * The factor parameter provides a mechanism to tweak the rate at which the + * pool tries to shrink its size. Values between 0 and 1 cause the pool to + * try to shrink its size more often. Values greater than 1 cause the pool + * to less frequently try to shrink its size. + * </p> + * + * @param pool + * the ObjectPool to be decorated so it shrinks its idle count + * when possible. + * @param factor + * a positive value to scale the rate at which the pool tries to + * reduce its size. If 0 < factor < 1 then the pool + * shrinks more aggressively. If 1 < factor then the pool + * shrinks less aggressively. + * @param <T> the type of objects in the pool + * @throws IllegalArgumentException + * when {@code pool} is {@code null} or when {@code factor} is + * not positive. + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(ObjectPool) + */ + public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool, + final float factor) { + if (pool == null) { + throw new IllegalArgumentException(MSG_NULL_POOL); + } + if (factor <= 0f) { + throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); + } + return new ErodingObjectPool<>(pool, factor); + } + + /** + * Gets the {@code Timer} for checking keyedPool's idle count. + * + * @return the {@link Timer} for checking keyedPool's idle count. + */ + private static Timer getMinIdleTimer() { + return TimerHolder.MIN_IDLE_TIMER; + } + + /** + * Calls {@link KeyedObjectPool#addObject(Object)} on {@code keyedPool} with + * each key in {@code keys} for {@code count} number of times. This has + * the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)} + * for each key in the {@code keys} collection. + * + * @param keyedPool + * the keyedPool to prefill. + * @param keys + * {@link Collection} of keys to add objects for. + * @param count + * the number of idle objects to add for each {@code key}. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @throws Exception + * when {@link KeyedObjectPool#addObject(Object)} fails. + * @throws IllegalArgumentException + * when {@code keyedPool}, {@code keys}, or any value in + * {@code keys} is {@code null}. + * @see #prefill(KeyedObjectPool, Object, int) + * @deprecated Use {@link KeyedObjectPool#addObjects(Collection, int)}. + */ + @Deprecated + public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, + final Collection<K> keys, final int count) throws Exception, + IllegalArgumentException { + if (keys == null) { + throw new IllegalArgumentException(MSG_NULL_KEYS); } + keyedPool.addObjects(keys, count); + } - /** - * {@inheritDoc} - */ - @Override - public int getNumActive(final K key) { - return keyedPool.getNumActive(key); + /** + * Calls {@link KeyedObjectPool#addObject(Object)} on {@code keyedPool} with + * {@code key} {@code count} number of times. + * + * @param keyedPool + * the keyedPool to prefill. + * @param key + * the key to add objects for. + * @param count + * the number of idle objects to add for {@code key}. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @throws Exception + * when {@link KeyedObjectPool#addObject(Object)} fails. + * @throws IllegalArgumentException + * when {@code keyedPool} or {@code key} is {@code null}. + * @deprecated Use {@link KeyedObjectPool#addObjects(Object, int)}. + */ + @Deprecated + public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, + final K key, final int count) throws Exception, + IllegalArgumentException { + if (keyedPool == null) { + throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); } + keyedPool.addObjects(key, count); + } - /** - * {@inheritDoc} - */ - @Override - public void clear() throws Exception, UnsupportedOperationException { - keyedPool.clear(); + /** + * Calls {@link ObjectPool#addObject()} on {@code pool} {@code count} number + * of times. + * + * @param pool + * the pool to prefill. + * @param count + * the number of idle objects to add. + * @param <T> the type of objects in the pool + * @throws Exception + * when {@link ObjectPool#addObject()} fails. + * @throws IllegalArgumentException + * when {@code pool} is {@code null}. + * @deprecated Use {@link ObjectPool#addObjects(int)}. + */ + @Deprecated + public static <T> void prefill(final ObjectPool<T> pool, final int count) + throws Exception, IllegalArgumentException { + if (pool == null) { + throw new IllegalArgumentException(MSG_NULL_POOL); } + pool.addObjects(count); + } - /** - * {@inheritDoc} - */ - @Override - public void clear(final K key) throws Exception, - UnsupportedOperationException { - keyedPool.clear(key); - } + /** + * Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by + * the specified KeyedPoolableObjectFactory. + * + * @param keyedFactory + * the KeyedPooledObjectFactory to be "wrapped" in a + * synchronized KeyedPooledObjectFactory. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @return a synchronized view of the specified KeyedPooledObjectFactory. + */ + public static <K, V> KeyedPooledObjectFactory<K, V> synchronizedKeyedPooledFactory( + final KeyedPooledObjectFactory<K, V> keyedFactory) { + return new SynchronizedKeyedPooledObjectFactory<>(keyedFactory); + } - /** - * {@inheritDoc} + /** + * Returns a synchronized (thread-safe) KeyedObjectPool backed by the + * specified KeyedObjectPool. + * <p> + * <b>Note:</b> This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. Wrapping a pool that {@link #wait() waits} for poolable + * objects to be returned before allowing another one to be borrowed with + * another layer of synchronization will cause liveliness issues or a + * deadlock. + * </p> + * + * @param keyedPool + * the KeyedObjectPool to be "wrapped" in a synchronized + * KeyedObjectPool. + * @param <K> the type of the pool key + * @param <V> the type of pool entries + * @return a synchronized view of the specified KeyedObjectPool. + */ + public static <K, V> KeyedObjectPool<K, V> synchronizedPool( + final KeyedObjectPool<K, V> keyedPool) { + /* + * assert !(keyedPool instanceof GenericKeyedObjectPool) : + * "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool + * instanceof StackKeyedObjectPool) : + * "StackKeyedObjectPool is already thread-safe"; assert + * !"org.apache.commons.pool.composite.CompositeKeyedObjectPool" + * .equals(keyedPool.getClass().getName()) : + * "CompositeKeyedObjectPools are already thread-safe"; */ - @Override - public void close() { - try { - keyedPool.close(); - } catch (final Exception e) { - // swallowed - } - } + return new SynchronizedKeyedObjectPool<>(keyedPool); + } - /** - * Returns the underlying pool - * - * @return the keyed pool that this ErodingKeyedObjectPool wraps - */ - protected KeyedObjectPool<K, V> getKeyedPool() { - return keyedPool; + /** + * Returns a synchronized (thread-safe) ObjectPool backed by the specified + * ObjectPool. + * <p> + * <b>Note:</b> This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. Wrapping a pool that {@link #wait() waits} for poolable + * objects to be returned before allowing another one to be borrowed with + * another layer of synchronization will cause liveliness issues or a + * deadlock. + * </p> + * + * @param pool + * the ObjectPool to be "wrapped" in a synchronized ObjectPool. + * @param <T> the type of objects in the pool + * @throws IllegalArgumentException + * when {@code pool} is {@code null}. + * @return a synchronized view of the specified ObjectPool. + */ + public static <T> ObjectPool<T> synchronizedPool(final ObjectPool<T> pool) { + if (pool == null) { + throw new IllegalArgumentException(MSG_NULL_POOL); } - - /** - * {@inheritDoc} + /* + * assert !(pool instanceof GenericObjectPool) : + * "GenericObjectPool is already thread-safe"; assert !(pool instanceof + * SoftReferenceObjectPool) : + * "SoftReferenceObjectPool is already thread-safe"; assert !(pool + * instanceof StackObjectPool) : + * "StackObjectPool is already thread-safe"; assert + * !"org.apache.commons.pool.composite.CompositeObjectPool" + * .equals(pool.getClass().getName()) : + * "CompositeObjectPools are already thread-safe"; */ - @Override - public String toString() { - return "ErodingKeyedObjectPool{" + "factor=" + - erodingFactor + ", keyedPool=" + keyedPool + '}'; - } + return new SynchronizedObjectPool<>(pool); } /** - * Extends ErodingKeyedObjectPool to allow erosion to take place on a - * per-key basis. Timing of erosion events is tracked separately for - * separate keyed pools. + * Returns a synchronized (thread-safe) PooledObjectFactory backed by the + * specified PooledObjectFactory. * - * @param <K> object pool key type - * @param <V> object pool value type + * @param factory + * the PooledObjectFactory to be "wrapped" in a synchronized + * PooledObjectFactory. + * @param <T> the type of objects in the pool + * @return a synchronized view of the specified PooledObjectFactory. */ - private static final class ErodingPerKeyKeyedObjectPool<K, V> extends - ErodingKeyedObjectPool<K, V> { - - /** Erosion factor - same for all pools */ - private final float factor; - - /** Map of ErodingFactor instances keyed on pool keys */ - private final Map<K, ErodingFactor> factors = Collections.synchronizedMap(new HashMap<K, ErodingFactor>()); - - /** - * Creates a new ErordingPerKeyKeyedObjectPool decorating the given keyed - * pool with the specified erosion factor. - * - * @param keyedPool - * underlying keyed pool - * @param factor - * erosion factor - */ - public ErodingPerKeyKeyedObjectPool( - final KeyedObjectPool<K, V> keyedPool, final float factor) { - super(keyedPool, null); - this.factor = factor; - } - - /** - * {@inheritDoc} - */ - @Override - protected ErodingFactor getErodingFactor(final K key) { - ErodingFactor eFactor = factors.get(key); - // this may result in two ErodingFactors being created for a key - // since they are small and cheap this is okay. - if (eFactor == null) { - eFactor = new ErodingFactor(this.factor); - factors.put(key, eFactor); - } - return eFactor; - } + public static <T> PooledObjectFactory<T> synchronizedPooledFactory( + final PooledObjectFactory<T> factory) { + return new SynchronizedPooledObjectFactory<>(factory); + } - /** - * {@inheritDoc} - */ - @SuppressWarnings("resource") // getKeyedPool(): ivar access - @Override - public String toString() { - return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor + - ", keyedPool=" + getKeyedPool() + '}'; - } + /** + * PoolUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);. + * This constructor is public to permit tools that require a JavaBean + * instance to operate. + */ + public PoolUtils() { } } diff --git a/src/main/java/org/apache/commons/pool2/PooledObject.java b/src/main/java/org/apache/commons/pool2/PooledObject.java index 3ee8219..1e5fc06 100644 --- a/src/main/java/org/apache/commons/pool2/PooledObject.java +++ b/src/main/java/org/apache/commons/pool2/PooledObject.java @@ -33,20 +33,46 @@ import java.util.Deque; public interface PooledObject<T> extends Comparable<PooledObject<T>> { /** - * Obtains the underlying object that is wrapped by this instance of - * {@link PooledObject}. + * Allocates the object. * - * @return The wrapped object + * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} */ - T getObject(); + boolean allocate(); /** - * Obtains the time (using the same basis as - * {@link System#currentTimeMillis()}) that this object was created. + * Orders instances based on idle time - i.e. the length of time since the + * instance was returned to the pool. Used by the GKOP idle object evictor. + *<p> + * Note: This class has a natural ordering that is inconsistent with + * equals if distinct objects have the same identity hash code. + * </p> + * <p> + * {@inheritDoc} + * </p> + */ + @Override + int compareTo(PooledObject<T> other); + + /** + * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} + * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}. * - * @return The creation time for the wrapped object + * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} */ - long getCreateTime(); + boolean deallocate(); + + /** + * Called to inform the object that the eviction test has ended. + * + * @param idleQueue The queue of idle objects to which the object should be + * returned + * + * @return Currently not used + */ + boolean endEvictionTest(Deque<PooledObject<T>> idleQueue); + + @Override + boolean equals(Object obj); /** * Obtains the time in milliseconds that this object last spent in the @@ -68,6 +94,14 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> { } /** + * Obtains the time (using the same basis as + * {@link System#currentTimeMillis()}) that this object was created. + * + * @return The creation time for the wrapped object + */ + long getCreateTime(); + + /** * Obtains the time in milliseconds that this object last spend in the * idle state (it may still be idle in which case subsequent calls will * return an increased value). @@ -102,73 +136,45 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> { long getLastUsedTime(); /** - * Orders instances based on idle time - i.e. the length of time since the - * instance was returned to the pool. Used by the GKOP idle object evictor. - *<p> - * Note: This class has a natural ordering that is inconsistent with - * equals if distinct objects have the same identity hash code. - * </p> - * <p> - * {@inheritDoc} - * </p> + * Obtains the underlying object that is wrapped by this instance of + * {@link PooledObject}. + * + * @return The wrapped object */ - @Override - int compareTo(PooledObject<T> other); - - @Override - boolean equals(Object obj); - - @Override - int hashCode(); + T getObject(); /** - * Provides a String form of the wrapper for debug purposes. The format is - * not fixed and may change at any time. - * <p> - * {@inheritDoc} + * Returns the state of this object. + * @return state */ + PooledObjectState getState(); + @Override - String toString(); + int hashCode(); /** - * Attempts to place the pooled object in the - * {@link PooledObjectState#EVICTION} state. - * - * @return {@code true} if the object was placed in the - * {@link PooledObjectState#EVICTION} state otherwise - * {@code false} + * Sets the state to {@link PooledObjectState#INVALID INVALID} */ - boolean startEvictionTest(); + void invalidate(); /** - * Called to inform the object that the eviction test has ended. - * - * @param idleQueue The queue of idle objects to which the object should be - * returned - * - * @return Currently not used + * Marks the pooled object as abandoned. */ - boolean endEvictionTest(Deque<PooledObject<T>> idleQueue); + void markAbandoned(); /** - * Allocates the object. - * - * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} + * Marks the object as returning to the pool. */ - boolean allocate(); + void markReturning(); /** - * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} - * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}. + * Prints the stack trace of the code that borrowed this pooled object and + * the stack trace of the last code to use this object (if available) to + * the supplied writer. * - * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} - */ - boolean deallocate(); - - /** - * Sets the state to {@link PooledObjectState#INVALID INVALID} + * @param writer The destination for the debug output */ - void invalidate(); + void printStackTrace(PrintWriter writer); /** * Is abandoned object tracking being used? If this is true the @@ -193,33 +199,27 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> { } /** - * Record the current stack trace as the last time the object was used. - */ - void use(); - - /** - * Prints the stack trace of the code that borrowed this pooled object and - * the stack trace of the last code to use this object (if available) to - * the supplied writer. + * Attempts to place the pooled object in the + * {@link PooledObjectState#EVICTION} state. * - * @param writer The destination for the debug output - */ - void printStackTrace(PrintWriter writer); - - /** - * Returns the state of this object. - * @return state + * @return {@code true} if the object was placed in the + * {@link PooledObjectState#EVICTION} state otherwise + * {@code false} */ - PooledObjectState getState(); + boolean startEvictionTest(); /** - * Marks the pooled object as abandoned. + * Provides a String form of the wrapper for debug purposes. The format is + * not fixed and may change at any time. + * <p> + * {@inheritDoc} */ - void markAbandoned(); + @Override + String toString(); /** - * Marks the object as returning to the pool. + * Record the current stack trace as the last time the object was used. */ - void markReturning(); + void use(); } diff --git a/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java b/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java index 08adf18..009f341 100644 --- a/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java +++ b/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java @@ -73,15 +73,16 @@ package org.apache.commons.pool2; public interface PooledObjectFactory<T> { /** - * Creates an instance that can be served by the pool and wrap it in a - * {@link PooledObject} to be managed by the pool. + * Reinitializes an instance to be returned by the pool. * - * @return a {@code PooledObject} wrapping an instance that can be served by the pool + * @param p a {@code PooledObject} wrapping the instance to be activated * - * @throws Exception if there is a problem creating a new instance, - * this will be propagated to the code requesting an object. + * @throws Exception if there is a problem activating {@code obj}, + * this exception may be swallowed by the pool. + * + * @see #destroyObject */ - PooledObject<T> makeObject() throws Exception; + void activateObject(PooledObject<T> p) throws Exception; /** * Destroys an instance no longer needed by the pool, using the default (NORMAL) @@ -127,36 +128,35 @@ public interface PooledObjectFactory<T> { } /** - * Ensures that the instance is safe to be returned by the pool. + * Creates an instance that can be served by the pool and wrap it in a + * {@link PooledObject} to be managed by the pool. * - * @param p a {@code PooledObject} wrapping the instance to be validated + * @return a {@code PooledObject} wrapping an instance that can be served by the pool * - * @return {@code false} if {@code obj} is not valid and should - * be dropped from the pool, {@code true} otherwise. + * @throws Exception if there is a problem creating a new instance, + * this will be propagated to the code requesting an object. */ - boolean validateObject(PooledObject<T> p); + PooledObject<T> makeObject() throws Exception; /** - * Reinitializes an instance to be returned by the pool. + * Uninitializes an instance to be returned to the idle object pool. * - * @param p a {@code PooledObject} wrapping the instance to be activated + * @param p a {@code PooledObject} wrapping the instance to be passivated * - * @throws Exception if there is a problem activating {@code obj}, + * @throws Exception if there is a problem passivating {@code obj}, * this exception may be swallowed by the pool. * * @see #destroyObject */ - void activateObject(PooledObject<T> p) throws Exception; + void passivateObject(PooledObject<T> p) throws Exception; /** - * Uninitializes an instance to be returned to the idle object pool. - * - * @param p a {@code PooledObject} wrapping the instance to be passivated + * Ensures that the instance is safe to be returned by the pool. * - * @throws Exception if there is a problem passivating {@code obj}, - * this exception may be swallowed by the pool. + * @param p a {@code PooledObject} wrapping the instance to be validated * - * @see #destroyObject + * @return {@code false} if {@code obj} is not valid and should + * be dropped from the pool, {@code true} otherwise. */ - void passivateObject(PooledObject<T> p) throws Exception; + boolean validateObject(PooledObject<T> p); }