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