    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.pool2;
    018
    019import java.util.Collection;
    020import java.util.Collections;
    021import java.util.HashMap;
    022import java.util.Iterator;
    023import java.util.Map;
    024import java.util.NoSuchElementException;
    025import java.util.Timer;
    026import java.util.TimerTask;
    027import java.util.concurrent.locks.ReentrantReadWriteLock;
    028import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
    029import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
    030
    031/**
    032 * This class consists exclusively of static methods that operate on or return
    033 * ObjectPool or KeyedObjectPool related interfaces.
    034 *
    035 * @since 2.0
    036 */
    037public final class PoolUtils {
    038
    039    private static final String MSG_FACTOR_NEGATIVE = "factor must be positive.";
    040    private static final String MSG_MIN_IDLE = "minIdle must be non-negative.";
    041    static final String MSG_NULL_KEY = "key must not be null.";
    042    private static final String MSG_NULL_KEYED_POOL = "keyedPool must not be null.";
    043    static final String MSG_NULL_KEYS = "keys must not be null.";
    044    private static final String MSG_NULL_POOL = "pool must not be null.";
    045
    046    /**
    047     * Timer used to periodically check pools idle object count. Because a
    048     * {@link Timer} creates a {@link Thread}, an IODH is used.
    049     */
    050    static class TimerHolder {
    051        static final Timer MIN_IDLE_TIMER = new Timer(true);
    052    }
    053
    054    /**
    055     * PoolUtils instances should NOT be constructed in standard programming.
    056     * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);.
    057     * This constructor is public to permit tools that require a JavaBean
    058     * instance to operate.
    059     */
    060    public PoolUtils() {
    061    }
    062
    063    /**
    064     * Should the supplied Throwable be re-thrown (eg if it is an instance of
    065     * one of the Throwables that should never be swallowed). Used by the pool
    066     * error handling for operations that throw exceptions that normally need to
    067     * be ignored.
    068     *
    069     * @param t
    070     *            The Throwable to check
    071     * @throws ThreadDeath
    072     *             if that is passed in
    073     * @throws VirtualMachineError
    074     *             if that is passed in
    075     */
    076    public static void checkRethrow(final Throwable t) {
    077        if (t instanceof ThreadDeath) {
    078            throw (ThreadDeath) t;
    079        }
    080        if (t instanceof VirtualMachineError) {
    081            throw (VirtualMachineError) t;
    082        }
    083        // All other instances of Throwable will be silently swallowed
    084    }
    085
    086    /**
    087     * Periodically check the idle object count for the pool. At most one idle
    088     * object will be added per period. If there is an exception when calling
    089     * {@link ObjectPool#addObject()} then no more checks will be performed.
    090     *
    091     * @param pool
    092     *            the pool to check periodically.
    093     * @param minIdle
    094     *            if the {@link ObjectPool#getNumIdle()} is less than this then
    095     *            add an idle object.
    096     * @param period
    097     *            the frequency to check the number of idle objects in a pool,
    098     *            see {@link Timer#schedule(TimerTask, long, long)}.
    099     * @param <T> the type of objects in the pool
    100     * @return the {@link TimerTask} that will periodically check the pools idle
    101     *         object count.
    102     * @throws IllegalArgumentException
    103     *             when <code>pool</code> is <code>null</code> or when
    104     *             <code>minIdle</code> is negative or when <code>period</code>
    105     *             isn't valid for {@link Timer#schedule(TimerTask, long, long)}
    106     */
    107    public static <T> TimerTask checkMinIdle(final ObjectPool<T> pool,
    108            final int minIdle, final long period)
    109            throws IllegalArgumentException {
    110        if (pool == null) {
    111            throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
    112        }
    113        if (minIdle < 0) {
    114            throw new IllegalArgumentException(MSG_MIN_IDLE);
    115        }
    116        final TimerTask task = new ObjectPoolMinIdleTimerTask<>(pool, minIdle);
    117        getMinIdleTimer().schedule(task, 0L, period);
    118        return task;
    119    }
    120
    121    /**
    122     * Periodically check the idle object count for the key in the keyedPool. At
    123     * most one idle object will be added per period. If there is an exception
    124     * when calling {@link KeyedObjectPool#addObject(Object)} then no more
    125     * checks for that key will be performed.
    126     *
    127     * @param keyedPool
    128     *            the keyedPool to check periodically.
    129     * @param key
    130     *            the key to check the idle count of.
    131     * @param minIdle
    132     *            if the {@link KeyedObjectPool#getNumIdle(Object)} is less than
    133     *            this then add an idle object.
    134     * @param period
    135     *            the frequency to check the number of idle objects in a
    136     *            keyedPool, see {@link Timer#schedule(TimerTask, long, long)}.
    137     * @param <K> the type of the pool key
    138     * @param <V> the type of pool entries
    139     * @return the {@link TimerTask} that will periodically check the pools idle
    140     *         object count.
    141     * @throws IllegalArgumentException
    142     *             when <code>keyedPool</code>, <code>key</code> is
    143     *             <code>null</code> or when <code>minIdle</code> is negative or
    144     *             when <code>period</code> isn't valid for
    145     *             {@link Timer#schedule(TimerTask, long, long)}.
    146     */
    147    public static <K, V> TimerTask checkMinIdle(
    148            final KeyedObjectPool<K, V> keyedPool, final K key,
    149            final int minIdle, final long period)
    150            throws IllegalArgumentException {
    151        if (keyedPool == null) {
    152            throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
    153        }
    154        if (key == null) {
    155            throw new IllegalArgumentException(MSG_NULL_KEY);
    156        }
    157        if (minIdle < 0) {
    158            throw new IllegalArgumentException(MSG_MIN_IDLE);
    159        }
    160        final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<>(
    161                keyedPool, key, minIdle);
    162        getMinIdleTimer().schedule(task, 0L, period);
    163        return task;
    164    }
    165
    166    /**
    167     * Periodically check the idle object count for each key in the
    168     * <code>Collection</code> <code>keys</code> in the keyedPool. At most one
    169     * idle object will be added per period.
    170     *
    171     * @param keyedPool
    172     *            the keyedPool to check periodically.
    173     * @param keys
    174     *            a collection of keys to check the idle object count.
    175     * @param minIdle
    176     *            if the {@link KeyedObjectPool#getNumIdle(Object)} is less than
    177     *            this then add an idle object.
    178     * @param period
    179     *            the frequency to check the number of idle objects in a
    180     *            keyedPool, see {@link Timer#schedule(TimerTask, long, long)}.
    181     * @param <K> the type of the pool key
    182     * @param <V> the type of pool entries
    183     * @return a {@link Map} of key and {@link TimerTask} pairs that will
    184     *         periodically check the pools idle object count.
    185     * @throws IllegalArgumentException
    186     *             when <code>keyedPool</code>, <code>keys</code>, or any of the
    187     *             values in the collection is <code>null</code> or when
    188     *             <code>minIdle</code> is negative or when <code>period</code>
    189     *             isn't valid for {@link Timer#schedule(TimerTask, long, long)}
    190     *             .
    191     * @see #checkMinIdle(KeyedObjectPool, Object, int, long)
    192     */
    193    public static <K, V> Map<K, TimerTask> checkMinIdle(
    194            final KeyedObjectPool<K, V> keyedPool, final Collection<K> keys,
    195            final int minIdle, final long period)
    196            throws IllegalArgumentException {
    197        if (keys == null) {
    198            throw new IllegalArgumentException(MSG_NULL_KEYS);
    199        }
    200        final Map<K, TimerTask> tasks = new HashMap<>(keys.size());
    201        final Iterator<K> iter = keys.iterator();
    202        while (iter.hasNext()) {
    203            final K key = iter.next();
    204            final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
    205            tasks.put(key, task);
    206        }
    207        return tasks;
    208    }
    209
    210    /**
    211     * Calls {@link ObjectPool#addObject()} on <code>pool</code> <code>count</code>
    212     * number of times.
    213     *
    214     * @param pool
    215     *            the pool to prefill.
    216     * @param count
    217     *            the number of idle objects to add.
    218     * @param <T> the type of objects in the pool
    219     * @throws Exception
    220     *             when {@link ObjectPool#addObject()} fails.
    221     * @throws IllegalArgumentException
    222     *             when <code>pool</code> is <code>null</code>.
    223     * @deprecated Use {@link ObjectPool#addObjects(int)}.
    224     */
    225    @Deprecated
    226    public static <T> void prefill(final ObjectPool<T> pool, final int count)
    227            throws Exception, IllegalArgumentException {
    228        if (pool == null) {
    229            throw new IllegalArgumentException(MSG_NULL_POOL);
    230        }
    231        pool.addObjects(count);
    232    }
    233
    234    /**
    235     * Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with
    236     * <code>key</code> <code>count</code> number of times.
    237     *
    238     * @param keyedPool
    239     *            the keyedPool to prefill.
    240     * @param key
    241     *            the key to add objects for.
    242     * @param count
    243     *            the number of idle objects to add for <code>key</code>.
    244     * @param <K> the type of the pool key
    245     * @param <V> the type of pool entries
    246     * @throws Exception
    247     *             when {@link KeyedObjectPool#addObject(Object)} fails.
    248     * @throws IllegalArgumentException
    249     *             when <code>keyedPool</code> or <code>key</code> is
    250     *             <code>null</code>.
    251     * @deprecated Use {@link KeyedObjectPool#addObjects(Object, int)}.
    252     */
    253    @Deprecated
    254    public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool,
    255            final K key, final int count) throws Exception,
    256            IllegalArgumentException {
    257        if (keyedPool == null) {
    258            throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
    259        }
    260        keyedPool.addObjects(key, count);
    261    }
    262
    263    /**
    264     * Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with each
    265     * key in <code>keys</code> for <code>count</code> number of times. This has
    266     * the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)}
    267     * for each key in the <code>keys</code> collection.
    268     *
    269     * @param keyedPool
    270     *            the keyedPool to prefill.
    271     * @param keys
    272     *            {@link Collection} of keys to add objects for.
    273     * @param count
    274     *            the number of idle objects to add for each <code>key</code>.
    275     * @param <K> the type of the pool key
    276     * @param <V> the type of pool entries
    277     * @throws Exception
    278     *             when {@link KeyedObjectPool#addObject(Object)} fails.
    279     * @throws IllegalArgumentException
    280     *             when <code>keyedPool</code>, <code>keys</code>, or any value
    281     *             in <code>keys</code> is <code>null</code>.
    282     * @see #prefill(KeyedObjectPool, Object, int)
    283     * @deprecated Use {@link KeyedObjectPool#addObjects(Collection, int)}.
    284     */
    285    @Deprecated
    286    public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool,
    287            final Collection<K> keys, final int count) throws Exception,
    288            IllegalArgumentException {
    289        if (keys == null) {
    290            throw new IllegalArgumentException(MSG_NULL_KEYS);
    291        }
    292        keyedPool.addObjects(keys, count);
    293    }
    294
    295    /**
    296     * Returns a synchronized (thread-safe) ObjectPool backed by the specified
    297     * ObjectPool.
    298     * <p>
    299     * <b>Note:</b> This should not be used on pool implementations that already
    300     * provide proper synchronization such as the pools provided in the Commons
    301     * Pool library. Wrapping a pool that {@link #wait() waits} for poolable
    302     * objects to be returned before allowing another one to be borrowed with
    303     * another layer of synchronization will cause liveliness issues or a
    304     * deadlock.
    305     * </p>
    306     *
    307     * @param pool
    308     *            the ObjectPool to be "wrapped" in a synchronized ObjectPool.
    309     * @param <T> the type of objects in the pool
    310     * @return a synchronized view of the specified ObjectPool.
    311     */
    312    public static <T> ObjectPool<T> synchronizedPool(final ObjectPool<T> pool) {
    313        if (pool == null) {
    314            throw new IllegalArgumentException(MSG_NULL_POOL);
    315        }
    316        /*
    317         * assert !(pool instanceof GenericObjectPool) :
    318         * "GenericObjectPool is already thread-safe"; assert !(pool instanceof
    319         * SoftReferenceObjectPool) :
    320         * "SoftReferenceObjectPool is already thread-safe"; assert !(pool
    321         * instanceof StackObjectPool) :
    322         * "StackObjectPool is already thread-safe"; assert
    323         * !"org.apache.commons.pool.composite.CompositeObjectPool"
    324         * .equals(pool.getClass().getName()) :
    325         * "CompositeObjectPools are already thread-safe";
    326         */
    327        return new SynchronizedObjectPool<>(pool);
    328    }
    329
    330    /**
    331     * Returns a synchronized (thread-safe) KeyedObjectPool backed by the
    332     * specified KeyedObjectPool.
    333     * <p>
    334     * <b>Note:</b> This should not be used on pool implementations that already
    335     * provide proper synchronization such as the pools provided in the Commons
    336     * Pool library. Wrapping a pool that {@link #wait() waits} for poolable
    337     * objects to be returned before allowing another one to be borrowed with
    338     * another layer of synchronization will cause liveliness issues or a
    339     * deadlock.
    340     * </p>
    341     *
    342     * @param keyedPool
    343     *            the KeyedObjectPool to be "wrapped" in a synchronized
    344     *            KeyedObjectPool.
    345     * @param <K> the type of the pool key
    346     * @param <V> the type of pool entries
    347     * @return a synchronized view of the specified KeyedObjectPool.
    348     */
    349    public static <K, V> KeyedObjectPool<K, V> synchronizedPool(
    350            final KeyedObjectPool<K, V> keyedPool) {
    351        /*
    352         * assert !(keyedPool instanceof GenericKeyedObjectPool) :
    353         * "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool
    354         * instanceof StackKeyedObjectPool) :
    355         * "StackKeyedObjectPool is already thread-safe"; assert
    356         * !"org.apache.commons.pool.composite.CompositeKeyedObjectPool"
    357         * .equals(keyedPool.getClass().getName()) :
    358         * "CompositeKeyedObjectPools are already thread-safe";
    359         */
    360        return new SynchronizedKeyedObjectPool<>(keyedPool);
    361    }
    362
    363    /**
    364     * Returns a synchronized (thread-safe) PooledObjectFactory backed by the
    365     * specified PooledObjectFactory.
    366     *
    367     * @param factory
    368     *            the PooledObjectFactory to be "wrapped" in a synchronized
    369     *            PooledObjectFactory.
    370     * @param <T> the type of objects in the pool
    371     * @return a synchronized view of the specified PooledObjectFactory.
    372     */
    373    public static <T> PooledObjectFactory<T> synchronizedPooledFactory(
    374            final PooledObjectFactory<T> factory) {
    375        return new SynchronizedPooledObjectFactory<>(factory);
    376    }
    377
    378    /**
    379     * Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by
    380     * the specified KeyedPoolableObjectFactory.
    381     *
    382     * @param keyedFactory
    383     *            the KeyedPooledObjectFactory to be "wrapped" in a
    384     *            synchronized KeyedPooledObjectFactory.
    385     * @param <K> the type of the pool key
    386     * @param <V> the type of pool entries
    387     * @return a synchronized view of the specified KeyedPooledObjectFactory.
    388     */
    389    public static <K, V> KeyedPooledObjectFactory<K, V> synchronizedKeyedPooledFactory(
    390            final KeyedPooledObjectFactory<K, V> keyedFactory) {
    391        return new SynchronizedKeyedPooledObjectFactory<>(keyedFactory);
    392    }
    393
    394    /**
    395     * Returns a pool that adaptively decreases its size when idle objects are
    396     * no longer needed. This is intended as an always thread-safe alternative
    397     * to using an idle object evictor provided by many pool implementations.
    398     * This is also an effective way to shrink FIFO ordered pools that
    399     * experience load spikes.
    400     *
    401     * @param pool
    402     *            the ObjectPool to be decorated so it shrinks its idle count
    403     *            when possible.
    404     * @param <T> the type of objects in the pool
    405     * @return a pool that adaptively decreases its size when idle objects are
    406     *         no longer needed.
    407     * @see #erodingPool(ObjectPool, float)
    408     */
    409    public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool) {
    410        return erodingPool(pool, 1f);
    411    }
    412
    413    /**
    414     * Returns a pool that adaptively decreases its size when idle objects are
    415     * no longer needed. This is intended as an always thread-safe alternative
    416     * to using an idle object evictor provided by many pool implementations.
    417     * This is also an effective way to shrink FIFO ordered pools that
    418     * experience load spikes.
    419     * <p>
    420     * The factor parameter provides a mechanism to tweak the rate at which the
    421     * pool tries to shrink its size. Values between 0 and 1 cause the pool to
    422     * try to shrink its size more often. Values greater than 1 cause the pool
    423     * to less frequently try to shrink its size.
    424     * </p>
    425     *
    426     * @param pool
    427     *            the ObjectPool to be decorated so it shrinks its idle count
    428     *            when possible.
    429     * @param factor
    430     *            a positive value to scale the rate at which the pool tries to
    431     *            reduce its size. If 0 &lt; factor &lt; 1 then the pool
    432     *            shrinks more aggressively. If 1 &lt; factor then the pool
    433     *            shrinks less aggressively.
    434     * @param <T> the type of objects in the pool
    435     * @return a pool that adaptively decreases its size when idle objects are
    436     *         no longer needed.
    437     * @see #erodingPool(ObjectPool)
    438     */
    439    public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool,
    440            final float factor) {
    441        if (pool == null) {
    442            throw new IllegalArgumentException(MSG_NULL_POOL);
    443        }
    444        if (factor <= 0f) {
    445            throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE);
    446        }
    447        return new ErodingObjectPool<>(pool, factor);
    448    }
    449
    450    /**
    451     * Returns a pool that adaptively decreases its size when idle objects are
    452     * no longer needed. This is intended as an always thread-safe alternative
    453     * to using an idle object evictor provided by many pool implementations.
    454     * This is also an effective way to shrink FIFO ordered pools that
    455     * experience load spikes.
    456     *
    457     * @param keyedPool
    458     *            the KeyedObjectPool to be decorated so it shrinks its idle
    459     *            count when possible.
    460     * @param <K> the type of the pool key
    461     * @param <V> the type of pool entries
    462     * @return a pool that adaptively decreases its size when idle objects are
    463     *         no longer needed.
    464     * @see #erodingPool(KeyedObjectPool, float)
    465     * @see #erodingPool(KeyedObjectPool, float, boolean)
    466     */
    467    public static <K, V> KeyedObjectPool<K, V> erodingPool(
    468            final KeyedObjectPool<K, V> keyedPool) {
    469        return erodingPool(keyedPool, 1f);
    470    }
    471
    472    /**
    473     * Returns a pool that adaptively decreases its size when idle objects are
    474     * no longer needed. This is intended as an always thread-safe alternative
    475     * to using an idle object evictor provided by many pool implementations.
    476     * This is also an effective way to shrink FIFO ordered pools that
    477     * experience load spikes.
    478     * <p>
    479     * The factor parameter provides a mechanism to tweak the rate at which the
    480     * pool tries to shrink its size. Values between 0 and 1 cause the pool to
    481     * try to shrink its size more often. Values greater than 1 cause the pool
    482     * to less frequently try to shrink its size.
    483     * </p>
    484     *
    485     * @param keyedPool
    486     *            the KeyedObjectPool to be decorated so it shrinks its idle
    487     *            count when possible.
    488     * @param factor
    489     *            a positive value to scale the rate at which the pool tries to
    490     *            reduce its size. If 0 &lt; factor &lt; 1 then the pool
    491     *            shrinks more aggressively. If 1 &lt; factor then the pool
    492     *            shrinks less aggressively.
    493     * @param <K> the type of the pool key
    494     * @param <V> the type of pool entries
    495     * @return a pool that adaptively decreases its size when idle objects are
    496     *         no longer needed.
    497     * @see #erodingPool(KeyedObjectPool, float, boolean)
    498     */
    499    public static <K, V> KeyedObjectPool<K, V> erodingPool(
    500            final KeyedObjectPool<K, V> keyedPool, final float factor) {
    501        return erodingPool(keyedPool, factor, false);
    502    }
    503
    504    /**
    505     * Returns a pool that adaptively decreases its size when idle objects are
    506     * no longer needed. This is intended as an always thread-safe alternative
    507     * to using an idle object evictor provided by many pool implementations.
    508     * This is also an effective way to shrink FIFO ordered pools that
    509     * experience load spikes.
    510     * <p>
    511     * The factor parameter provides a mechanism to tweak the rate at which the
    512     * pool tries to shrink its size. Values between 0 and 1 cause the pool to
    513     * try to shrink its size more often. Values greater than 1 cause the pool
    514     * to less frequently try to shrink its size.
    515     * </p>
    516     * <p>
    517     * The perKey parameter determines if the pool shrinks on a whole pool basis
    518     * or a per key basis. When perKey is false, the keys do not have an effect
    519     * on the rate at which the pool tries to shrink its size. When perKey is
    520     * true, each key is shrunk independently.
    521     * </p>
    522     *
    523     * @param keyedPool
    524     *            the KeyedObjectPool to be decorated so it shrinks its idle
    525     *            count when possible.
    526     * @param factor
    527     *            a positive value to scale the rate at which the pool tries to
    528     *            reduce its size. If 0 &lt; factor &lt; 1 then the pool
    529     *            shrinks more aggressively. If 1 &lt; factor then the pool
    530     *            shrinks less aggressively.
    531     * @param perKey
    532     *            when true, each key is treated independently.
    533     * @param <K> the type of the pool key
    534     * @param <V> the type of pool entries
    535     * @return a pool that adaptively decreases its size when idle objects are
    536     *         no longer needed.
    537     * @see #erodingPool(KeyedObjectPool)
    538     * @see #erodingPool(KeyedObjectPool, float)
    539     */
    540    public static <K, V> KeyedObjectPool<K, V> erodingPool(
    541            final KeyedObjectPool<K, V> keyedPool, final float factor,
    542            final boolean perKey) {
    543        if (keyedPool == null) {
    544            throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
    545        }
    546        if (factor <= 0f) {
    547            throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE);
    548        }
    549        if (perKey) {
    550            return new ErodingPerKeyKeyedObjectPool<>(keyedPool, factor);
    551        }
    552        return new ErodingKeyedObjectPool<>(keyedPool, factor);
    553    }
    554
    555    /**
    556     * Gets the <code>Timer</code> for checking keyedPool's idle count.
    557     *
    558     * @return the {@link Timer} for checking keyedPool's idle count.
    559     */
    560    private static Timer getMinIdleTimer() {
    561        return TimerHolder.MIN_IDLE_TIMER;
    562    }
    563
    564    /**
    565     * Timer task that adds objects to the pool until the number of idle
    566     * instances reaches the configured minIdle. Note that this is not the same
    567     * as the pool's minIdle setting.
    568     *
    569     * @param <T> type of objects in the pool
    570     */
    571    private static final class ObjectPoolMinIdleTimerTask<T> extends TimerTask {
    572
    573        /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
    574        private final int minIdle;
    575
    576        /** Object pool */
    577        private final ObjectPool<T> pool;
    578
    579        /**
    580         * Create a new ObjectPoolMinIdleTimerTask for the given pool with the
    581         * given minIdle setting.
    582         *
    583         * @param pool
    584         *            object pool
    585         * @param minIdle
    586         *            number of idle instances to maintain
    587         * @throws IllegalArgumentException
    588         *             if the pool is null
    589         */
    590        ObjectPoolMinIdleTimerTask(final ObjectPool<T> pool, final int minIdle)
    591                throws IllegalArgumentException {
    592            if (pool == null) {
    593                throw new IllegalArgumentException(MSG_NULL_POOL);
    594            }
    595            this.pool = pool;
    596            this.minIdle = minIdle;
    597        }
    598
    599        /**
    600         * {@inheritDoc}
    601         */
    602        @Override
    603        public void run() {
    604            boolean success = false;
    605            try {
    606                if (pool.getNumIdle() < minIdle) {
    607                    pool.addObject();
    608                }
    609                success = true;
    610
    611            } catch (final Exception e) {
    612                cancel();
    613            } finally {
    614                // detect other types of Throwable and cancel this Timer
    615                if (!success) {
    616                    cancel();
    617                }
    618            }
    619        }
    620
    621        /**
    622         * {@inheritDoc}
    623         */
    624        @Override
    625        public String toString() {
    626            final StringBuilder sb = new StringBuilder();
    627            sb.append("ObjectPoolMinIdleTimerTask");
    628            sb.append("{minIdle=").append(minIdle);
    629            sb.append(", pool=").append(pool);
    630            sb.append('}');
    631            return sb.toString();
    632        }
    633    }
    634
    635    /**
    636     * Timer task that adds objects to the pool until the number of idle
    637     * instances for the given key reaches the configured minIdle. Note that
    638     * this is not the same as the pool's minIdle setting.
    639     *
    640     * @param <K> object pool key type
    641     * @param <V> object pool value type
    642     */
    643    private static final class KeyedObjectPoolMinIdleTimerTask<K, V> extends
    644            TimerTask {
    645
    646        /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
    647        private final int minIdle;
    648
    649        /** Key to ensure minIdle for */
    650        private final K key;
    651
    652        /** Keyed object pool */
    653        private final KeyedObjectPool<K, V> keyedPool;
    654
    655        /**
    656         * Creates a new KeyedObjecPoolMinIdleTimerTask.
    657         *
    658         * @param keyedPool
    659         *            keyed object pool
    660         * @param key
    661         *            key to ensure minimum number of idle instances
    662         * @param minIdle
    663         *            minimum number of idle instances
    664         * @throws IllegalArgumentException
    665         *             if the key is null
    666         */
    667        KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool<K, V> keyedPool,
    668                final K key, final int minIdle) throws IllegalArgumentException {
    669            if (keyedPool == null) {
    670                throw new IllegalArgumentException(
    671                        MSG_NULL_KEYED_POOL);
    672            }
    673            this.keyedPool = keyedPool;
    674            this.key = key;
    675            this.minIdle = minIdle;
    676        }
    677
    678        /**
    679         * {@inheritDoc}
    680         */
    681        @Override
    682        public void run() {
    683            boolean success = false;
    684            try {
    685                if (keyedPool.getNumIdle(key) < minIdle) {
    686                    keyedPool.addObject(key);
    687                }
    688                success = true;
    689
    690            } catch (final Exception e) {
    691                cancel();
    692
    693            } finally {
    694                // detect other types of Throwable and cancel this Timer
    695                if (!success) {
    696                    cancel();
    697                }
    698            }
    699        }
    700
    701        /**
    702         * {@inheritDoc}
    703         */
    704        @Override
    705        public String toString() {
    706            final StringBuilder sb = new StringBuilder();
    707            sb.append("KeyedObjectPoolMinIdleTimerTask");
    708            sb.append("{minIdle=").append(minIdle);
    709            sb.append(", key=").append(key);
    710            sb.append(", keyedPool=").append(keyedPool);
    711            sb.append('}');
    712            return sb.toString();
    713        }
    714    }
    715
    716    /**
    717     * A synchronized (thread-safe) ObjectPool backed by the specified
    718     * ObjectPool.
    719     * <p>
    720     * <b>Note:</b> This should not be used on pool implementations that already
    721     * provide proper synchronization such as the pools provided in the Commons
    722     * Pool library. Wrapping a pool that {@link #wait() waits} for poolable
    723     * objects to be returned before allowing another one to be borrowed with
    724     * another layer of synchronization will cause liveliness issues or a
    725     * deadlock.
    726     * </p>
    727     *
    728     * @param <T> type of objects in the pool
    729     */
    730    private static final class SynchronizedObjectPool<T> implements ObjectPool<T> {
    731
    732        /**
    733         * Object whose monitor is used to synchronize methods on the wrapped
    734         * pool.
    735         */
    736        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    737
    738        /** the underlying object pool */
    739        private final ObjectPool<T> pool;
    740
    741        /**
    742         * Creates a new SynchronizedObjectPool wrapping the given pool.
    743         *
    744         * @param pool
    745         *            the ObjectPool to be "wrapped" in a synchronized
    746         *            ObjectPool.
    747         * @throws IllegalArgumentException
    748         *             if the pool is null
    749         */
    750        SynchronizedObjectPool(final ObjectPool<T> pool)
    751                throws IllegalArgumentException {
    752            if (pool == null) {
    753                throw new IllegalArgumentException(MSG_NULL_POOL);
    754            }
    755            this.pool = pool;
    756        }
    757
    758        /**
    759         * {@inheritDoc}
    760         */
    761        @Override
    762        public T borrowObject() throws Exception, NoSuchElementException,
    763                IllegalStateException {
    764            final WriteLock writeLock = readWriteLock.writeLock();
    765            writeLock.lock();
    766            try {
    767                return pool.borrowObject();
    768            } finally {
    769                writeLock.unlock();
    770            }
    771        }
    772
    773        /**
    774         * {@inheritDoc}
    775         */
    776        @Override
    777        public void returnObject(final T obj) {
    778            final WriteLock writeLock = readWriteLock.writeLock();
    779            writeLock.lock();
    780            try {
    781                pool.returnObject(obj);
    782            } catch (final Exception e) {
    783                // swallowed as of Pool 2
    784            } finally {
    785                writeLock.unlock();
    786            }
    787        }
    788
    789        /**
    790         * {@inheritDoc}
    791         */
    792        @Override
    793        public void invalidateObject(final T obj) {
    794            final WriteLock writeLock = readWriteLock.writeLock();
    795            writeLock.lock();
    796            try {
    797                pool.invalidateObject(obj);
    798            } catch (final Exception e) {
    799                // swallowed as of Pool 2
    800            } finally {
    801                writeLock.unlock();
    802            }
    803        }
    804
    805        /**
    806         * {@inheritDoc}
    807         */
    808        @Override
    809        public void addObject() throws Exception, IllegalStateException,
    810                UnsupportedOperationException {
    811            final WriteLock writeLock = readWriteLock.writeLock();
    812            writeLock.lock();
    813            try {
    814                pool.addObject();
    815            } finally {
    816                writeLock.unlock();
    817            }
    818        }
    819
    820        /**
    821         * {@inheritDoc}
    822         */
    823        @Override
    824        public int getNumIdle() {
    825            final ReadLock readLock = readWriteLock.readLock();
    826            readLock.lock();
    827            try {
    828                return pool.getNumIdle();
    829            } finally {
    830                readLock.unlock();
    831            }
    832        }
    833
    834        /**
    835         * {@inheritDoc}
    836         */
    837        @Override
    838        public int getNumActive() {
    839            final ReadLock readLock = readWriteLock.readLock();
    840            readLock.lock();
    841            try {
    842                return pool.getNumActive();
    843            } finally {
    844                readLock.unlock();
    845            }
    846        }
    847
    848        /**
    849         * {@inheritDoc}
    850         */
    851        @Override
    852        public void clear() throws Exception, UnsupportedOperationException {
    853            final WriteLock writeLock = readWriteLock.writeLock();
    854            writeLock.lock();
    855            try {
    856                pool.clear();
    857            } finally {
    858                writeLock.unlock();
    859            }
    860        }
    861
    862        /**
    863         * {@inheritDoc}
    864         */
    865        @Override
    866        public void close() {
    867            final WriteLock writeLock = readWriteLock.writeLock();
    868            writeLock.lock();
    869            try {
    870                pool.close();
    871            } catch (final Exception e) {
    872                // swallowed as of Pool 2
    873            } finally {
    874                writeLock.unlock();
    875            }
    876        }
    877
    878        /**
    879         * {@inheritDoc}
    880         */
    881        @Override
    882        public String toString() {
    883            final StringBuilder sb = new StringBuilder();
    884            sb.append("SynchronizedObjectPool");
    885            sb.append("{pool=").append(pool);
    886            sb.append('}');
    887            return sb.toString();
    888        }
    889    }
    890
    891    /**
    892     * A synchronized (thread-safe) KeyedObjectPool backed by the specified
    893     * KeyedObjectPool.
    894     * <p>
    895     * <b>Note:</b> This should not be used on pool implementations that already
    896     * provide proper synchronization such as the pools provided in the Commons
    897     * Pool library. Wrapping a pool that {@link #wait() waits} for poolable
    898     * objects to be returned before allowing another one to be borrowed with
    899     * another layer of synchronization will cause liveliness issues or a
    900     * deadlock.
    901     * </p>
    902     *
    903     * @param <K> object pool key type
    904     * @param <V> object pool value type
    905     */
    906    private static final class SynchronizedKeyedObjectPool<K, V> implements
    907            KeyedObjectPool<K, V> {
    908
    909        /**
    910         * Object whose monitor is used to synchronize methods on the wrapped
    911         * pool.
    912         */
    913        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    914
    915        /** Underlying object pool */
    916        private final KeyedObjectPool<K, V> keyedPool;
    917
    918        /**
    919         * Creates a new SynchronizedKeyedObjectPool wrapping the given pool
    920         *
    921         * @param keyedPool
    922         *            KeyedObjectPool to wrap
    923         * @throws IllegalArgumentException
    924         *             if keyedPool is null
    925         */
    926        SynchronizedKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool)
    927                throws IllegalArgumentException {
    928            if (keyedPool == null) {
    929                throw new IllegalArgumentException(
    930                        MSG_NULL_KEYED_POOL);
    931            }
    932            this.keyedPool = keyedPool;
    933        }
    934
    935        /**
    936         * {@inheritDoc}
    937         */
    938        @Override
    939        public V borrowObject(final K key) throws Exception,
    940                NoSuchElementException, IllegalStateException {
    941            final WriteLock writeLock = readWriteLock.writeLock();
    942            writeLock.lock();
    943            try {
    944                return keyedPool.borrowObject(key);
    945            } finally {
    946                writeLock.unlock();
    947            }
    948        }
    949
    950        /**
    951         * {@inheritDoc}
    952         */
    953        @Override
    954        public void returnObject(final K key, final V obj) {
    955            final WriteLock writeLock = readWriteLock.writeLock();
    956            writeLock.lock();
    957            try {
    958                keyedPool.returnObject(key, obj);
    959            } catch (final Exception e) {
    960                // swallowed
    961            } finally {
    962                writeLock.unlock();
    963            }
    964        }
    965
    966        /**
    967         * {@inheritDoc}
    968         */
    969        @Override
    970        public void invalidateObject(final K key, final V obj) {
    971            final WriteLock writeLock = readWriteLock.writeLock();
    972            writeLock.lock();
    973            try {
    974                keyedPool.invalidateObject(key, obj);
    975            } catch (final Exception e) {
    976                // swallowed as of Pool 2
    977            } finally {
    978                writeLock.unlock();
    979            }
    980        }
    981
    982        /**
    983         * {@inheritDoc}
    984         */
    985        @Override
    986        public void addObject(final K key) throws Exception,
    987                IllegalStateException, UnsupportedOperationException {
    988            final WriteLock writeLock = readWriteLock.writeLock();
    989            writeLock.lock();
    990            try {
    991                keyedPool.addObject(key);
    992            } finally {
    993                writeLock.unlock();
    994            }
    995        }
    996
    997        /**
    998         * {@inheritDoc}
    999         */
    1000        @Override
    1001        public int getNumIdle(final K key) {
    1002            final ReadLock readLock = readWriteLock.readLock();
    1003            readLock.lock();
    1004            try {
    1005                return keyedPool.getNumIdle(key);
    1006            } finally {
    1007                readLock.unlock();
    1008            }
    1009        }
    1010
    1011        /**
    1012         * {@inheritDoc}
    1013         */
    1014        @Override
    1015        public int getNumActive(final K key) {
    1016            final ReadLock readLock = readWriteLock.readLock();
    1017            readLock.lock();
    1018            try {
    1019                return keyedPool.getNumActive(key);
    1020            } finally {
    1021                readLock.unlock();
    1022            }
    1023        }
    1024
    1025        /**
    1026         * {@inheritDoc}
    1027         */
    1028        @Override
    1029        public int getNumIdle() {
    1030            final ReadLock readLock = readWriteLock.readLock();
    1031            readLock.lock();
    1032            try {
    1033                return keyedPool.getNumIdle();
    1034            } finally {
    1035                readLock.unlock();
    1036            }
    1037        }
    1038
    1039        /**
    1040         * {@inheritDoc}
    1041         */
    1042        @Override
    1043        public int getNumActive() {
    1044            final ReadLock readLock = readWriteLock.readLock();
    1045            readLock.lock();
    1046            try {
    1047                return keyedPool.getNumActive();
    1048            } finally {
    1049                readLock.unlock();
    1050            }
    1051        }
    1052
    1053        /**
    1054         * {@inheritDoc}
    1055         */
    1056        @Override
    1057        public void clear() throws Exception, UnsupportedOperationException {
    1058            final WriteLock writeLock = readWriteLock.writeLock();
    1059            writeLock.lock();
    1060            try {
    1061                keyedPool.clear();
    1062            } finally {
    1063                writeLock.unlock();
    1064            }
    1065        }
    1066
    1067        /**
    1068         * {@inheritDoc}
    1069         */
    1070        @Override
    1071        public void clear(final K key) throws Exception,
    1072                UnsupportedOperationException {
    1073            final WriteLock writeLock = readWriteLock.writeLock();
    1074            writeLock.lock();
    1075            try {
    1076                keyedPool.clear(key);
    1077            } finally {
    1078                writeLock.unlock();
    1079            }
    1080        }
    1081
    1082        /**
    1083         * {@inheritDoc}
    1084         */
    1085        @Override
    1086        public void close() {
    1087            final WriteLock writeLock = readWriteLock.writeLock();
    1088            writeLock.lock();
    1089            try {
    1090                keyedPool.close();
    1091            } catch (final Exception e) {
    1092                // swallowed as of Pool 2
    1093            } finally {
    1094                writeLock.unlock();
    1095            }
    1096        }
    1097
    1098        /**
    1099         * {@inheritDoc}
    1100         */
    1101        @Override
    1102        public String toString() {
    1103            final StringBuilder sb = new StringBuilder();
    1104            sb.append("SynchronizedKeyedObjectPool");
    1105            sb.append("{keyedPool=").append(keyedPool);
    1106            sb.append('}');
    1107            return sb.toString();
    1108        }
    1109    }
    1110
    1111    /**
    1112     * A fully synchronized PooledObjectFactory that wraps a
    1113     * PooledObjectFactory and synchronizes access to the wrapped factory
    1114     * methods.
    1115     * <p>
    1116     * <b>Note:</b> This should not be used on pool implementations that already
    1117     * provide proper synchronization such as the pools provided in the Commons
    1118     * Pool library.
    1119     * </p>
    1120     *
    1121     * @param <T> pooled object factory type
    1122     */
    1123    private static final class SynchronizedPooledObjectFactory<T> implements
    1124            PooledObjectFactory<T> {
    1125
    1126        /** Synchronization lock */
    1127        private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
    1128
    1129        /** Wrapped factory */
    1130        private final PooledObjectFactory<T> factory;
    1131
    1132        /**
    1133         * Creates a SynchronizedPoolableObjectFactory wrapping the given
    1134         * factory.
    1135         *
    1136         * @param factory
    1137         *            underlying factory to wrap
    1138         * @throws IllegalArgumentException
    1139         *             if the factory is null
    1140         */
    1141        SynchronizedPooledObjectFactory(final PooledObjectFactory<T> factory)
    1142                throws IllegalArgumentException {
    1143            if (factory == null) {
    1144                throw new IllegalArgumentException("factory must not be null.");
    1145            }
    1146            this.factory = factory;
    1147        }
    1148
    1149        /**
    1150         * {@inheritDoc}
    1151         */
    1152        @Override
    1153        public PooledObject<T> makeObject() throws Exception {
    1154            writeLock.lock();
    1155            try {
    1156                return factory.makeObject();
    1157            } finally {
    1158                writeLock.unlock();
    1159            }
    1160        }
    1161
    1162        /**
    1163         * {@inheritDoc}
    1164         */
    1165        @Override
    1166        public void destroyObject(final PooledObject<T> p) throws Exception {
    1167            writeLock.lock();
    1168            try {
    1169                factory.destroyObject(p);
    1170            } finally {
    1171                writeLock.unlock();
    1172            }
    1173        }
    1174
    1175        /**
    1176         * {@inheritDoc}
    1177         */
    1178        @Override
    1179        public boolean validateObject(final PooledObject<T> p) {
    1180            writeLock.lock();
    1181            try {
    1182                return factory.validateObject(p);
    1183            } finally {
    1184                writeLock.unlock();
    1185            }
    1186        }
    1187
    1188        /**
    1189         * {@inheritDoc}
    1190         */
    1191        @Override
    1192        public void activateObject(final PooledObject<T> p) throws Exception {
    1193            writeLock.lock();
    1194            try {
    1195                factory.activateObject(p);
    1196            } finally {
    1197                writeLock.unlock();
    1198            }
    1199        }
    1200
    1201        /**
    1202         * {@inheritDoc}
    1203         */
    1204        @Override
    1205        public void passivateObject(final PooledObject<T> p) throws Exception {
    1206            writeLock.lock();
    1207            try {
    1208                factory.passivateObject(p);
    1209            } finally {
    1210                writeLock.unlock();
    1211            }
    1212        }
    1213
    1214        /**
    1215         * {@inheritDoc}
    1216         */
    1217        @Override
    1218        public String toString() {
    1219            final StringBuilder sb = new StringBuilder();
    1220            sb.append("SynchronizedPoolableObjectFactory");
    1221            sb.append("{factory=").append(factory);
    1222            sb.append('}');
    1223            return sb.toString();
    1224        }
    1225    }
    1226
    1227    /**
    1228     * A fully synchronized KeyedPooledObjectFactory that wraps a
    1229     * KeyedPooledObjectFactory and synchronizes access to the wrapped factory
    1230     * methods.
    1231     * <p>
    1232     * <b>Note:</b> This should not be used on pool implementations that already
    1233     * provide proper synchronization such as the pools provided in the Commons
    1234     * Pool library.
    1235     * </p>
    1236     *
    1237     * @param <K> pooled object factory key type
    1238     * @param <V> pooled object factory key value
    1239     */
    1240    private static final class SynchronizedKeyedPooledObjectFactory<K, V>
    1241            implements KeyedPooledObjectFactory<K, V> {
    1242
    1243        /** Synchronization lock */
    1244        private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
    1245
    1246        /** Wrapped factory */
    1247        private final KeyedPooledObjectFactory<K, V> keyedFactory;
    1248
    1249        /**
    1250         * Creates a SynchronizedKeyedPoolableObjectFactory wrapping the given
    1251         * factory.
    1252         *
    1253         * @param keyedFactory
    1254         *            underlying factory to wrap
    1255         * @throws IllegalArgumentException
    1256         *             if the factory is null
    1257         */
    1258        SynchronizedKeyedPooledObjectFactory(
    1259                final KeyedPooledObjectFactory<K, V> keyedFactory)
    1260                throws IllegalArgumentException {
    1261            if (keyedFactory == null) {
    1262                throw new IllegalArgumentException(
    1263                        "keyedFactory must not be null.");
    1264            }
    1265            this.keyedFactory = keyedFactory;
    1266        }
    1267
    1268        /**
    1269         * {@inheritDoc}
    1270         */
    1271        @Override
    1272        public PooledObject<V> makeObject(final K key) throws Exception {
    1273            writeLock.lock();
    1274            try {
    1275                return keyedFactory.makeObject(key);
    1276            } finally {
    1277                writeLock.unlock();
    1278            }
    1279        }
    1280
    1281        /**
    1282         * {@inheritDoc}
    1283         */
    1284        @Override
    1285        public void destroyObject(final K key, final PooledObject<V> p) throws Exception {
    1286            writeLock.lock();
    1287            try {
    1288                keyedFactory.destroyObject(key, p);
    1289            } finally {
    1290                writeLock.unlock();
    1291            }
    1292        }
    1293
    1294        /**
    1295         * {@inheritDoc}
    1296         */
    1297        @Override
    1298        public boolean validateObject(final K key, final PooledObject<V> p) {
    1299            writeLock.lock();
    1300            try {
    1301                return keyedFactory.validateObject(key, p);
    1302            } finally {
    1303                writeLock.unlock();
    1304            }
    1305        }
    1306
    1307        /**
    1308         * {@inheritDoc}
    1309         */
    1310        @Override
    1311        public void activateObject(final K key, final PooledObject<V> p) throws Exception {
    1312            writeLock.lock();
    1313            try {
    1314                keyedFactory.activateObject(key, p);
    1315            } finally {
    1316                writeLock.unlock();
    1317            }
    1318        }
    1319
    1320        /**
    1321         * {@inheritDoc}
    1322         */
    1323        @Override
    1324        public void passivateObject(final K key, final PooledObject<V> p) throws Exception {
    1325            writeLock.lock();
    1326            try {
    1327                keyedFactory.passivateObject(key, p);
    1328            } finally {
    1329                writeLock.unlock();
    1330            }
    1331        }
    1332
    1333        /**
    1334         * {@inheritDoc}
    1335         */
    1336        @Override
    1337        public String toString() {
    1338            final StringBuilder sb = new StringBuilder();
    1339            sb.append("SynchronizedKeyedPoolableObjectFactory");
    1340            sb.append("{keyedFactory=").append(keyedFactory);
    1341            sb.append('}');
    1342            return sb.toString();
    1343        }
    1344    }
    1345
    1346    /**
    1347     * Encapsulate the logic for when the next poolable object should be
    1348     * discarded. Each time update is called, the next time to shrink is
    1349     * recomputed, based on the float factor, number of idle instances in the
    1350     * pool and high water mark. Float factor is assumed to be between 0 and 1.
    1351     * Values closer to 1 cause less frequent erosion events. Erosion event
    1352     * timing also depends on numIdle. When this value is relatively high (close
    1353     * to previously established high water mark), erosion occurs more
    1354     * frequently.
    1355     */
    1356    private static final class ErodingFactor {
    1357        /** Determines frequency of "erosion" events */
    1358        private final float factor;
    1359
    1360        /** Time of next shrink event */
    1361        private transient volatile long nextShrink;
    1362
    1363        /** High water mark - largest numIdle encountered */
    1364        private transient volatile int idleHighWaterMark;
    1365
    1366        /**
    1367         * Creates a new ErodingFactor with the given erosion factor.
    1368         *
    1369         * @param factor
    1370         *            erosion factor
    1371         */
    1372        public ErodingFactor(final float factor) {
    1373            this.factor = factor;
    1374            nextShrink = System.currentTimeMillis() + (long) (900000 * factor); // now
    1375                                                                                // +
    1376                                                                                // 15
    1377                                                                                // min
    1378                                                                                // *
    1379                                                                                // factor
    1380            idleHighWaterMark = 1;
    1381        }
    1382
    1383        /**
    1384         * Updates internal state using the supplied time and numIdle.
    1385         *
    1386         * @param now
    1387         *            current time
    1388         * @param numIdle
    1389         *            number of idle elements in the pool
    1390         */
    1391        public void update(final long now, final int numIdle) {
    1392            final int idle = Math.max(0, numIdle);
    1393            idleHighWaterMark = Math.max(idle, idleHighWaterMark);
    1394            final float maxInterval = 15f;
    1395            final float minutes = maxInterval +
    1396                    ((1f - maxInterval) / idleHighWaterMark) * idle;
    1397            nextShrink = now + (long) (minutes * 60000f * factor);
    1398        }
    1399
    1400        /**
    1401         * Returns the time of the next erosion event.
    1402         *
    1403         * @return next shrink time
    1404         */
    1405        public long getNextShrink() {
    1406            return nextShrink;
    1407        }
    1408
    1409        /**
    1410         * {@inheritDoc}
    1411         */
    1412        @Override
    1413        public String toString() {
    1414            return "ErodingFactor{" + "factor=" + factor +
    1415                    ", idleHighWaterMark=" + idleHighWaterMark + '}';
    1416        }
    1417    }
    1418
    1419    /**
    1420     * Decorates an object pool, adding "eroding" behavior. Based on the
    1421     * configured {@link #factor erosion factor}, objects returning to the pool
    1422     * may be invalidated instead of being added to idle capacity.
    1423     *
    1424     * @param <T> type of objects in the pool
    1425     */
    1426    private static class ErodingObjectPool<T> implements ObjectPool<T> {
    1427
    1428        /** Underlying object pool */
    1429        private final ObjectPool<T> pool;
    1430
    1431        /** Erosion factor */
    1432        private final ErodingFactor factor;
    1433
    1434        /**
    1435         * Creates an ErodingObjectPool wrapping the given pool using the
    1436         * specified erosion factor.
    1437         *
    1438         * @param pool
    1439         *            underlying pool
    1440         * @param factor
    1441         *            erosion factor - determines the frequency of erosion
    1442         *            events
    1443         * @see #factor
    1444         */
    1445        public ErodingObjectPool(final ObjectPool<T> pool, final float factor) {
    1446            this.pool = pool;
    1447            this.factor = new ErodingFactor(factor);
    1448        }
    1449
    1450        /**
    1451         * {@inheritDoc}
    1452         */
    1453        @Override
    1454        public T borrowObject() throws Exception, NoSuchElementException,
    1455                IllegalStateException {
    1456            return pool.borrowObject();
    1457        }
    1458
    1459        /**
    1460         * Returns obj to the pool, unless erosion is triggered, in which case
    1461         * obj is invalidated. Erosion is triggered when there are idle
    1462         * instances in the pool and more than the {@link #factor erosion
    1463         * factor}-determined time has elapsed since the last returnObject
    1464         * activation.
    1465         *
    1466         * @param obj
    1467         *            object to return or invalidate
    1468         * @see #factor
    1469         */
    1470        @Override
    1471        public void returnObject(final T obj) {
    1472            boolean discard = false;
    1473            final long now = System.currentTimeMillis();
    1474            synchronized (pool) {
    1475                if (factor.getNextShrink() < now) { // XXX: Pool 3: move test
    1476                                                    // out of sync block
    1477                    final int numIdle = pool.getNumIdle();
    1478                    if (numIdle > 0) {
    1479                        discard = true;
    1480                    }
    1481
    1482                    factor.update(now, numIdle);
    1483                }
    1484            }
    1485            try {
    1486                if (discard) {
    1487                    pool.invalidateObject(obj);
    1488                } else {
    1489                    pool.returnObject(obj);
    1490                }
    1491            } catch (final Exception e) {
    1492                // swallowed
    1493            }
    1494        }
    1495
    1496        /**
    1497         * {@inheritDoc}
    1498         */
    1499        @Override
    1500        public void invalidateObject(final T obj) {
    1501            try {
    1502                pool.invalidateObject(obj);
    1503            } catch (final Exception e) {
    1504                // swallowed
    1505            }
    1506        }
    1507
    1508        /**
    1509         * {@inheritDoc}
    1510         */
    1511        @Override
    1512        public void addObject() throws Exception, IllegalStateException,
    1513                UnsupportedOperationException {
    1514            pool.addObject();
    1515        }
    1516
    1517        /**
    1518         * {@inheritDoc}
    1519         */
    1520        @Override
    1521        public int getNumIdle() {
    1522            return pool.getNumIdle();
    1523        }
    1524
    1525        /**
    1526         * {@inheritDoc}
    1527         */
    1528        @Override
    1529        public int getNumActive() {
    1530            return pool.getNumActive();
    1531        }
    1532
    1533        /**
    1534         * {@inheritDoc}
    1535         */
    1536        @Override
    1537        public void clear() throws Exception, UnsupportedOperationException {
    1538            pool.clear();
    1539        }
    1540
    1541        /**
    1542         * {@inheritDoc}
    1543         */
    1544        @Override
    1545        public void close() {
    1546            try {
    1547                pool.close();
    1548            } catch (final Exception e) {
    1549                // swallowed
    1550            }
    1551        }
    1552
    1553        /**
    1554         * {@inheritDoc}
    1555         */
    1556        @Override
    1557        public String toString() {
    1558            return "ErodingObjectPool{" + "factor=" + factor + ", pool=" +
    1559                    pool + '}';
    1560        }
    1561    }
    1562
    1563    /**
    1564     * Decorates a keyed object pool, adding "eroding" behavior. Based on the
    1565     * configured erosion factor, objects returning to the pool
    1566     * may be invalidated instead of being added to idle capacity.
    1567     *
    1568     * @param <K> object pool key type
    1569     * @param <V> object pool value type
    1570     */
    1571    private static class ErodingKeyedObjectPool<K, V> implements
    1572            KeyedObjectPool<K, V> {
    1573
    1574        /** Underlying pool */
    1575        private final KeyedObjectPool<K, V> keyedPool;
    1576
    1577        /** Erosion factor */
    1578        private final ErodingFactor erodingFactor;
    1579
    1580        /**
    1581         * Creates an ErodingObjectPool wrapping the given pool using the
    1582         * specified erosion factor.
    1583         *
    1584         * @param keyedPool
    1585         *            underlying pool
    1586         * @param factor
    1587         *            erosion factor - determines the frequency of erosion
    1588         *            events
    1589         * @see #erodingFactor
    1590         */
    1591        public ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
    1592                final float factor) {
    1593            this(keyedPool, new ErodingFactor(factor));
    1594        }
    1595
    1596        /**
    1597         * Creates an ErodingObjectPool wrapping the given pool using the
    1598         * specified erosion factor.
    1599         *
    1600         * @param keyedPool
    1601         *            underlying pool - must not be null
    1602         * @param erodingFactor
    1603         *            erosion factor - determines the frequency of erosion
    1604         *            events
    1605         * @see #erodingFactor
    1606         */
    1607        protected ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
    1608                final ErodingFactor erodingFactor) {
    1609            if (keyedPool == null) {
    1610                throw new IllegalArgumentException(
    1611                        MSG_NULL_KEYED_POOL);
    1612            }
    1613            this.keyedPool = keyedPool;
    1614            this.erodingFactor = erodingFactor;
    1615        }
    1616
    1617        /**
    1618         * {@inheritDoc}
    1619         */
    1620        @Override
    1621        public V borrowObject(final K key) throws Exception,
    1622                NoSuchElementException, IllegalStateException {
    1623            return keyedPool.borrowObject(key);
    1624        }
    1625
    1626        /**
    1627         * Returns obj to the pool, unless erosion is triggered, in which case
    1628         * obj is invalidated. Erosion is triggered when there are idle
    1629         * instances in the pool associated with the given key and more than the
    1630         * configured {@link #erodingFactor erosion factor} time has elapsed
    1631         * since the last returnObject activation.
    1632         *
    1633         * @param obj
    1634         *            object to return or invalidate
    1635         * @param key
    1636         *            key
    1637         * @see #erodingFactor
    1638         */
    1639        @Override
    1640        public void returnObject(final K key, final V obj) throws Exception {
    1641            boolean discard = false;
    1642            final long now = System.currentTimeMillis();
    1643            final ErodingFactor factor = getErodingFactor(key);
    1644            synchronized (keyedPool) {
    1645                if (factor.getNextShrink() < now) {
    1646                    final int numIdle = getNumIdle(key);
    1647                    if (numIdle > 0) {
    1648                        discard = true;
    1649                    }
    1650
    1651                    factor.update(now, numIdle);
    1652                }
    1653            }
    1654            try {
    1655                if (discard) {
    1656                    keyedPool.invalidateObject(key, obj);
    1657                } else {
    1658                    keyedPool.returnObject(key, obj);
    1659                }
    1660            } catch (final Exception e) {
    1661                // swallowed
    1662            }
    1663        }
    1664
    1665        /**
    1666         * Returns the eroding factor for the given key
    1667         *
    1668         * @param key
    1669         *            key
    1670         * @return eroding factor for the given keyed pool
    1671         */
    1672        protected ErodingFactor getErodingFactor(final K key) {
    1673            return erodingFactor;
    1674        }
    1675
    1676        /**
    1677         * {@inheritDoc}
    1678         */
    1679        @Override
    1680        public void invalidateObject(final K key, final V obj) {
    1681            try {
    1682                keyedPool.invalidateObject(key, obj);
    1683            } catch (final Exception e) {
    1684                // swallowed
    1685            }
    1686        }
    1687
    1688        /**
    1689         * {@inheritDoc}
    1690         */
    1691        @Override
    1692        public void addObject(final K key) throws Exception,
    1693                IllegalStateException, UnsupportedOperationException {
    1694            keyedPool.addObject(key);
    1695        }
    1696
    1697        /**
    1698         * {@inheritDoc}
    1699         */
    1700        @Override
    1701        public int getNumIdle() {
    1702            return keyedPool.getNumIdle();
    1703        }
    1704
    1705        /**
    1706         * {@inheritDoc}
    1707         */
    1708        @Override
    1709        public int getNumIdle(final K key) {
    1710            return keyedPool.getNumIdle(key);
    1711        }
    1712
    1713        /**
    1714         * {@inheritDoc}
    1715         */
    1716        @Override
    1717        public int getNumActive() {
    1718            return keyedPool.getNumActive();
    1719        }
    1720
    1721        /**
    1722         * {@inheritDoc}
    1723         */
    1724        @Override
    1725        public int getNumActive(final K key) {
    1726            return keyedPool.getNumActive(key);
    1727        }
    1728
    1729        /**
    1730         * {@inheritDoc}
    1731         */
    1732        @Override
    1733        public void clear() throws Exception, UnsupportedOperationException {
    1734            keyedPool.clear();
    1735        }
    1736
    1737        /**
    1738         * {@inheritDoc}
    1739         */
    1740        @Override
    1741        public void clear(final K key) throws Exception,
    1742                UnsupportedOperationException {
    1743            keyedPool.clear(key);
    1744        }
    1745
    1746        /**
    1747         * {@inheritDoc}
    1748         */
    1749        @Override
    1750        public void close() {
    1751            try {
    1752                keyedPool.close();
    1753            } catch (final Exception e) {
    1754                // swallowed
    1755            }
    1756        }
    1757
    1758        /**
    1759         * Returns the underlying pool
    1760         *
    1761         * @return the keyed pool that this ErodingKeyedObjectPool wraps
    1762         */
    1763        protected KeyedObjectPool<K, V> getKeyedPool() {
    1764            return keyedPool;
    1765        }
    1766
    1767        /**
    1768         * {@inheritDoc}
    1769         */
    1770        @Override
    1771        public String toString() {
    1772            return "ErodingKeyedObjectPool{" + "factor=" +
    1773                    erodingFactor + ", keyedPool=" + keyedPool + '}';
    1774        }
    1775    }
    1776
    1777    /**
    1778     * Extends ErodingKeyedObjectPool to allow erosion to take place on a
    1779     * per-key basis. Timing of erosion events is tracked separately for
    1780     * separate keyed pools.
    1781     *
    1782     * @param <K> object pool key type
    1783     * @param <V> object pool value type
    1784     */
    1785    private static final class ErodingPerKeyKeyedObjectPool<K, V> extends
    1786            ErodingKeyedObjectPool<K, V> {
    1787
    1788        /** Erosion factor - same for all pools */
    1789        private final float factor;
    1790
    1791        /** Map of ErodingFactor instances keyed on pool keys */
    1792        private final Map<K, ErodingFactor> factors = Collections.synchronizedMap(new HashMap<K, ErodingFactor>());
    1793
    1794        /**
    1795         * Creates a new ErordingPerKeyKeyedObjectPool decorating the given keyed
    1796         * pool with the specified erosion factor.
    1797         *
    1798         * @param keyedPool
    1799         *            underlying keyed pool
    1800         * @param factor
    1801         *            erosion factor
    1802         */
    1803        public ErodingPerKeyKeyedObjectPool(
    1804                final KeyedObjectPool<K, V> keyedPool, final float factor) {
    1805            super(keyedPool, null);
    1806            this.factor = factor;
    1807        }
    1808
    1809        /**
    1810         * {@inheritDoc}
    1811         */
    1812        @Override
    1813        protected ErodingFactor getErodingFactor(final K key) {
    1814            ErodingFactor eFactor = factors.get(key);
    1815            // this may result in two ErodingFactors being created for a key
    1816            // since they are small and cheap this is okay.
    1817            if (eFactor == null) {
    1818                eFactor = new ErodingFactor(this.factor);
    1819                factors.put(key, eFactor);
    1820            }
    1821            return eFactor;
    1822        }
    1823
    1824        /**
    1825         * {@inheritDoc}
    1826         */
    1827        @Override
    1828        public String toString() {
    1829            return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor +
    1830                    ", keyedPool=" + getKeyedPool() + '}';
    1831        }
    1832    }
    1833}