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