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