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  
18  package org.apache.commons.pool;
19  
20  import java.util.Collection;
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.Collections;
28  
29  /**
30   * This class consists exclusively of static methods that operate on or return ObjectPool
31   * or KeyedObjectPool related interfaces.
32   *
33   * @author Sandy McArthur
34   * @version $Revision: 777748 $ $Date: 2009-05-22 20:00:44 -0400 (Fri, 22 May 2009) $
35   * @since Pool 1.3
36   */
37  public final class PoolUtils {
38  
39      /**
40       * Timer used to periodically check pools idle object count.
41       * Because a {@link Timer} creates a {@link Thread} this is lazily instantiated.
42       */
43      private static Timer MIN_IDLE_TIMER;
44  
45      /**
46       * PoolUtils instances should NOT be constructed in standard programming.
47       * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);.
48       * This constructor is public to permit tools that require a JavaBean instance to operate.
49       */
50      public PoolUtils() {
51      }
52  
53      /**
54       * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
55       * needed. This method is the equivalent of calling
56       * {@link #adapt(KeyedPoolableObjectFactory, Object) PoolUtils.adapt(aKeyedPoolableObjectFactory, new Object())}.
57       *
58       * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
59       * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with an internal key.
60       * @throws IllegalArgumentException when <code>keyedFactory</code> is <code>null</code>.
61       * @see #adapt(KeyedPoolableObjectFactory, Object)
62       * @since Pool 1.3
63       */
64      public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
65          return adapt(keyedFactory, new Object());
66      }
67  
68      /**
69       * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
70       * needed using the specified <code>key</code> when delegating.
71       *
72       * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
73       * @param key the key to use when delegating.
74       * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with the specified key.
75       * @throws IllegalArgumentException when <code>keyedFactory</code> or <code>key</code> is <code>null</code>.
76       * @see #adapt(KeyedPoolableObjectFactory)
77       * @since Pool 1.3
78       */
79      public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory, final Object key) throws IllegalArgumentException {
80          return new PoolableObjectFactoryAdaptor(keyedFactory, key);
81      }
82  
83      /**
84       * Adapt a <code>PoolableObjectFactory</code> instance to work where a <code>KeyedPoolableObjectFactory</code> is
85       * needed. The key is ignored.
86       *
87       * @param factory the {@link PoolableObjectFactory} to delegate to.
88       * @return a {@link KeyedPoolableObjectFactory} that delegates to <code>factory</code> ignoring the key.
89       * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
90       * @since Pool 1.3
91       */
92      public static KeyedPoolableObjectFactory adapt(final PoolableObjectFactory factory) throws IllegalArgumentException {
93          return new KeyedPoolableObjectFactoryAdaptor(factory);
94      }
95  
96      /**
97       * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed. This is the
98       * equivalent of calling {@link #adapt(KeyedObjectPool, Object) PoolUtils.adapt(aKeyedObjectPool, new Object())}.
99       *
100      * @param keyedPool the {@link KeyedObjectPool} to delegate to.
101      * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with an internal key.
102      * @throws IllegalArgumentException when <code>keyedPool</code> is <code>null</code>.
103      * @see #adapt(KeyedObjectPool, Object)
104      * @since Pool 1.3
105      */
106     public static ObjectPool adapt(final KeyedObjectPool keyedPool) throws IllegalArgumentException {
107         return adapt(keyedPool, new Object());
108     }
109 
110     /**
111      * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed using the
112      * specified <code>key</code> when delegating.
113      *
114      * @param keyedPool the {@link KeyedObjectPool} to delegate to.
115      * @param key the key to use when delegating.
116      * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with the specified key.
117      * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
118      * @see #adapt(KeyedObjectPool)
119      * @since Pool 1.3
120      */
121     public static ObjectPool adapt(final KeyedObjectPool keyedPool, final Object key) throws IllegalArgumentException {
122         return new ObjectPoolAdaptor(keyedPool, key);
123     }
124 
125     /**
126      * Adapt an <code>ObjectPool</code> to work where an <code>KeyedObjectPool</code> is needed.
127      * The key is ignored.
128      *
129      * @param pool the {@link ObjectPool} to delegate to.
130      * @return a {@link KeyedObjectPool} that delegates to <code>pool</code> ignoring the key.
131      * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
132      * @since Pool 1.3
133      */
134     public static KeyedObjectPool adapt(final ObjectPool pool) throws IllegalArgumentException {
135         return new KeyedObjectPoolAdaptor(pool);
136     }
137 
138     /**
139      * Wraps an <code>ObjectPool</code> and dynamically checks the type of objects borrowed and returned to the pool.
140      * If an object is passed to the pool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
141      *
142      * @param pool the pool to enforce type safety on
143      * @param type the class type to enforce.
144      * @return an <code>ObjectPool</code> that will only allow objects of <code>type</code>
145      * @since Pool 1.3
146      */
147     public static ObjectPool checkedPool(final ObjectPool pool, final Class type) {
148         if (pool == null) {
149             throw new IllegalArgumentException("pool must not be null.");
150         }
151         if (type == null) {
152             throw new IllegalArgumentException("type must not be null.");
153         }
154         return new CheckedObjectPool(pool, type);
155     }
156 
157     /**
158      * Wraps a <code>KeyedObjectPool</code> and dynamically checks the type of objects borrowed and returned to the keyedPool.
159      * If an object is passed to the keyedPool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
160      *
161      * @param keyedPool the keyedPool to enforce type safety on
162      * @param type the class type to enforce.
163      * @return a <code>KeyedObjectPool</code> that will only allow objects of <code>type</code>
164      * @since Pool 1.3
165      */
166     public static KeyedObjectPool checkedPool(final KeyedObjectPool keyedPool, final Class type) {
167         if (keyedPool == null) {
168             throw new IllegalArgumentException("keyedPool must not be null.");
169         }
170         if (type == null) {
171             throw new IllegalArgumentException("type must not be null.");
172         }
173         return new CheckedKeyedObjectPool(keyedPool, type);
174     }
175 
176     /**
177      * Periodically check the idle object count for the pool. At most one idle object will be added per period.
178      * If there is an exception when calling {@link ObjectPool#addObject()} then no more checks will be performed.
179      *
180      * @param pool the pool to check periodically.
181      * @param minIdle if the {@link ObjectPool#getNumIdle()} is less than this then add an idle object.
182      * @param period the frequency to check the number of idle objects in a pool, see
183      *      {@link Timer#schedule(TimerTask, long, long)}.
184      * @return the {@link TimerTask} that will periodically check the pools idle object count.
185      * @throws IllegalArgumentException when <code>pool</code> is <code>null</code> or
186      *      when <code>minIdle</code> is negative or when <code>period</code> isn't
187      *      valid for {@link Timer#schedule(TimerTask, long, long)}.
188      * @since Pool 1.3
189      */
190     public static TimerTask checkMinIdle(final ObjectPool pool, final int minIdle, final long period) throws IllegalArgumentException {
191         if (pool == null) {
192             throw new IllegalArgumentException("keyedPool must not be null.");
193         }
194         if (minIdle < 0) {
195             throw new IllegalArgumentException("minIdle must be non-negative.");
196         }
197         final TimerTask task = new ObjectPoolMinIdleTimerTask(pool, minIdle);
198         getMinIdleTimer().schedule(task, 0L, period);
199         return task;
200     }
201 
202     /**
203      * Periodically check the idle object count for the key in the keyedPool. At most one idle object will be added per period.
204      * If there is an exception when calling {@link KeyedObjectPool#addObject(Object)} then no more checks for that key
205      * will be performed.
206      *
207      * @param keyedPool the keyedPool to check periodically.
208      * @param key the key to check the idle count of.
209      * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
210      * @param period the frequency to check the number of idle objects in a keyedPool, see
211      *      {@link Timer#schedule(TimerTask, long, long)}.
212      * @return the {@link TimerTask} that will periodically check the pools idle object count.
213      * @throws IllegalArgumentException when <code>keyedPool</code>, <code>key</code> is <code>null</code> or
214      *      when <code>minIdle</code> is negative or when <code>period</code> isn't
215      *      valid for {@link Timer#schedule(TimerTask, long, long)}.
216      * @since Pool 1.3
217      */
218     public static TimerTask checkMinIdle(final KeyedObjectPool keyedPool, final Object key, final int minIdle, final long period) throws IllegalArgumentException {
219         if (keyedPool == null) {
220             throw new IllegalArgumentException("keyedPool must not be null.");
221         }
222         if (key == null) {
223             throw new IllegalArgumentException("key must not be null.");
224         }
225         if (minIdle < 0) {
226             throw new IllegalArgumentException("minIdle must be non-negative.");
227         }
228         final TimerTask task = new KeyedObjectPoolMinIdleTimerTask(keyedPool, key, minIdle);
229         getMinIdleTimer().schedule(task, 0L, period);
230         return task;
231     }
232 
233     /**
234      * Periodically check the idle object count for each key in the <code>Collection</code> <code>keys</code> in the keyedPool.
235      * At most one idle object will be added per period.
236      *
237      * @param keyedPool the keyedPool to check periodically.
238      * @param keys a collection of keys to check the idle object count.
239      * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
240      * @param period the frequency to check the number of idle objects in a keyedPool, see
241      *      {@link Timer#schedule(TimerTask, long, long)}.
242      * @return a {@link Map} of key and {@link TimerTask} pairs that will periodically check the pools idle object count.
243      * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or any of the values in the
244      *      collection is <code>null</code> or when <code>minIdle</code> is negative or when <code>period</code> isn't
245      *      valid for {@link Timer#schedule(TimerTask, long, long)}.
246      * @see #checkMinIdle(KeyedObjectPool, Object, int, long)
247      * @since Pool 1.3
248      */
249     public static Map checkMinIdle(final KeyedObjectPool keyedPool, final Collection keys, final int minIdle, final long period) throws IllegalArgumentException {
250         if (keys == null) {
251             throw new IllegalArgumentException("keys must not be null.");
252         }
253         final Map tasks = new HashMap(keys.size());
254         final Iterator iter = keys.iterator();
255         while (iter.hasNext()) {
256             final Object key = iter.next();
257             final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
258             tasks.put(key, task);
259         }
260         return tasks;
261     }
262 
263     /**
264      * Call <code>addObject()</code> on <code>pool</code> <code>count</code> number of times.
265      *
266      * @param pool the pool to prefill.
267      * @param count the number of idle objects to add.
268      * @throws Exception when {@link ObjectPool#addObject()} fails.
269      * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
270      * @since Pool 1.3
271      */
272     public static void prefill(final ObjectPool pool, final int count) throws Exception, IllegalArgumentException {
273         if (pool == null) {
274             throw new IllegalArgumentException("pool must not be null.");
275         }
276         for (int i = 0; i < count; i++) {
277             pool.addObject();
278         }
279     }
280 
281     /**
282      * Call <code>addObject(Object)</code> on <code>keyedPool</code> with <code>key</code> <code>count</code>
283      * number of times.
284      *
285      * @param keyedPool the keyedPool to prefill.
286      * @param key the key to add objects for.
287      * @param count the number of idle objects to add for <code>key</code>.
288      * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
289      * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
290      * @since Pool 1.3
291      */
292     public static void prefill(final KeyedObjectPool keyedPool, final Object key, final int count) throws Exception, IllegalArgumentException {
293         if (keyedPool == null) {
294             throw new IllegalArgumentException("keyedPool must not be null.");
295         }
296         if (key == null) {
297             throw new IllegalArgumentException("key must not be null.");
298         }
299         for (int i = 0; i < count; i++) {
300             keyedPool.addObject(key);
301         }
302     }
303 
304     /**
305      * Call <code>addObject(Object)</code> on <code>keyedPool</code> with each key in <code>keys</code> for
306      * <code>count</code> number of times. This has the same effect as calling
307      * {@link #prefill(KeyedObjectPool, Object, int)} for each key in the <code>keys</code> collection.
308      *
309      * @param keyedPool the keyedPool to prefill.
310      * @param keys {@link Collection} of keys to add objects for.
311      * @param count the number of idle objects to add for each <code>key</code>.
312      * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
313      * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or
314      *      any value in <code>keys</code> is <code>null</code>.
315      * @see #prefill(KeyedObjectPool, Object, int)
316      * @since Pool 1.3
317      */
318     public static void prefill(final KeyedObjectPool keyedPool, final Collection keys, final int count) throws Exception, IllegalArgumentException {
319         if (keys == null) {
320             throw new IllegalArgumentException("keys must not be null.");
321         }
322         final Iterator iter = keys.iterator();
323         while (iter.hasNext()) {
324             prefill(keyedPool, iter.next(), count);
325         }
326     }
327 
328     /**
329      * Returns a synchronized (thread-safe) ObjectPool backed by the specified ObjectPool.
330      *
331      * <p><b>Note:</b>
332      * This should not be used on pool implementations that already provide proper synchronization
333      * such as the pools provided in the Commons Pool library. Wrapping a pool that
334      * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
335      * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
336      * </p>
337      *
338      * @param pool the ObjectPool to be "wrapped" in a synchronized ObjectPool.
339      * @return a synchronized view of the specified ObjectPool.
340      * @since Pool 1.3
341      */
342     public static ObjectPool synchronizedPool(final ObjectPool pool) {
343         if (pool == null) {
344             throw new IllegalArgumentException("pool must not be null.");
345         }
346         /*
347         assert !(pool instanceof GenericObjectPool)
348                 : "GenericObjectPool is already thread-safe";
349         assert !(pool instanceof SoftReferenceObjectPool)
350                 : "SoftReferenceObjectPool is already thread-safe";
351         assert !(pool instanceof StackObjectPool)
352                 : "StackObjectPool is already thread-safe";
353         assert !"org.apache.commons.pool.composite.CompositeObjectPool".equals(pool.getClass().getName())
354                 : "CompositeObjectPools are already thread-safe";
355         */
356         return new SynchronizedObjectPool(pool);
357     }
358 
359     /**
360      * Returns a synchronized (thread-safe) KeyedObjectPool backed by the specified KeyedObjectPool.
361      *
362      * <p><b>Note:</b>
363      * This should not be used on pool implementations that already provide proper synchronization
364      * such as the pools provided in the Commons Pool library. Wrapping a pool that
365      * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
366      * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
367      * </p>
368      *
369      * @param keyedPool the KeyedObjectPool to be "wrapped" in a synchronized KeyedObjectPool.
370      * @return a synchronized view of the specified KeyedObjectPool.
371      * @since Pool 1.3
372      */
373     public static KeyedObjectPool synchronizedPool(final KeyedObjectPool keyedPool) {
374         if (keyedPool == null) {
375             throw new IllegalArgumentException("keyedPool must not be null.");
376         }
377         /*
378         assert !(keyedPool instanceof GenericKeyedObjectPool)
379                 : "GenericKeyedObjectPool is already thread-safe";
380         assert !(keyedPool instanceof StackKeyedObjectPool)
381                 : "StackKeyedObjectPool is already thread-safe";
382         assert !"org.apache.commons.pool.composite.CompositeKeyedObjectPool".equals(keyedPool.getClass().getName())
383                 : "CompositeKeyedObjectPools are already thread-safe";
384         */
385         return new SynchronizedKeyedObjectPool(keyedPool);
386     }
387 
388     /**
389      * Returns a synchronized (thread-safe) PoolableObjectFactory backed by the specified PoolableObjectFactory.
390      *
391      * @param factory the PoolableObjectFactory to be "wrapped" in a synchronized PoolableObjectFactory.
392      * @return a synchronized view of the specified PoolableObjectFactory.
393      * @since Pool 1.3
394      */
395     public static PoolableObjectFactory synchronizedPoolableFactory(final PoolableObjectFactory factory) {
396         return new SynchronizedPoolableObjectFactory(factory);
397     }
398 
399     /**
400      * Returns a synchronized (thread-safe) KeyedPoolableObjectFactory backed by the specified KeyedPoolableObjectFactory.
401      *
402      * @param keyedFactory the KeyedPoolableObjectFactory to be "wrapped" in a synchronized KeyedPoolableObjectFactory.
403      * @return a synchronized view of the specified KeyedPoolableObjectFactory.
404      * @since Pool 1.3
405      */
406     public static KeyedPoolableObjectFactory synchronizedPoolableFactory(final KeyedPoolableObjectFactory keyedFactory) {
407         return new SynchronizedKeyedPoolableObjectFactory(keyedFactory);
408     }
409 
410     /**
411      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
412      * This is intended as an always thread-safe alternative to using an idle object evictor
413      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
414      * pools that experience load spikes.
415      *
416      * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
417      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
418      * @see #erodingPool(ObjectPool, float)
419      * @since Pool 1.4
420      */
421     public static ObjectPool erodingPool(final ObjectPool pool) {
422         return erodingPool(pool, 1f);
423     }
424 
425     /**
426      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
427      * This is intended as an always thread-safe alternative to using an idle object evictor
428      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
429      * pools that experience load spikes.
430      *
431      * <p>
432      * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
433      * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
434      * Values greater than 1 cause the pool to less frequently try to shrink it's size.
435      * </p>
436      *
437      * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
438      * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
439      * If 0 &lt; factor &lt; 1 then the pool shrinks more aggressively.
440      * If 1 &lt; factor then the pool shrinks less aggressively.
441      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
442      * @see #erodingPool(ObjectPool)
443      * @since Pool 1.4
444      */
445     public static ObjectPool erodingPool(final ObjectPool pool, final float factor) {
446         if (pool == null) {
447             throw new IllegalArgumentException("pool must not be null.");
448         }
449         if (factor <= 0f) {
450             throw new IllegalArgumentException("factor must be positive.");
451         }
452         return new ErodingObjectPool(pool, factor);
453     }
454 
455     /**
456      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
457      * This is intended as an always thread-safe alternative to using an idle object evictor
458      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
459      * pools that experience load spikes.
460      *
461      * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
462      * possible.
463      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
464      * @see #erodingPool(KeyedObjectPool, float)
465      * @see #erodingPool(KeyedObjectPool, float, boolean)
466      * @since Pool 1.4
467      */
468     public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool) {
469         return erodingPool(keyedPool, 1f);
470     }
471 
472     /**
473      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
474      * This is intended as an always thread-safe alternative to using an idle object evictor
475      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
476      * pools that experience load spikes.
477      *
478      * <p>
479      * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
480      * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
481      * Values greater than 1 cause the pool to less frequently try to shrink it's size.
482      * </p>
483      *
484      * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
485      * possible.
486      * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
487      * If 0 &lt; factor &lt; 1 then the pool shrinks more aggressively.
488      * If 1 &lt; factor then the pool shrinks less aggressively.
489      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
490      * @see #erodingPool(KeyedObjectPool, float, boolean)
491      * @since Pool 1.4
492      */
493     public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool, final float factor) {
494         return erodingPool(keyedPool, factor, false);
495     }
496 
497     /**
498      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
499      * This is intended as an always thread-safe alternative to using an idle object evictor
500      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
501      * pools that experience load spikes.
502      *
503      * <p>
504      * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
505      * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
506      * Values greater than 1 cause the pool to less frequently try to shrink it's size.
507      * </p>
508      *
509      * <p>
510      * The perKey parameter determines if the pool shrinks on a whole pool basis or a per key basis.
511      * When perKey is false, the keys do not have an effect on the rate at which the pool tries to
512      * shrink it's size. When perKey is true, each key is shrunk independently.
513      * </p>
514      *
515      * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
516      * possible.
517      * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
518      * If 0 &lt; factor &lt; 1 then the pool shrinks more aggressively.
519      * If 1 &lt; factor then the pool shrinks less aggressively.
520      * @param perKey when true, each key is treated independently.
521      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
522      * @see #erodingPool(KeyedObjectPool)
523      * @see #erodingPool(KeyedObjectPool, float)
524      * @since Pool 1.4
525      */
526     public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool, final float factor, final boolean perKey) {
527         if (keyedPool == null) {
528             throw new IllegalArgumentException("keyedPool must not be null.");
529         }
530         if (factor <= 0f) {
531             throw new IllegalArgumentException("factor must be positive.");
532         }
533         if (perKey) {
534             return new ErodingPerKeyKeyedObjectPool(keyedPool, factor);
535         } else {
536             return new ErodingKeyedObjectPool(keyedPool, factor);
537         }
538     }
539 
540     /**
541      * Get the <code>Timer</code> for checking keyedPool's idle count. Lazily create the {@link Timer} as needed.
542      *
543      * @return the {@link Timer} for checking keyedPool's idle count.
544      * @since Pool 1.3
545      */
546     private static synchronized Timer getMinIdleTimer() {
547         if (MIN_IDLE_TIMER == null) {
548             MIN_IDLE_TIMER = new Timer(true);
549         }
550         return MIN_IDLE_TIMER;
551     }
552 
553     private static class PoolableObjectFactoryAdaptor implements PoolableObjectFactory {
554         private final Object key;
555         private final KeyedPoolableObjectFactory keyedFactory;
556 
557         PoolableObjectFactoryAdaptor(final KeyedPoolableObjectFactory keyedFactory, final Object key) throws IllegalArgumentException {
558             if (keyedFactory == null) {
559                 throw new IllegalArgumentException("keyedFactory must not be null.");
560             }
561             if (key == null) {
562                 throw new IllegalArgumentException("key must not be null.");
563             }
564             this.keyedFactory = keyedFactory;
565             this.key = key;
566         }
567 
568         public Object makeObject() throws Exception {
569             return keyedFactory.makeObject(key);
570         }
571 
572         public void destroyObject(final Object obj) throws Exception {
573             keyedFactory.destroyObject(key, obj);
574         }
575 
576         public boolean validateObject(final Object obj) {
577             return keyedFactory.validateObject(key, obj);
578         }
579 
580         public void activateObject(final Object obj) throws Exception {
581             keyedFactory.activateObject(key, obj);
582         }
583 
584         public void passivateObject(final Object obj) throws Exception {
585             keyedFactory.passivateObject(key, obj);
586         }
587 
588         public String toString() {
589             final StringBuffer sb = new StringBuffer();
590             sb.append("PoolableObjectFactoryAdaptor");
591             sb.append("{key=").append(key);
592             sb.append(", keyedFactory=").append(keyedFactory);
593             sb.append('}');
594             return sb.toString();
595         }
596     }
597 
598     private static class KeyedPoolableObjectFactoryAdaptor implements KeyedPoolableObjectFactory {
599         private final PoolableObjectFactory factory;
600 
601         KeyedPoolableObjectFactoryAdaptor(final PoolableObjectFactory factory) throws IllegalArgumentException {
602             if (factory == null) {
603                 throw new IllegalArgumentException("factory must not be null.");
604             }
605             this.factory = factory;
606         }
607 
608         public Object makeObject(final Object key) throws Exception {
609             return factory.makeObject();
610         }
611 
612         public void destroyObject(final Object key, final Object obj) throws Exception {
613             factory.destroyObject(obj);
614         }
615 
616         public boolean validateObject(final Object key, final Object obj) {
617             return factory.validateObject(obj);
618         }
619 
620         public void activateObject(final Object key, final Object obj) throws Exception {
621             factory.activateObject(obj);
622         }
623 
624         public void passivateObject(final Object key, final Object obj) throws Exception {
625             factory.passivateObject(obj);
626         }
627 
628         public String toString() {
629             final StringBuffer sb = new StringBuffer();
630             sb.append("KeyedPoolableObjectFactoryAdaptor");
631             sb.append("{factory=").append(factory);
632             sb.append('}');
633             return sb.toString();
634         }
635     }
636 
637     private static class ObjectPoolAdaptor implements ObjectPool {
638         private final Object key;
639         private final KeyedObjectPool keyedPool;
640 
641         ObjectPoolAdaptor(final KeyedObjectPool keyedPool, final Object key) throws IllegalArgumentException {
642             if (keyedPool == null) {
643                 throw new IllegalArgumentException("keyedPool must not be null.");
644             }
645             if (key == null) {
646                 throw new IllegalArgumentException("key must not be null.");
647             }
648             this.keyedPool = keyedPool;
649             this.key = key;
650         }
651 
652         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
653             return keyedPool.borrowObject(key);
654         }
655 
656         public void returnObject(final Object obj) {
657             try {
658                 keyedPool.returnObject(key, obj);
659             } catch (Exception e) {
660                 // swallowed as of Pool 2
661             }
662         }
663 
664         public void invalidateObject(final Object obj) {
665             try {
666                 keyedPool.invalidateObject(key, obj);
667             } catch (Exception e) {
668                 // swallowed as of Pool 2
669             }
670         }
671 
672         public void addObject() throws Exception, IllegalStateException {
673             keyedPool.addObject(key);
674         }
675 
676         public int getNumIdle() throws UnsupportedOperationException {
677             return keyedPool.getNumIdle(key);
678         }
679 
680         public int getNumActive() throws UnsupportedOperationException {
681             return keyedPool.getNumActive(key);
682         }
683 
684         public void clear() throws Exception, UnsupportedOperationException {
685             keyedPool.clear();
686         }
687 
688         public void close() {
689             try {
690                 keyedPool.close();
691             } catch (Exception e) {
692                 // swallowed as of Pool 2
693             }
694         }
695 
696         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
697             keyedPool.setFactory(adapt(factory));
698         }
699 
700         public String toString() {
701             final StringBuffer sb = new StringBuffer();
702             sb.append("ObjectPoolAdaptor");
703             sb.append("{key=").append(key);
704             sb.append(", keyedPool=").append(keyedPool);
705             sb.append('}');
706             return sb.toString();
707         }
708     }
709 
710     private static class KeyedObjectPoolAdaptor implements KeyedObjectPool {
711         private final ObjectPool pool;
712 
713         KeyedObjectPoolAdaptor(final ObjectPool pool) throws IllegalArgumentException {
714             if (pool == null) {
715                 throw new IllegalArgumentException("pool must not be null.");
716             }
717             this.pool = pool;
718         }
719 
720         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
721             return pool.borrowObject();
722         }
723 
724         public void returnObject(final Object key, final Object obj) {
725             try {
726                 pool.returnObject(obj);
727             } catch (Exception e) {
728                 // swallowed as of Pool 2
729             }
730         }
731 
732         public void invalidateObject(final Object key, final Object obj) {
733             try {
734                 pool.invalidateObject(obj);
735             } catch (Exception e) {
736                 // swallowed as of Pool 2
737             }
738         }
739 
740         public void addObject(final Object key) throws Exception, IllegalStateException {
741             pool.addObject();
742         }
743 
744         public int getNumIdle(final Object key) throws UnsupportedOperationException {
745             return pool.getNumIdle();
746         }
747 
748         public int getNumActive(final Object key) throws UnsupportedOperationException {
749             return pool.getNumActive();
750         }
751 
752         public int getNumIdle() throws UnsupportedOperationException {
753             return pool.getNumIdle();
754         }
755 
756         public int getNumActive() throws UnsupportedOperationException {
757             return pool.getNumActive();
758         }
759 
760         public void clear() throws Exception, UnsupportedOperationException {
761             pool.clear();
762         }
763 
764         public void clear(final Object key) throws Exception, UnsupportedOperationException {
765             pool.clear();
766         }
767 
768         public void close() {
769             try {
770                 pool.close();
771             } catch (Exception e) {
772                 // swallowed as of Pool 2
773             }
774         }
775 
776         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
777             pool.setFactory(adapt(factory));
778         }
779 
780         public String toString() {
781             final StringBuffer sb = new StringBuffer();
782             sb.append("KeyedObjectPoolAdaptor");
783             sb.append("{pool=").append(pool);
784             sb.append('}');
785             return sb.toString();
786         }
787     }
788 
789     private static class CheckedObjectPool implements ObjectPool {
790         private final Class type;
791         private final ObjectPool pool;
792 
793         CheckedObjectPool(final ObjectPool pool, final Class type) {
794             if (pool == null) {
795                 throw new IllegalArgumentException("pool must not be null.");
796             }
797             if (type == null) {
798                 throw new IllegalArgumentException("type must not be null.");
799             }
800             this.pool = pool;
801             this.type = type;
802         }
803 
804         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
805             final Object obj = pool.borrowObject();
806             if (type.isInstance(obj)) {
807                 return obj;
808             } else {
809                 throw new ClassCastException("Borrowed object is not of type: " + type.getName() + " was: " + obj);
810             }
811         }
812 
813         public void returnObject(final Object obj) {
814             if (type.isInstance(obj)) {
815                 try {
816                     pool.returnObject(obj);
817                 } catch (Exception e) {
818                     // swallowed as of Pool 2
819                 }
820             } else {
821                 throw new ClassCastException("Returned object is not of type: " + type.getName() + " was: " + obj);
822             }
823         }
824 
825         public void invalidateObject(final Object obj) {
826             if (type.isInstance(obj)) {
827                 try {
828                     pool.invalidateObject(obj);
829                 } catch (Exception e) {
830                     // swallowed as of Pool 2
831                 }
832             } else {
833                 throw new ClassCastException("Invalidated object is not of type: " + type.getName() + " was: " + obj);
834             }
835         }
836 
837         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
838             pool.addObject();
839         }
840 
841         public int getNumIdle() throws UnsupportedOperationException {
842             return pool.getNumIdle();
843         }
844 
845         public int getNumActive() throws UnsupportedOperationException {
846             return pool.getNumActive();
847         }
848 
849         public void clear() throws Exception, UnsupportedOperationException {
850             pool.clear();
851         }
852 
853         public void close() {
854             try {
855                 pool.close();
856             } catch (Exception e) {
857                 // swallowed as of Pool 2
858             }
859         }
860 
861         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
862             pool.setFactory(factory);
863         }
864 
865         public String toString() {
866             final StringBuffer sb = new StringBuffer();
867             sb.append("CheckedObjectPool");
868             sb.append("{type=").append(type);
869             sb.append(", pool=").append(pool);
870             sb.append('}');
871             return sb.toString();
872         }
873     }
874 
875     private static class CheckedKeyedObjectPool implements KeyedObjectPool {
876         private final Class type;
877         private final KeyedObjectPool keyedPool;
878 
879         CheckedKeyedObjectPool(final KeyedObjectPool keyedPool, final Class type) {
880             if (keyedPool == null) {
881                 throw new IllegalArgumentException("keyedPool must not be null.");
882             }
883             if (type == null) {
884                 throw new IllegalArgumentException("type must not be null.");
885             }
886             this.keyedPool = keyedPool;
887             this.type = type;
888         }
889 
890         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
891             Object obj = keyedPool.borrowObject(key);
892             if (type.isInstance(obj)) {
893                 return obj;
894             } else {
895                 throw new ClassCastException("Borrowed object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
896             }
897         }
898 
899         public void returnObject(final Object key, final Object obj) {
900             if (type.isInstance(obj)) {
901                 try {
902                     keyedPool.returnObject(key, obj);
903                 } catch (Exception e) {
904                     // swallowed as of Pool 2
905                 }
906             } else {
907                 throw new ClassCastException("Returned object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
908             }
909         }
910 
911         public void invalidateObject(final Object key, final Object obj) {
912             if (type.isInstance(obj)) {
913                 try {
914                     keyedPool.invalidateObject(key, obj);
915                 } catch (Exception e) {
916                     // swallowed as of Pool 2
917                 }
918             } else {
919                 throw new ClassCastException("Invalidated object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
920             }
921         }
922 
923         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
924             keyedPool.addObject(key);
925         }
926 
927         public int getNumIdle(final Object key) throws UnsupportedOperationException {
928             return keyedPool.getNumIdle(key);
929         }
930 
931         public int getNumActive(final Object key) throws UnsupportedOperationException {
932             return keyedPool.getNumActive(key);
933         }
934 
935         public int getNumIdle() throws UnsupportedOperationException {
936             return keyedPool.getNumIdle();
937         }
938 
939         public int getNumActive() throws UnsupportedOperationException {
940             return keyedPool.getNumActive();
941         }
942 
943         public void clear() throws Exception, UnsupportedOperationException {
944             keyedPool.clear();
945         }
946 
947         public void clear(final Object key) throws Exception, UnsupportedOperationException {
948             keyedPool.clear(key);
949         }
950 
951         public void close() {
952             try {
953                 keyedPool.close();
954             } catch (Exception e) {
955                 // swallowed as of Pool 2
956             }
957         }
958 
959         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
960             keyedPool.setFactory(factory);
961         }
962 
963         public String toString() {
964             final StringBuffer sb = new StringBuffer();
965             sb.append("CheckedKeyedObjectPool");
966             sb.append("{type=").append(type);
967             sb.append(", keyedPool=").append(keyedPool);
968             sb.append('}');
969             return sb.toString();
970         }
971     }
972 
973     private static class ObjectPoolMinIdleTimerTask extends TimerTask {
974         private final int minIdle;
975         private final ObjectPool pool;
976 
977         ObjectPoolMinIdleTimerTask(final ObjectPool pool, final int minIdle) throws IllegalArgumentException {
978             if (pool == null) {
979                 throw new IllegalArgumentException("pool must not be null.");
980             }
981             this.pool = pool;
982             this.minIdle = minIdle;
983         }
984 
985         public void run() {
986             boolean success = false;
987             try {
988                 if (pool.getNumIdle() < minIdle) {
989                     pool.addObject();
990                 }
991                 success = true;
992 
993             } catch (Exception e) {
994                 cancel();
995 
996             } finally {
997                 // detect other types of Throwable and cancel this Timer
998                 if (!success) {
999                     cancel();
1000                 }
1001             }
1002         }
1003 
1004         public String toString() {
1005             final StringBuffer sb = new StringBuffer();
1006             sb.append("ObjectPoolMinIdleTimerTask");
1007             sb.append("{minIdle=").append(minIdle);
1008             sb.append(", pool=").append(pool);
1009             sb.append('}');
1010             return sb.toString();
1011         }
1012     }
1013 
1014     private static class KeyedObjectPoolMinIdleTimerTask extends TimerTask {
1015         private final int minIdle;
1016         private final Object key;
1017         private final KeyedObjectPool keyedPool;
1018 
1019         KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool keyedPool, final Object key, final int minIdle) throws IllegalArgumentException {
1020             if (keyedPool == null) {
1021                 throw new IllegalArgumentException("keyedPool must not be null.");
1022             }
1023             this.keyedPool = keyedPool;
1024             this.key = key;
1025             this.minIdle = minIdle;
1026         }
1027 
1028         public void run() {
1029             boolean success = false;
1030             try {
1031                 if (keyedPool.getNumIdle(key) < minIdle) {
1032                     keyedPool.addObject(key);
1033                 }
1034                 success = true;
1035 
1036             } catch (Exception e) {
1037                 cancel();
1038 
1039             } finally {
1040                 // detect other types of Throwable and cancel this Timer
1041                 if (!success) {
1042                     cancel();
1043                 }
1044             }
1045         }
1046 
1047         public String toString() {
1048             final StringBuffer sb = new StringBuffer();
1049             sb.append("KeyedObjectPoolMinIdleTimerTask");
1050             sb.append("{minIdle=").append(minIdle);
1051             sb.append(", key=").append(key);
1052             sb.append(", keyedPool=").append(keyedPool);
1053             sb.append('}');
1054             return sb.toString();
1055         }
1056     }
1057 
1058     private static class SynchronizedObjectPool implements ObjectPool {
1059         private final Object lock;
1060         private final ObjectPool pool;
1061 
1062         SynchronizedObjectPool(final ObjectPool pool) throws IllegalArgumentException {
1063             if (pool == null) {
1064                 throw new IllegalArgumentException("pool must not be null.");
1065             }
1066             this.pool = pool;
1067             lock = new Object();
1068         }
1069 
1070         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
1071             synchronized (lock) {
1072                 return pool.borrowObject();
1073             }
1074         }
1075 
1076         public void returnObject(final Object obj) {
1077             synchronized (lock) {
1078                 try {
1079                     pool.returnObject(obj);
1080                 } catch (Exception e) {
1081                     // swallowed as of Pool 2
1082                 }
1083             }
1084         }
1085 
1086         public void invalidateObject(final Object obj) {
1087             synchronized (lock) {
1088                 try {
1089                     pool.invalidateObject(obj);
1090                 } catch (Exception e) {
1091                     // swallowed as of Pool 2
1092                 }
1093             }
1094         }
1095 
1096         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
1097             synchronized (lock) {
1098                 pool.addObject();
1099             }
1100         }
1101 
1102         public int getNumIdle() throws UnsupportedOperationException {
1103             synchronized (lock) {
1104                 return pool.getNumIdle();
1105             }
1106         }
1107 
1108         public int getNumActive() throws UnsupportedOperationException {
1109             synchronized (lock) {
1110                 return pool.getNumActive();
1111             }
1112         }
1113 
1114         public void clear() throws Exception, UnsupportedOperationException {
1115             synchronized (lock) {
1116                 pool.clear();
1117             }
1118         }
1119 
1120         public void close() {
1121             try {
1122                 synchronized (lock) {
1123                     pool.close();
1124                 }
1125             } catch (Exception e) {
1126                 // swallowed as of Pool 2
1127             }
1128         }
1129 
1130         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1131             synchronized (lock) {
1132                 pool.setFactory(factory);
1133             }
1134         }
1135 
1136         public String toString() {
1137             final StringBuffer sb = new StringBuffer();
1138             sb.append("SynchronizedObjectPool");
1139             sb.append("{pool=").append(pool);
1140             sb.append('}');
1141             return sb.toString();
1142         }
1143     }
1144 
1145     private static class SynchronizedKeyedObjectPool implements KeyedObjectPool {
1146         private final Object lock;
1147         private final KeyedObjectPool keyedPool;
1148 
1149         SynchronizedKeyedObjectPool(final KeyedObjectPool keyedPool) throws IllegalArgumentException {
1150             if (keyedPool == null) {
1151                 throw new IllegalArgumentException("keyedPool must not be null.");
1152             }
1153             this.keyedPool = keyedPool;
1154             lock = new Object();
1155         }
1156 
1157         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
1158             synchronized (lock) {
1159                 return keyedPool.borrowObject(key);
1160             }
1161         }
1162 
1163         public void returnObject(final Object key, final Object obj) {
1164             synchronized (lock) {
1165                 try {
1166                     keyedPool.returnObject(key, obj);
1167                 } catch (Exception e) {
1168                     // swallowed
1169                 }
1170             }
1171         }
1172 
1173         public void invalidateObject(final Object key, final Object obj) {
1174             synchronized (lock) {
1175                 try {
1176                     keyedPool.invalidateObject(key, obj);
1177                 } catch (Exception e) {
1178                     // swallowed as of Pool 2
1179                 }
1180             }
1181         }
1182 
1183         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
1184             synchronized (lock) {
1185                 keyedPool.addObject(key);
1186             }
1187         }
1188 
1189         public int getNumIdle(final Object key) throws UnsupportedOperationException {
1190             synchronized (lock) {
1191                 return keyedPool.getNumIdle(key);
1192             }
1193         }
1194 
1195         public int getNumActive(final Object key) throws UnsupportedOperationException {
1196             synchronized (lock) {
1197                 return keyedPool.getNumActive(key);
1198             }
1199         }
1200 
1201         public int getNumIdle() throws UnsupportedOperationException {
1202             synchronized (lock) {
1203                 return keyedPool.getNumIdle();
1204             }
1205         }
1206 
1207         public int getNumActive() throws UnsupportedOperationException {
1208             synchronized (lock) {
1209                 return keyedPool.getNumActive();
1210             }
1211         }
1212 
1213         public void clear() throws Exception, UnsupportedOperationException {
1214             synchronized (lock) {
1215                 keyedPool.clear();
1216             }
1217         }
1218 
1219         public void clear(final Object key) throws Exception, UnsupportedOperationException {
1220             synchronized (lock) {
1221                 keyedPool.clear(key);
1222             }
1223         }
1224 
1225         public void close() {
1226             try {
1227                 synchronized (lock) {
1228                     keyedPool.close();
1229                 }
1230             } catch (Exception e) {
1231                 // swallowed as of Pool 2
1232             }
1233         }
1234 
1235         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1236             synchronized (lock) {
1237                 keyedPool.setFactory(factory);
1238             }
1239         }
1240 
1241         public String toString() {
1242             final StringBuffer sb = new StringBuffer();
1243             sb.append("SynchronizedKeyedObjectPool");
1244             sb.append("{keyedPool=").append(keyedPool);
1245             sb.append('}');
1246             return sb.toString();
1247         }
1248     }
1249 
1250     private static class SynchronizedPoolableObjectFactory implements PoolableObjectFactory {
1251         private final Object lock;
1252         private final PoolableObjectFactory factory;
1253 
1254         SynchronizedPoolableObjectFactory(final PoolableObjectFactory factory) throws IllegalArgumentException {
1255             if (factory == null) {
1256                 throw new IllegalArgumentException("factory must not be null.");
1257             }
1258             this.factory = factory;
1259             lock = new Object();
1260         }
1261 
1262         public Object makeObject() throws Exception {
1263             synchronized (lock) {
1264                 return factory.makeObject();
1265             }
1266         }
1267 
1268         public void destroyObject(final Object obj) throws Exception {
1269             synchronized (lock) {
1270                 factory.destroyObject(obj);
1271             }
1272         }
1273 
1274         public boolean validateObject(final Object obj) {
1275             synchronized (lock) {
1276                 return factory.validateObject(obj);
1277             }
1278         }
1279 
1280         public void activateObject(final Object obj) throws Exception {
1281             synchronized (lock) {
1282                 factory.activateObject(obj);
1283             }
1284         }
1285 
1286         public void passivateObject(final Object obj) throws Exception {
1287             synchronized (lock) {
1288                 factory.passivateObject(obj);
1289             }
1290         }
1291 
1292         public String toString() {
1293             final StringBuffer sb = new StringBuffer();
1294             sb.append("SynchronizedPoolableObjectFactory");
1295             sb.append("{factory=").append(factory);
1296             sb.append('}');
1297             return sb.toString();
1298         }
1299     }
1300 
1301     private static class SynchronizedKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory {
1302         private final Object lock;
1303         private final KeyedPoolableObjectFactory keyedFactory;
1304 
1305         SynchronizedKeyedPoolableObjectFactory(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
1306             if (keyedFactory == null) {
1307                 throw new IllegalArgumentException("keyedFactory must not be null.");
1308             }
1309             this.keyedFactory = keyedFactory;
1310             lock = new Object();
1311         }
1312 
1313         public Object makeObject(final Object key) throws Exception {
1314             synchronized (lock) {
1315                 return keyedFactory.makeObject(key);
1316             }
1317         }
1318 
1319         public void destroyObject(final Object key, final Object obj) throws Exception {
1320             synchronized (lock) {
1321                 keyedFactory.destroyObject(key, obj);
1322             }
1323         }
1324 
1325         public boolean validateObject(final Object key, final Object obj) {
1326             synchronized (lock) {
1327                 return keyedFactory.validateObject(key, obj);
1328             }
1329         }
1330 
1331         public void activateObject(final Object key, final Object obj) throws Exception {
1332             synchronized (lock) {
1333                 keyedFactory.activateObject(key, obj);
1334             }
1335         }
1336 
1337         public void passivateObject(final Object key, final Object obj) throws Exception {
1338             synchronized (lock) {
1339                 keyedFactory.passivateObject(key, obj);
1340             }
1341         }
1342 
1343         public String toString() {
1344             final StringBuffer sb = new StringBuffer();
1345             sb.append("SynchronizedKeyedPoolableObjectFactory");
1346             sb.append("{keyedFactory=").append(keyedFactory);
1347             sb.append('}');
1348             return sb.toString();
1349         }
1350     }
1351 
1352     /**
1353      * Encapsulate the logic for when the next poolable object should be discarded.
1354      */
1355     private static class ErodingFactor {
1356         private final float factor;
1357         private transient volatile long nextShrink;
1358         private transient volatile int idleHighWaterMark;
1359 
1360         public ErodingFactor(final float factor) {
1361             this.factor = factor;
1362             nextShrink = System.currentTimeMillis() + (long)(900000 * factor); // now + 15 min * factor
1363             idleHighWaterMark = 1;
1364         }
1365 
1366         public void update(final int numIdle) {
1367             update(System.currentTimeMillis(), numIdle);
1368         }
1369 
1370         public void update(final long now, final int numIdle) {
1371             final int idle = Math.max(0, numIdle);
1372             idleHighWaterMark = Math.max(idle, idleHighWaterMark);
1373             final float maxInterval = 15f;
1374             final float minutes = maxInterval + ((1f-maxInterval)/idleHighWaterMark) * idle;
1375             nextShrink = now + (long)(minutes * 60000f * factor);
1376         }
1377 
1378         public long getNextShrink() {
1379             return nextShrink;
1380         }
1381 
1382         public String toString() {
1383             return "ErodingFactor{" +
1384                     "factor=" + factor +
1385                     ", idleHighWaterMark=" + idleHighWaterMark +
1386                     '}';
1387         }
1388     }
1389 
1390     private static class ErodingObjectPool implements ObjectPool {
1391         private final ObjectPool pool;
1392         private final ErodingFactor factor;
1393 
1394         public ErodingObjectPool(final ObjectPool pool, final float factor) {
1395             this.pool = pool;
1396             this.factor = new ErodingFactor(factor);
1397         }
1398 
1399         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
1400             return pool.borrowObject();
1401         }
1402 
1403         public void returnObject(final Object obj) {
1404             boolean discard = false;
1405             final long now = System.currentTimeMillis();
1406             synchronized (pool) {
1407                 if (factor.getNextShrink() < now) { // XXX: Pool 3: move test out of sync block
1408                     final int numIdle = pool.getNumIdle();
1409                     if (numIdle > 0) {
1410                         discard = true;
1411                     }
1412 
1413                     factor.update(now, numIdle);
1414                 }
1415             }
1416             try {
1417                 if (discard) {
1418                     pool.invalidateObject(obj);
1419                 } else {
1420                     pool.returnObject(obj);
1421                 }
1422             } catch (Exception e) {
1423                 // swallowed
1424             }
1425         }
1426 
1427         public void invalidateObject(final Object obj) {
1428             try {
1429                 pool.invalidateObject(obj);
1430             } catch (Exception e) {
1431                 // swallowed
1432             }
1433         }
1434 
1435         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
1436             pool.addObject();
1437         }
1438 
1439         public int getNumIdle() throws UnsupportedOperationException {
1440             return pool.getNumIdle();
1441         }
1442 
1443         public int getNumActive() throws UnsupportedOperationException {
1444             return pool.getNumActive();
1445         }
1446 
1447         public void clear() throws Exception, UnsupportedOperationException {
1448             pool.clear();
1449         }
1450 
1451         public void close() {
1452             try {
1453                 pool.close();
1454             } catch (Exception e) {
1455                 // swallowed
1456             }
1457         }
1458 
1459         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1460             pool.setFactory(factory);
1461         }
1462 
1463         public String toString() {
1464             return "ErodingObjectPool{" +
1465                     "factor=" + factor +
1466                     ", pool=" + pool +
1467                     '}';
1468         }
1469     }
1470 
1471     private static class ErodingKeyedObjectPool implements KeyedObjectPool {
1472         private final KeyedObjectPool keyedPool;
1473         private final ErodingFactor erodingFactor;
1474 
1475         public ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) {
1476             this(keyedPool, new ErodingFactor(factor));
1477         }
1478 
1479         protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final ErodingFactor erodingFactor) {
1480             if (keyedPool == null) {
1481                 throw new IllegalArgumentException("keyedPool must not be null.");
1482             }
1483             this.keyedPool = keyedPool;
1484             this.erodingFactor = erodingFactor;
1485         }
1486 
1487         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
1488             return keyedPool.borrowObject(key);
1489         }
1490 
1491         public void returnObject(final Object key, final Object obj) throws Exception {
1492             boolean discard = false;
1493             final long now = System.currentTimeMillis();
1494             final ErodingFactor factor = getErodingFactor(key);
1495             synchronized (keyedPool) {
1496                 if (factor.getNextShrink() < now) {
1497                     final int numIdle = numIdle(key);
1498                     if (numIdle > 0) {
1499                         discard = true;
1500                     }
1501 
1502                     factor.update(now, numIdle);
1503                 }
1504             }
1505             try {
1506                 if (discard) {
1507                     keyedPool.invalidateObject(key, obj);
1508                 } else {
1509                     keyedPool.returnObject(key, obj);
1510                 }
1511             } catch (Exception e) {
1512                 // swallowed
1513             }
1514         }
1515 
1516         protected int numIdle(final Object key) {
1517             return getKeyedPool().getNumIdle();
1518         }
1519 
1520         protected ErodingFactor getErodingFactor(final Object key) {
1521             return erodingFactor;
1522         }
1523 
1524         public void invalidateObject(final Object key, final Object obj) {
1525             try {
1526                 keyedPool.invalidateObject(key, obj);
1527             } catch (Exception e) {
1528                 // swallowed
1529             }
1530         }
1531 
1532         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
1533             keyedPool.addObject(key);
1534         }
1535 
1536         public int getNumIdle() throws UnsupportedOperationException {
1537             return keyedPool.getNumIdle();
1538         }
1539 
1540         public int getNumIdle(final Object key) throws UnsupportedOperationException {
1541             return keyedPool.getNumIdle(key);
1542         }
1543 
1544         public int getNumActive() throws UnsupportedOperationException {
1545             return keyedPool.getNumActive();
1546         }
1547 
1548         public int getNumActive(final Object key) throws UnsupportedOperationException {
1549             return keyedPool.getNumActive(key);
1550         }
1551 
1552         public void clear() throws Exception, UnsupportedOperationException {
1553             keyedPool.clear();
1554         }
1555 
1556         public void clear(final Object key) throws Exception, UnsupportedOperationException {
1557             keyedPool.clear(key);
1558         }
1559 
1560         public void close() {
1561             try {
1562                 keyedPool.close();
1563             } catch (Exception e) {
1564                 // swallowed
1565             }
1566         }
1567 
1568         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1569             keyedPool.setFactory(factory);
1570         }
1571 
1572         protected KeyedObjectPool getKeyedPool() {
1573             return keyedPool;
1574         }
1575 
1576         public String toString() {
1577             return "ErodingKeyedObjectPool{" +
1578                     "erodingFactor=" + erodingFactor +
1579                     ", keyedPool=" + keyedPool +
1580                     '}';
1581         }
1582     }
1583 
1584     private static class ErodingPerKeyKeyedObjectPool extends ErodingKeyedObjectPool {
1585         private final float factor;
1586         private final Map factors = Collections.synchronizedMap(new HashMap());
1587 
1588         public ErodingPerKeyKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) {
1589             super(keyedPool, null);
1590             this.factor = factor;
1591         }
1592 
1593         protected int numIdle(final Object key) {
1594             return getKeyedPool().getNumIdle(key);
1595         }
1596 
1597         protected ErodingFactor getErodingFactor(final Object key) {
1598             ErodingFactor factor = (ErodingFactor)factors.get(key);
1599             // this may result in two ErodingFactors being created for a key
1600             // since they are small and cheap this is okay.
1601             if (factor == null) {
1602                 factor = new ErodingFactor(this.factor);
1603                 factors.put(key, factor);
1604             }
1605             return factor;
1606         }
1607 
1608         public String toString() {
1609             return "ErodingPerKeyKeyedObjectPool{" +
1610                     "factor=" + factor +
1611                     ", keyedPool=" + getKeyedPool() +
1612                     '}';
1613         }
1614     }
1615 }