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