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