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