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