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