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