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: 1206485 $ $Date: 2011-11-26 09:39:32 -0700 (Sat, 26 Nov 2011) $
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; //@GuardedBy("this")
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       * Should the supplied Throwable be re-thrown (eg if it is an instance of
55       * one of the Throwables that should never be swallowed). Used by the pool
56       * error handling for operations that throw exceptions that normally need to
57       * be ignored.
58       * @param t The Throwable to check
59       * @throws ThreadDeath if that is passed in
60       * @throws VirtualMachineError if that is passed in
61       * @since Pool 1.5.5
62       */
63      public static void checkRethrow(Throwable t) {
64          if (t instanceof ThreadDeath) {
65              throw (ThreadDeath) t;
66          }
67          if (t instanceof VirtualMachineError) {
68              throw (VirtualMachineError) t;
69          }
70          // All other instances of Throwable will be silently swallowed
71      }
72  
73      /**
74       * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
75       * needed. This method is the equivalent of calling
76       * {@link #adapt(KeyedPoolableObjectFactory, Object) PoolUtils.adapt(aKeyedPoolableObjectFactory, new Object())}.
77       *
78       * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
79       * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with an internal key.
80       * @throws IllegalArgumentException when <code>keyedFactory</code> is <code>null</code>.
81       * @see #adapt(KeyedPoolableObjectFactory, Object)
82       * @since Pool 1.3
83       */
84      public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
85          return adapt(keyedFactory, new Object());
86      }
87  
88      /**
89       * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
90       * needed using the specified <code>key</code> when delegating.
91       *
92       * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
93       * @param key the key to use when delegating.
94       * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with the specified key.
95       * @throws IllegalArgumentException when <code>keyedFactory</code> or <code>key</code> is <code>null</code>.
96       * @see #adapt(KeyedPoolableObjectFactory)
97       * @since Pool 1.3
98       */
99      public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory, final Object key) throws IllegalArgumentException {
100         return new PoolableObjectFactoryAdaptor(keyedFactory, key);
101     }
102 
103     /**
104      * Adapt a <code>PoolableObjectFactory</code> instance to work where a <code>KeyedPoolableObjectFactory</code> is
105      * needed. The key is ignored.
106      *
107      * @param factory the {@link PoolableObjectFactory} to delegate to.
108      * @return a {@link KeyedPoolableObjectFactory} that delegates to <code>factory</code> ignoring the key.
109      * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
110      * @since Pool 1.3
111      */
112     public static KeyedPoolableObjectFactory adapt(final PoolableObjectFactory factory) throws IllegalArgumentException {
113         return new KeyedPoolableObjectFactoryAdaptor(factory);
114     }
115 
116     /**
117      * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed. This is the
118      * equivalent of calling {@link #adapt(KeyedObjectPool, Object) PoolUtils.adapt(aKeyedObjectPool, new Object())}.
119      *
120      * @param keyedPool the {@link KeyedObjectPool} to delegate to.
121      * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with an internal key.
122      * @throws IllegalArgumentException when <code>keyedPool</code> is <code>null</code>.
123      * @see #adapt(KeyedObjectPool, Object)
124      * @since Pool 1.3
125      */
126     public static ObjectPool adapt(final KeyedObjectPool keyedPool) throws IllegalArgumentException {
127         return adapt(keyedPool, new Object());
128     }
129 
130     /**
131      * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed using the
132      * specified <code>key</code> when delegating.
133      *
134      * @param keyedPool the {@link KeyedObjectPool} to delegate to.
135      * @param key the key to use when delegating.
136      * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with the specified key.
137      * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
138      * @see #adapt(KeyedObjectPool)
139      * @since Pool 1.3
140      */
141     public static ObjectPool adapt(final KeyedObjectPool keyedPool, final Object key) throws IllegalArgumentException {
142         return new ObjectPoolAdaptor(keyedPool, key);
143     }
144 
145     /**
146      * Adapt an <code>ObjectPool</code> to work where an <code>KeyedObjectPool</code> is needed.
147      * The key is ignored.
148      *
149      * @param pool the {@link ObjectPool} to delegate to.
150      * @return a {@link KeyedObjectPool} that delegates to <code>pool</code> ignoring the key.
151      * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
152      * @since Pool 1.3
153      */
154     public static KeyedObjectPool adapt(final ObjectPool pool) throws IllegalArgumentException {
155         return new KeyedObjectPoolAdaptor(pool);
156     }
157 
158     /**
159      * Wraps an <code>ObjectPool</code> and dynamically checks the type of objects borrowed and returned to the pool.
160      * If an object is passed to the pool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
161      *
162      * @param pool the pool to enforce type safety on
163      * @param type the class type to enforce.
164      * @return an <code>ObjectPool</code> that will only allow objects of <code>type</code>
165      * @since Pool 1.3
166      */
167     public static ObjectPool checkedPool(final ObjectPool pool, final Class type) {
168         if (pool == null) {
169             throw new IllegalArgumentException("pool must not be null.");
170         }
171         if (type == null) {
172             throw new IllegalArgumentException("type must not be null.");
173         }
174         return new CheckedObjectPool(pool, type);
175     }
176 
177     /**
178      * Wraps a <code>KeyedObjectPool</code> and dynamically checks the type of objects borrowed and returned to the keyedPool.
179      * If an object is passed to the keyedPool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
180      *
181      * @param keyedPool the keyedPool to enforce type safety on
182      * @param type the class type to enforce.
183      * @return a <code>KeyedObjectPool</code> that will only allow objects of <code>type</code>
184      * @since Pool 1.3
185      */
186     public static KeyedObjectPool checkedPool(final KeyedObjectPool keyedPool, final Class type) {
187         if (keyedPool == null) {
188             throw new IllegalArgumentException("keyedPool must not be null.");
189         }
190         if (type == null) {
191             throw new IllegalArgumentException("type must not be null.");
192         }
193         return new CheckedKeyedObjectPool(keyedPool, type);
194     }
195 
196     /**
197      * Periodically check the idle object count for the pool. At most one idle object will be added per period.
198      * If there is an exception when calling {@link ObjectPool#addObject()} then no more checks will be performed.
199      *
200      * @param pool the pool to check periodically.
201      * @param minIdle if the {@link ObjectPool#getNumIdle()} is less than this then add an idle object.
202      * @param period the frequency to check the number of idle objects in a pool, see
203      *      {@link Timer#schedule(TimerTask, long, long)}.
204      * @return the {@link TimerTask} that will periodically check the pools idle object count.
205      * @throws IllegalArgumentException when <code>pool</code> is <code>null</code> or
206      *      when <code>minIdle</code> is negative or when <code>period</code> isn't
207      *      valid for {@link Timer#schedule(TimerTask, long, long)}.
208      * @since Pool 1.3
209      */
210     public static TimerTask checkMinIdle(final ObjectPool pool, final int minIdle, final long period) throws IllegalArgumentException {
211         if (pool == null) {
212             throw new IllegalArgumentException("keyedPool must not be null.");
213         }
214         if (minIdle < 0) {
215             throw new IllegalArgumentException("minIdle must be non-negative.");
216         }
217         final TimerTask task = new ObjectPoolMinIdleTimerTask(pool, minIdle);
218         getMinIdleTimer().schedule(task, 0L, period);
219         return task;
220     }
221 
222     /**
223      * Periodically check the idle object count for the key in the keyedPool. At most one idle object will be added per period.
224      * If there is an exception when calling {@link KeyedObjectPool#addObject(Object)} then no more checks for that key
225      * will be performed.
226      *
227      * @param keyedPool the keyedPool to check periodically.
228      * @param key the key to check the idle count of.
229      * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
230      * @param period the frequency to check the number of idle objects in a keyedPool, see
231      *      {@link Timer#schedule(TimerTask, long, long)}.
232      * @return the {@link TimerTask} that will periodically check the pools idle object count.
233      * @throws IllegalArgumentException when <code>keyedPool</code>, <code>key</code> is <code>null</code> or
234      *      when <code>minIdle</code> is negative or when <code>period</code> isn't
235      *      valid for {@link Timer#schedule(TimerTask, long, long)}.
236      * @since Pool 1.3
237      */
238     public static TimerTask checkMinIdle(final KeyedObjectPool keyedPool, final Object key, final int minIdle, final long period) throws IllegalArgumentException {
239         if (keyedPool == null) {
240             throw new IllegalArgumentException("keyedPool must not be null.");
241         }
242         if (key == null) {
243             throw new IllegalArgumentException("key must not be null.");
244         }
245         if (minIdle < 0) {
246             throw new IllegalArgumentException("minIdle must be non-negative.");
247         }
248         final TimerTask task = new KeyedObjectPoolMinIdleTimerTask(keyedPool, key, minIdle);
249         getMinIdleTimer().schedule(task, 0L, period);
250         return task;
251     }
252 
253     /**
254      * Periodically check the idle object count for each key in the <code>Collection</code> <code>keys</code> in the keyedPool.
255      * At most one idle object will be added per period.
256      *
257      * @param keyedPool the keyedPool to check periodically.
258      * @param keys a collection of keys to check the idle object count.
259      * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
260      * @param period the frequency to check the number of idle objects in a keyedPool, see
261      *      {@link Timer#schedule(TimerTask, long, long)}.
262      * @return a {@link Map} of key and {@link TimerTask} pairs that will periodically check the pools idle object count.
263      * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or any of the values in the
264      *      collection is <code>null</code> or when <code>minIdle</code> is negative or when <code>period</code> isn't
265      *      valid for {@link Timer#schedule(TimerTask, long, long)}.
266      * @see #checkMinIdle(KeyedObjectPool, Object, int, long)
267      * @since Pool 1.3
268      */
269     public static Map checkMinIdle(final KeyedObjectPool keyedPool, final Collection keys, final int minIdle, final long period) throws IllegalArgumentException {
270         if (keys == null) {
271             throw new IllegalArgumentException("keys must not be null.");
272         }
273         final Map tasks = new HashMap(keys.size());
274         final Iterator iter = keys.iterator();
275         while (iter.hasNext()) {
276             final Object key = iter.next();
277             final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
278             tasks.put(key, task);
279         }
280         return tasks;
281     }
282 
283     /**
284      * Call <code>addObject()</code> on <code>pool</code> <code>count</code> number of times.
285      *
286      * @param pool the pool to prefill.
287      * @param count the number of idle objects to add.
288      * @throws Exception when {@link ObjectPool#addObject()} fails.
289      * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
290      * @since Pool 1.3
291      */
292     public static void prefill(final ObjectPool pool, final int count) throws Exception, IllegalArgumentException {
293         if (pool == null) {
294             throw new IllegalArgumentException("pool must not be null.");
295         }
296         for (int i = 0; i < count; i++) {
297             pool.addObject();
298         }
299     }
300 
301     /**
302      * Call <code>addObject(Object)</code> on <code>keyedPool</code> with <code>key</code> <code>count</code>
303      * number of times.
304      *
305      * @param keyedPool the keyedPool to prefill.
306      * @param key the key to add objects for.
307      * @param count the number of idle objects to add for <code>key</code>.
308      * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
309      * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
310      * @since Pool 1.3
311      */
312     public static void prefill(final KeyedObjectPool keyedPool, final Object key, final int count) throws Exception, IllegalArgumentException {
313         if (keyedPool == null) {
314             throw new IllegalArgumentException("keyedPool must not be null.");
315         }
316         if (key == null) {
317             throw new IllegalArgumentException("key must not be null.");
318         }
319         for (int i = 0; i < count; i++) {
320             keyedPool.addObject(key);
321         }
322     }
323 
324     /**
325      * Call <code>addObject(Object)</code> on <code>keyedPool</code> with each key in <code>keys</code> for
326      * <code>count</code> number of times. This has the same effect as calling
327      * {@link #prefill(KeyedObjectPool, Object, int)} for each key in the <code>keys</code> collection.
328      *
329      * @param keyedPool the keyedPool to prefill.
330      * @param keys {@link Collection} of keys to add objects for.
331      * @param count the number of idle objects to add for each <code>key</code>.
332      * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
333      * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or
334      *      any value in <code>keys</code> is <code>null</code>.
335      * @see #prefill(KeyedObjectPool, Object, int)
336      * @since Pool 1.3
337      */
338     public static void prefill(final KeyedObjectPool keyedPool, final Collection keys, final int count) throws Exception, IllegalArgumentException {
339         if (keys == null) {
340             throw new IllegalArgumentException("keys must not be null.");
341         }
342         final Iterator iter = keys.iterator();
343         while (iter.hasNext()) {
344             prefill(keyedPool, iter.next(), count);
345         }
346     }
347 
348     /**
349      * Returns a synchronized (thread-safe) ObjectPool backed by the specified ObjectPool.
350      *
351      * <p><b>Note:</b>
352      * This should not be used on pool implementations that already provide proper synchronization
353      * such as the pools provided in the Commons Pool library. Wrapping a pool that
354      * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
355      * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
356      * </p>
357      *
358      * @param pool the ObjectPool to be "wrapped" in a synchronized ObjectPool.
359      * @return a synchronized view of the specified ObjectPool.
360      * @since Pool 1.3
361      */
362     public static ObjectPool synchronizedPool(final ObjectPool pool) {
363         if (pool == null) {
364             throw new IllegalArgumentException("pool must not be null.");
365         }
366         /*
367         assert !(pool instanceof GenericObjectPool)
368                 : "GenericObjectPool is already thread-safe";
369         assert !(pool instanceof SoftReferenceObjectPool)
370                 : "SoftReferenceObjectPool is already thread-safe";
371         assert !(pool instanceof StackObjectPool)
372                 : "StackObjectPool is already thread-safe";
373         assert !"org.apache.commons.pool.composite.CompositeObjectPool".equals(pool.getClass().getName())
374                 : "CompositeObjectPools are already thread-safe";
375         */
376         return new SynchronizedObjectPool(pool);
377     }
378 
379     /**
380      * Returns a synchronized (thread-safe) KeyedObjectPool backed by the specified KeyedObjectPool.
381      *
382      * <p><b>Note:</b>
383      * This should not be used on pool implementations that already provide proper synchronization
384      * such as the pools provided in the Commons Pool library. Wrapping a pool that
385      * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
386      * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
387      * </p>
388      *
389      * @param keyedPool the KeyedObjectPool to be "wrapped" in a synchronized KeyedObjectPool.
390      * @return a synchronized view of the specified KeyedObjectPool.
391      * @since Pool 1.3
392      */
393     public static KeyedObjectPool synchronizedPool(final KeyedObjectPool keyedPool) {
394         if (keyedPool == null) {
395             throw new IllegalArgumentException("keyedPool must not be null.");
396         }
397         /*
398         assert !(keyedPool instanceof GenericKeyedObjectPool)
399                 : "GenericKeyedObjectPool is already thread-safe";
400         assert !(keyedPool instanceof StackKeyedObjectPool)
401                 : "StackKeyedObjectPool is already thread-safe";
402         assert !"org.apache.commons.pool.composite.CompositeKeyedObjectPool".equals(keyedPool.getClass().getName())
403                 : "CompositeKeyedObjectPools are already thread-safe";
404         */
405         return new SynchronizedKeyedObjectPool(keyedPool);
406     }
407 
408     /**
409      * Returns a synchronized (thread-safe) PoolableObjectFactory backed by the specified PoolableObjectFactory.
410      *
411      * @param factory the PoolableObjectFactory to be "wrapped" in a synchronized PoolableObjectFactory.
412      * @return a synchronized view of the specified PoolableObjectFactory.
413      * @since Pool 1.3
414      */
415     public static PoolableObjectFactory synchronizedPoolableFactory(final PoolableObjectFactory factory) {
416         return new SynchronizedPoolableObjectFactory(factory);
417     }
418 
419     /**
420      * Returns a synchronized (thread-safe) KeyedPoolableObjectFactory backed by the specified KeyedPoolableObjectFactory.
421      *
422      * @param keyedFactory the KeyedPoolableObjectFactory to be "wrapped" in a synchronized KeyedPoolableObjectFactory.
423      * @return a synchronized view of the specified KeyedPoolableObjectFactory.
424      * @since Pool 1.3
425      */
426     public static KeyedPoolableObjectFactory synchronizedPoolableFactory(final KeyedPoolableObjectFactory keyedFactory) {
427         return new SynchronizedKeyedPoolableObjectFactory(keyedFactory);
428     }
429 
430     /**
431      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
432      * This is intended as an always thread-safe alternative to using an idle object evictor
433      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
434      * pools that experience load spikes.
435      *
436      * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
437      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
438      * @see #erodingPool(ObjectPool, float)
439      * @since Pool 1.4
440      */
441     public static ObjectPool erodingPool(final ObjectPool pool) {
442         return erodingPool(pool, 1f);
443     }
444 
445     /**
446      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
447      * This is intended as an always thread-safe alternative to using an idle object evictor
448      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
449      * pools that experience load spikes.
450      *
451      * <p>
452      * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
453      * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
454      * Values greater than 1 cause the pool to less frequently try to shrink it's size.
455      * </p>
456      *
457      * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
458      * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
459      * If 0 &lt; factor &lt; 1 then the pool shrinks more aggressively.
460      * If 1 &lt; factor then the pool shrinks less aggressively.
461      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
462      * @see #erodingPool(ObjectPool)
463      * @since Pool 1.4
464      */
465     public static ObjectPool erodingPool(final ObjectPool pool, final float factor) {
466         if (pool == null) {
467             throw new IllegalArgumentException("pool must not be null.");
468         }
469         if (factor <= 0f) {
470             throw new IllegalArgumentException("factor must be positive.");
471         }
472         return new ErodingObjectPool(pool, factor);
473     }
474 
475     /**
476      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
477      * This is intended as an always thread-safe alternative to using an idle object evictor
478      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
479      * pools that experience load spikes.
480      *
481      * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
482      * possible.
483      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
484      * @see #erodingPool(KeyedObjectPool, float)
485      * @see #erodingPool(KeyedObjectPool, float, boolean)
486      * @since Pool 1.4
487      */
488     public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool) {
489         return erodingPool(keyedPool, 1f);
490     }
491 
492     /**
493      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
494      * This is intended as an always thread-safe alternative to using an idle object evictor
495      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
496      * pools that experience load spikes.
497      *
498      * <p>
499      * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
500      * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
501      * Values greater than 1 cause the pool to less frequently try to shrink it's size.
502      * </p>
503      *
504      * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
505      * possible.
506      * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
507      * If 0 &lt; factor &lt; 1 then the pool shrinks more aggressively.
508      * If 1 &lt; factor then the pool shrinks less aggressively.
509      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
510      * @see #erodingPool(KeyedObjectPool, float, boolean)
511      * @since Pool 1.4
512      */
513     public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool, final float factor) {
514         return erodingPool(keyedPool, factor, false);
515     }
516 
517     /**
518      * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
519      * This is intended as an always thread-safe alternative to using an idle object evictor
520      * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
521      * pools that experience load spikes.
522      *
523      * <p>
524      * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
525      * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
526      * Values greater than 1 cause the pool to less frequently try to shrink it's size.
527      * </p>
528      *
529      * <p>
530      * The perKey parameter determines if the pool shrinks on a whole pool basis or a per key basis.
531      * When perKey is false, the keys do not have an effect on the rate at which the pool tries to
532      * shrink it's size. When perKey is true, each key is shrunk independently.
533      * </p>
534      *
535      * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
536      * possible.
537      * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
538      * If 0 &lt; factor &lt; 1 then the pool shrinks more aggressively.
539      * If 1 &lt; factor then the pool shrinks less aggressively.
540      * @param perKey when true, each key is treated independently.
541      * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
542      * @see #erodingPool(KeyedObjectPool)
543      * @see #erodingPool(KeyedObjectPool, float)
544      * @since Pool 1.4
545      */
546     public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool, final float factor, final boolean perKey) {
547         if (keyedPool == null) {
548             throw new IllegalArgumentException("keyedPool must not be null.");
549         }
550         if (factor <= 0f) {
551             throw new IllegalArgumentException("factor must be positive.");
552         }
553         if (perKey) {
554             return new ErodingPerKeyKeyedObjectPool(keyedPool, factor);
555         } else {
556             return new ErodingKeyedObjectPool(keyedPool, factor);
557         }
558     }
559 
560     /**
561      * Get the <code>Timer</code> for checking keyedPool's idle count. Lazily create the {@link Timer} as needed.
562      *
563      * @return the {@link Timer} for checking keyedPool's idle count.
564      * @since Pool 1.3
565      */
566     private static synchronized Timer getMinIdleTimer() {
567         if (MIN_IDLE_TIMER == null) {
568             MIN_IDLE_TIMER = new Timer(true);
569         }
570         return MIN_IDLE_TIMER;
571     }
572 
573     /**
574      * Adaptor class that wraps and converts a KeyedPoolableObjectFactory with a fixed
575      * key to a PoolableObjectFactory.
576      */
577     private static class PoolableObjectFactoryAdaptor implements PoolableObjectFactory {
578         /** Fixed key */
579         private final Object key;
580         
581         /** Wrapped factory */
582         private final KeyedPoolableObjectFactory keyedFactory;
583 
584         /**
585          * Create a PoolableObjectFactoryAdaptor wrapping the provided KeyedPoolableObjectFactory with the 
586          * given fixed key.
587          * 
588          * @param keyedFactory KeyedPoolableObjectFactory that will manage objects
589          * @param key fixed key
590          * @throws IllegalArgumentException if either of the parameters is null
591          */
592         PoolableObjectFactoryAdaptor(final KeyedPoolableObjectFactory keyedFactory, final Object key)
593         throws IllegalArgumentException {
594             if (keyedFactory == null) {
595                 throw new IllegalArgumentException("keyedFactory must not be null.");
596             }
597             if (key == null) {
598                 throw new IllegalArgumentException("key must not be null.");
599             }
600             this.keyedFactory = keyedFactory;
601             this.key = key;
602         }
603 
604         /**
605          * Create an object instance using the configured factory and key.
606          * 
607          * @return new object instance
608          */
609         public Object makeObject() throws Exception {
610             return keyedFactory.makeObject(key);
611         }
612 
613         /**
614          * Destroy the object, passing the fixed key to the factory.
615          * 
616          * @param obj object to destroy
617          */
618         public void destroyObject(final Object obj) throws Exception {
619             keyedFactory.destroyObject(key, obj);
620         }
621 
622         /**
623          * Validate the object, passing the fixed key to the factory.
624          * 
625          * @param obj object to validate
626          * @return true if validation is successful
627          */
628         public boolean validateObject(final Object obj) {
629             return keyedFactory.validateObject(key, obj);
630         }
631 
632         /**
633          * Activate the object, passing the fixed key to the factory.
634          * 
635          * @param obj object to activate
636          */
637         public void activateObject(final Object obj) throws Exception {
638             keyedFactory.activateObject(key, obj);
639         }
640 
641         /**
642          * Passivate the object, passing the fixed key to the factory.
643          * 
644          * @param obj object to passivate
645          */
646         public void passivateObject(final Object obj) throws Exception {
647             keyedFactory.passivateObject(key, obj);
648         }
649 
650         /**
651          * {@inheritDoc}
652          */
653         public String toString() {
654             final StringBuffer sb = new StringBuffer();
655             sb.append("PoolableObjectFactoryAdaptor");
656             sb.append("{key=").append(key);
657             sb.append(", keyedFactory=").append(keyedFactory);
658             sb.append('}');
659             return sb.toString();
660         }
661     }
662 
663     /**
664      * Adaptor class that turns a PoolableObjectFactory into a KeyedPoolableObjectFactory by
665      * ignoring keys.
666      */
667     private static class KeyedPoolableObjectFactoryAdaptor implements KeyedPoolableObjectFactory {
668         
669         /** Underlying PoolableObjectFactory */
670         private final PoolableObjectFactory factory;
671 
672         /**
673          * Create a new KeyedPoolableObjectFactoryAdaptor using the given PoolableObjectFactory to
674          * manage objects.
675          * 
676          * @param factory wrapped PoolableObjectFactory 
677          * @throws IllegalArgumentException if the factory is null
678          */
679         KeyedPoolableObjectFactoryAdaptor(final PoolableObjectFactory factory) throws IllegalArgumentException {
680             if (factory == null) {
681                 throw new IllegalArgumentException("factory must not be null.");
682             }
683             this.factory = factory;
684         }
685 
686         /**
687          * Create a new object instance, ignoring the key
688          * 
689          * @param key ignored
690          * @return newly created object instance
691          */
692         public Object makeObject(final Object key) throws Exception {
693             return factory.makeObject();
694         }
695 
696         /**
697          * Destroy the object, ignoring the key.
698          * 
699          * @param key ignored
700          * @param obj instance to destroy
701          */
702         public void destroyObject(final Object key, final Object obj) throws Exception {
703             factory.destroyObject(obj);
704         }
705 
706         /**
707          * Validate the object, ignoring the key
708          * 
709          * @param key ignored
710          * @param obj object to validate
711          * @return true if validation is successful
712          */
713         public boolean validateObject(final Object key, final Object obj) {
714             return factory.validateObject(obj);
715         }
716 
717         /**
718          * Activate the object, ignoring the key.
719          * 
720          * @param key ignored
721          * @param obj object to be activated
722          */
723         public void activateObject(final Object key, final Object obj) throws Exception {
724             factory.activateObject(obj);
725         }
726 
727         /**
728          * Passivate the object, ignoring the key.
729          * 
730          * @param key ignored
731          * @param obj object to passivate
732          */
733         public void passivateObject(final Object key, final Object obj) throws Exception {
734             factory.passivateObject(obj);
735         }
736 
737         /**
738          * {@inheritDoc}
739          */
740         public String toString() {
741             final StringBuffer sb = new StringBuffer();
742             sb.append("KeyedPoolableObjectFactoryAdaptor");
743             sb.append("{factory=").append(factory);
744             sb.append('}');
745             return sb.toString();
746         }
747     }
748 
749     /**
750      * Adapts a KeyedObjectPool to make it an ObjectPool by fixing restricting to
751      * a fixed key.
752      */
753     private static class ObjectPoolAdaptor implements ObjectPool {
754         
755         /** Fixed key */
756         private final Object key;
757         
758         /** Underlying KeyedObjectPool */
759         private final KeyedObjectPool keyedPool;
760 
761         /**
762          * Create a new ObjectPoolAdaptor using the provided KeyedObjectPool and fixed key.
763          * 
764          * @param keyedPool underlying KeyedObjectPool
765          * @param key fixed key
766          * @throws IllegalArgumentException if either of the parameters is null
767          */
768         ObjectPoolAdaptor(final KeyedObjectPool keyedPool, final Object key) throws IllegalArgumentException {
769             if (keyedPool == null) {
770                 throw new IllegalArgumentException("keyedPool must not be null.");
771             }
772             if (key == null) {
773                 throw new IllegalArgumentException("key must not be null.");
774             }
775             this.keyedPool = keyedPool;
776             this.key = key;
777         }
778 
779         /**
780          * {@inheritDoc}
781          */
782         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
783             return keyedPool.borrowObject(key);
784         }
785 
786         /**
787          * {@inheritDoc}
788          */
789         public void returnObject(final Object obj) {
790             try {
791                 keyedPool.returnObject(key, obj);
792             } catch (Exception e) {
793                 // swallowed as of Pool 2
794             }
795         }
796 
797         /**
798          * {@inheritDoc}
799          */
800         public void invalidateObject(final Object obj) {
801             try {
802                 keyedPool.invalidateObject(key, obj);
803             } catch (Exception e) {
804                 // swallowed as of Pool 2
805             }
806         }
807 
808         /**
809          * {@inheritDoc}
810          */
811         public void addObject() throws Exception, IllegalStateException {
812             keyedPool.addObject(key);
813         }
814 
815         /**
816          * {@inheritDoc}
817          */
818         public int getNumIdle() throws UnsupportedOperationException {
819             return keyedPool.getNumIdle(key);
820         }
821 
822         /**
823          * {@inheritDoc}
824          */
825         public int getNumActive() throws UnsupportedOperationException {
826             return keyedPool.getNumActive(key);
827         }
828 
829         /**
830          * {@inheritDoc}
831          */
832         public void clear() throws Exception, UnsupportedOperationException {
833             keyedPool.clear();
834         }
835 
836         /**
837          * {@inheritDoc}
838          */
839         public void close() {
840             try {
841                 keyedPool.close();
842             } catch (Exception e) {
843                 // swallowed as of Pool 2
844             }
845         }
846 
847         /**
848          * Sets the PoolableObjectFactory for the pool.
849          * 
850          * @param factory new PoolableObjectFactory 
851          * @deprecated to be removed in version 2.0
852          */
853         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
854             keyedPool.setFactory(adapt(factory));
855         }
856 
857         /**
858          * {@inheritDoc}
859          */
860         public String toString() {
861             final StringBuffer sb = new StringBuffer();
862             sb.append("ObjectPoolAdaptor");
863             sb.append("{key=").append(key);
864             sb.append(", keyedPool=").append(keyedPool);
865             sb.append('}');
866             return sb.toString();
867         }
868     }
869 
870     /**
871      * Adapts an ObjectPool to implement KeyedObjectPool by ignoring key arguments.
872      */
873     private static class KeyedObjectPoolAdaptor implements KeyedObjectPool {
874        
875         /** Underlying pool */
876         private final ObjectPool pool;
877 
878         /**
879          * Create a new KeyedObjectPoolAdaptor wrapping the given ObjectPool
880          * 
881          * @param pool underlying object pool
882          * @throws IllegalArgumentException if pool is null
883          */
884         KeyedObjectPoolAdaptor(final ObjectPool pool) throws IllegalArgumentException {
885             if (pool == null) {
886                 throw new IllegalArgumentException("pool must not be null.");
887             }
888             this.pool = pool;
889         }
890 
891         /**
892          * Borrow and object from the pool, ignoring the key
893          * 
894          * @param key ignored
895          * @return newly created object instance
896          */
897         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
898             return pool.borrowObject();
899         }
900 
901         /**
902          * Return and object to the pool, ignoring the key
903          * 
904          * @param key ignored
905          * @param obj object to return
906          */
907         public void returnObject(final Object key, final Object obj) {
908             try {
909                 pool.returnObject(obj);
910             } catch (Exception e) {
911                 // swallowed as of Pool 2
912             }
913         }
914 
915         /**
916          * Invalidate and object, ignoring the key
917          * 
918          * @param obj object to invalidate
919          * @param key ignored
920          */
921         public void invalidateObject(final Object key, final Object obj) {
922             try {
923                 pool.invalidateObject(obj);
924             } catch (Exception e) {
925                 // swallowed as of Pool 2
926             }
927         }
928 
929         /**
930          * Add an object to the pool, ignoring the key
931          * 
932          * @param key ignored
933          */
934         public void addObject(final Object key) throws Exception, IllegalStateException {
935             pool.addObject();
936         }
937 
938         /**
939          * Return the number of objects idle in the pool, ignoring the key.
940          * 
941          * @param key ignored
942          * @return idle instance count
943          */
944         public int getNumIdle(final Object key) throws UnsupportedOperationException {
945             return pool.getNumIdle();
946         }
947 
948         /**
949          * Return the number of objects checked out from the pool, ignoring the key.
950          * 
951          * @param key ignored
952          * @return active instance count
953          */
954         public int getNumActive(final Object key) throws UnsupportedOperationException {
955             return pool.getNumActive();
956         }
957 
958         /**
959          * {@inheritDoc}
960          */
961         public int getNumIdle() throws UnsupportedOperationException {
962             return pool.getNumIdle();
963         }
964 
965         /**
966          * {@inheritDoc}
967          */
968         public int getNumActive() throws UnsupportedOperationException {
969             return pool.getNumActive();
970         }
971 
972         /**
973          * {@inheritDoc}
974          */
975         public void clear() throws Exception, UnsupportedOperationException {
976             pool.clear();
977         }
978 
979         /**
980          * Clear the pool, ignoring the key (has same effect as {@link #clear()}.
981          * 
982          * @param key ignored.
983          */
984         public void clear(final Object key) throws Exception, UnsupportedOperationException {
985             pool.clear();
986         }
987 
988         /**
989          * {@inheritDoc}
990          */
991         public void close() {
992             try {
993                 pool.close();
994             } catch (Exception e) {
995                 // swallowed as of Pool 2
996             }
997         }
998 
999         /**
1000          * Sets the factory used to manage objects.
1001          * 
1002          * @param factory new factory to use managing object instances
1003          * @deprecated to be removed in version 2.0
1004          */
1005         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1006             pool.setFactory(adapt(factory));
1007         }
1008 
1009         /**
1010          * {@inheritDoc}
1011          */
1012         public String toString() {
1013             final StringBuffer sb = new StringBuffer();
1014             sb.append("KeyedObjectPoolAdaptor");
1015             sb.append("{pool=").append(pool);
1016             sb.append('}');
1017             return sb.toString();
1018         }
1019     }
1020 
1021     /**
1022      * An object pool that performs type checking on objects passed
1023      * to pool methods.
1024      *
1025      */
1026     private static class CheckedObjectPool implements ObjectPool {
1027         /** 
1028          * Type of objects allowed in the pool. This should be a subtype of the return type of
1029          * the underlying pool's associated object factory.
1030          */
1031         private final Class type;
1032        
1033         /** Underlying object pool */
1034         private final ObjectPool pool;
1035 
1036         /**
1037          * Create a CheckedObjectPool accepting objects of the given type using
1038          * the given pool.
1039          * 
1040          * @param pool underlying object pool
1041          * @param type expected pooled object type
1042          * @throws IllegalArgumentException if either parameter is null
1043          */
1044         CheckedObjectPool(final ObjectPool pool, final Class type) {
1045             if (pool == null) {
1046                 throw new IllegalArgumentException("pool must not be null.");
1047             }
1048             if (type == null) {
1049                 throw new IllegalArgumentException("type must not be null.");
1050             }
1051             this.pool = pool;
1052             this.type = type;
1053         }
1054 
1055         /**
1056          * Borrow an object from the pool, checking its type.
1057          * 
1058          * @return a type-checked object from the pool
1059          * @throws ClassCastException if the object returned by the pool is not of the expected type
1060          */
1061         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
1062             final Object obj = pool.borrowObject();
1063             if (type.isInstance(obj)) {
1064                 return obj;
1065             } else {
1066                 throw new ClassCastException("Borrowed object is not of type: " + type.getName() + " was: " + obj);
1067             }
1068         }
1069 
1070         /**
1071          * Return an object to the pool, verifying that it is of the correct type.
1072          * 
1073          * @param obj object to return
1074          * @throws ClassCastException if obj is not of the expected type
1075          */
1076         public void returnObject(final Object obj) {
1077             if (type.isInstance(obj)) {
1078                 try {
1079                     pool.returnObject(obj);
1080                 } catch (Exception e) {
1081                     // swallowed as of Pool 2
1082                 }
1083             } else {
1084                 throw new ClassCastException("Returned object is not of type: " + type.getName() + " was: " + obj);
1085             }
1086         }
1087 
1088         /**
1089          * Invalidates an object from the pool, verifying that it is of the expected type.
1090          * 
1091          * @param obj object to invalidate
1092          * @throws ClassCastException if obj is not of the expected type
1093          */
1094         public void invalidateObject(final Object obj) {
1095             if (type.isInstance(obj)) {
1096                 try {
1097                     pool.invalidateObject(obj);
1098                 } catch (Exception e) {
1099                     // swallowed as of Pool 2
1100                 }
1101             } else {
1102                 throw new ClassCastException("Invalidated object is not of type: " + type.getName() + " was: " + obj);
1103             }
1104         }
1105 
1106         /**
1107          * {@inheritDoc}
1108          */
1109         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
1110             pool.addObject();
1111         }
1112 
1113         /**
1114          * {@inheritDoc}
1115          */
1116         public int getNumIdle() throws UnsupportedOperationException {
1117             return pool.getNumIdle();
1118         }
1119 
1120         /**
1121          * {@inheritDoc}
1122          */
1123         public int getNumActive() throws UnsupportedOperationException {
1124             return pool.getNumActive();
1125         }
1126 
1127         /**
1128          * {@inheritDoc}
1129          */
1130         public void clear() throws Exception, UnsupportedOperationException {
1131             pool.clear();
1132         }
1133 
1134         /**
1135          * {@inheritDoc}
1136          */
1137         public void close() {
1138             try {
1139                 pool.close();
1140             } catch (Exception e) {
1141                 // swallowed as of Pool 2
1142             }
1143         }
1144 
1145         /**
1146          * Sets the object factory associated with the pool
1147          * 
1148          * @param factory object factory
1149          * @deprecated to be removed in version 2.0
1150          */
1151         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1152             pool.setFactory(factory);
1153         }
1154 
1155         /**
1156          * {@inheritDoc}
1157          */
1158         public String toString() {
1159             final StringBuffer sb = new StringBuffer();
1160             sb.append("CheckedObjectPool");
1161             sb.append("{type=").append(type);
1162             sb.append(", pool=").append(pool);
1163             sb.append('}');
1164             return sb.toString();
1165         }
1166     }
1167 
1168     /**
1169      * A keyed object pool that performs type checking on objects passed
1170      * to pool methods.
1171      *
1172      */
1173     private static class CheckedKeyedObjectPool implements KeyedObjectPool {
1174         /** 
1175          * Expected type of objects managed by the pool.  This should be
1176          * a subtype of the return type of the object factory used by the pool.
1177          */
1178         private final Class type;
1179         
1180         /** Underlying pool */
1181         private final KeyedObjectPool keyedPool;
1182 
1183         /**
1184          * Create a new CheckedKeyedObjectPool from the given pool with given expected object type.
1185          * 
1186          * @param keyedPool underlying pool
1187          * @param type expected object type
1188          * @throws IllegalArgumentException if either parameter is null
1189          */
1190         CheckedKeyedObjectPool(final KeyedObjectPool keyedPool, final Class type) {
1191             if (keyedPool == null) {
1192                 throw new IllegalArgumentException("keyedPool must not be null.");
1193             }
1194             if (type == null) {
1195                 throw new IllegalArgumentException("type must not be null.");
1196             }
1197             this.keyedPool = keyedPool;
1198             this.type = type;
1199         }
1200 
1201         /**
1202          * Borrow an object from the pool, verifying correct return type.
1203          * 
1204          * @param key pool key
1205          * @return type-checked object from the pool under the given key
1206          * @throws ClassCastException if the object returned by the pool is not of the expected type
1207          */
1208         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
1209             Object obj = keyedPool.borrowObject(key);
1210             if (type.isInstance(obj)) {
1211                 return obj;
1212             } else {
1213                 throw new ClassCastException("Borrowed object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
1214             }
1215         }
1216 
1217         /**
1218          * Return an object to the pool, checking its type.
1219          * 
1220          * @param key the associated key (not type-checked)
1221          * @param obj the object to return (type-checked)
1222          * @throws ClassCastException if obj is not of the expected type
1223          */
1224         public void returnObject(final Object key, final Object obj) {
1225             if (type.isInstance(obj)) {
1226                 try {
1227                     keyedPool.returnObject(key, obj);
1228                 } catch (Exception e) {
1229                     // swallowed as of Pool 2
1230                 }
1231             } else {
1232                 throw new ClassCastException("Returned object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
1233             }
1234         }
1235 
1236         /**
1237          * Invalidate an object to the pool, checking its type.
1238          * 
1239          * @param key the associated key (not type-checked)
1240          * @param obj the object to return (type-checked)
1241          * @throws ClassCastException if obj is not of the expected type
1242          */
1243         public void invalidateObject(final Object key, final Object obj) {
1244             if (type.isInstance(obj)) {
1245                 try {
1246                     keyedPool.invalidateObject(key, obj);
1247                 } catch (Exception e) {
1248                     // swallowed as of Pool 2
1249                 }
1250             } else {
1251                 throw new ClassCastException("Invalidated object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
1252             }
1253         }
1254 
1255         /**
1256          * {@inheritDoc}
1257          */
1258         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
1259             keyedPool.addObject(key);
1260         }
1261         
1262         /**
1263          * {@inheritDoc}
1264          */
1265         public int getNumIdle(final Object key) throws UnsupportedOperationException {
1266             return keyedPool.getNumIdle(key);
1267         }
1268 
1269         /**
1270          * {@inheritDoc}
1271          */
1272         public int getNumActive(final Object key) throws UnsupportedOperationException {
1273             return keyedPool.getNumActive(key);
1274         }
1275 
1276         /**
1277          * {@inheritDoc}
1278          */
1279         public int getNumIdle() throws UnsupportedOperationException {
1280             return keyedPool.getNumIdle();
1281         }
1282 
1283         /**
1284          * {@inheritDoc}
1285          */
1286         public int getNumActive() throws UnsupportedOperationException {
1287             return keyedPool.getNumActive();
1288         }
1289 
1290         /**
1291          * {@inheritDoc}
1292          */
1293         public void clear() throws Exception, UnsupportedOperationException {
1294             keyedPool.clear();
1295         }
1296 
1297         /**
1298          * {@inheritDoc}
1299          */
1300         public void clear(final Object key) throws Exception, UnsupportedOperationException {
1301             keyedPool.clear(key);
1302         }
1303 
1304         /**
1305          * {@inheritDoc}
1306          */
1307         public void close() {
1308             try {
1309                 keyedPool.close();
1310             } catch (Exception e) {
1311                 // swallowed as of Pool 2
1312             }
1313         }
1314 
1315         /**
1316          * Sets the object factory associated with the pool
1317          * 
1318          * @param factory object factory
1319          * @deprecated to be removed in version 2.0
1320          */
1321         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1322             keyedPool.setFactory(factory);
1323         }
1324 
1325         /**
1326          * {@inheritDoc}
1327          */
1328         public String toString() {
1329             final StringBuffer sb = new StringBuffer();
1330             sb.append("CheckedKeyedObjectPool");
1331             sb.append("{type=").append(type);
1332             sb.append(", keyedPool=").append(keyedPool);
1333             sb.append('}');
1334             return sb.toString();
1335         }
1336     }
1337 
1338     /**
1339      * Timer task that adds objects to the pool until the number of idle
1340      * instances reaches the configured minIdle.  Note that this is not the
1341      * same as the pool's minIdle setting.
1342      * 
1343      */
1344     private static class ObjectPoolMinIdleTimerTask extends TimerTask {
1345         
1346         /** Minimum number of idle instances.  Not the same as pool.getMinIdle(). */
1347         private final int minIdle;
1348         
1349         /** Object pool */
1350         private final ObjectPool pool;
1351 
1352         /**
1353          * Create a new ObjectPoolMinIdleTimerTask for the given pool with the given minIdle setting.
1354          * 
1355          * @param pool object pool
1356          * @param minIdle number of idle instances to maintain
1357          * @throws IllegalArgumentException if the pool is null
1358          */
1359         ObjectPoolMinIdleTimerTask(final ObjectPool pool, final int minIdle) throws IllegalArgumentException {
1360             if (pool == null) {
1361                 throw new IllegalArgumentException("pool must not be null.");
1362             }
1363             this.pool = pool;
1364             this.minIdle = minIdle;
1365         }
1366 
1367         /**
1368          * {@inheritDoc}
1369          */
1370         public void run() {
1371             boolean success = false;
1372             try {
1373                 if (pool.getNumIdle() < minIdle) {
1374                     pool.addObject();
1375                 }
1376                 success = true;
1377 
1378             } catch (Exception e) {
1379                 cancel();
1380 
1381             } finally {
1382                 // detect other types of Throwable and cancel this Timer
1383                 if (!success) {
1384                     cancel();
1385                 }
1386             }
1387         }
1388 
1389         /**
1390          * {@inheritDoc}
1391          */
1392         public String toString() {
1393             final StringBuffer sb = new StringBuffer();
1394             sb.append("ObjectPoolMinIdleTimerTask");
1395             sb.append("{minIdle=").append(minIdle);
1396             sb.append(", pool=").append(pool);
1397             sb.append('}');
1398             return sb.toString();
1399         }
1400     }
1401 
1402     /**
1403      * Timer task that adds objects to the pool until the number of idle
1404      * instances for the given key reaches the configured minIdle.  Note that this is not the
1405      * same as the pool's minIdle setting.
1406      * 
1407      */
1408     private static class KeyedObjectPoolMinIdleTimerTask extends TimerTask {
1409         /** Minimum number of idle instances.  Not the same as pool.getMinIdle(). */
1410         private final int minIdle;
1411         
1412         /** Key to ensure minIdle for */
1413         private final Object key;
1414         
1415         /** Keyed object pool */
1416         private final KeyedObjectPool keyedPool;
1417 
1418         /**
1419          * Create a new KeyedObjecPoolMinIdleTimerTask.
1420          * 
1421          * @param keyedPool keyed object pool
1422          * @param key key to ensure minimum number of idle instances
1423          * @param minIdle minimum number of idle instances 
1424          * @throws IllegalArgumentException if the key is null
1425          */
1426         KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool keyedPool, final Object key, final int minIdle) throws IllegalArgumentException {
1427             if (keyedPool == null) {
1428                 throw new IllegalArgumentException("keyedPool must not be null.");
1429             }
1430             this.keyedPool = keyedPool;
1431             this.key = key;
1432             this.minIdle = minIdle;
1433         }
1434 
1435         /**
1436          * {@inheritDoc}
1437          */
1438         public void run() {
1439             boolean success = false;
1440             try {
1441                 if (keyedPool.getNumIdle(key) < minIdle) {
1442                     keyedPool.addObject(key);
1443                 }
1444                 success = true;
1445 
1446             } catch (Exception e) {
1447                 cancel();
1448 
1449             } finally {
1450                 // detect other types of Throwable and cancel this Timer
1451                 if (!success) {
1452                     cancel();
1453                 }
1454             }
1455         }
1456 
1457         /**
1458          * {@inheritDoc}
1459          */
1460         public String toString() {
1461             final StringBuffer sb = new StringBuffer();
1462             sb.append("KeyedObjectPoolMinIdleTimerTask");
1463             sb.append("{minIdle=").append(minIdle);
1464             sb.append(", key=").append(key);
1465             sb.append(", keyedPool=").append(keyedPool);
1466             sb.append('}');
1467             return sb.toString();
1468         }
1469     }
1470 
1471     /**
1472      * A synchronized (thread-safe) ObjectPool backed by the specified ObjectPool.
1473      *
1474      * <p><b>Note:</b>
1475      * This should not be used on pool implementations that already provide proper synchronization
1476      * such as the pools provided in the Commons Pool library. Wrapping a pool that
1477      * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
1478      * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
1479      * </p>
1480      */
1481     private static class SynchronizedObjectPool implements ObjectPool {
1482         
1483         /** Object whose monitor is used to synchronize methods on the wrapped pool. */
1484         private final Object lock;
1485         
1486         /** the underlying object pool */
1487         private final ObjectPool pool;
1488 
1489         /**
1490          * Create a new SynchronizedObjectPool wrapping the given pool.
1491          * 
1492          * @param pool the ObjectPool to be "wrapped" in a synchronized ObjectPool.
1493          * @throws IllegalArgumentException if the pool is null
1494          */
1495         SynchronizedObjectPool(final ObjectPool pool) throws IllegalArgumentException {
1496             if (pool == null) {
1497                 throw new IllegalArgumentException("pool must not be null.");
1498             }
1499             this.pool = pool;
1500             lock = new Object();
1501         }
1502 
1503         /**
1504          * {@inheritDoc}
1505          */
1506         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
1507             synchronized (lock) {
1508                 return pool.borrowObject();
1509             }
1510         }
1511 
1512         /**
1513          * {@inheritDoc}
1514          */
1515         public void returnObject(final Object obj) {
1516             synchronized (lock) {
1517                 try {
1518                     pool.returnObject(obj);
1519                 } catch (Exception e) {
1520                     // swallowed as of Pool 2
1521                 }
1522             }
1523         }
1524 
1525         /**
1526          * {@inheritDoc}
1527          */
1528         public void invalidateObject(final Object obj) {
1529             synchronized (lock) {
1530                 try {
1531                     pool.invalidateObject(obj);
1532                 } catch (Exception e) {
1533                     // swallowed as of Pool 2
1534                 }
1535             }
1536         }
1537 
1538         /**
1539          * {@inheritDoc}
1540          */
1541         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
1542             synchronized (lock) {
1543                 pool.addObject();
1544             }
1545         }
1546 
1547         /**
1548          * {@inheritDoc}
1549          */
1550         public int getNumIdle() throws UnsupportedOperationException {
1551             synchronized (lock) {
1552                 return pool.getNumIdle();
1553             }
1554         }
1555 
1556         /**
1557          * {@inheritDoc}
1558          */
1559         public int getNumActive() throws UnsupportedOperationException {
1560             synchronized (lock) {
1561                 return pool.getNumActive();
1562             }
1563         }
1564 
1565         /**
1566          * {@inheritDoc}
1567          */
1568         public void clear() throws Exception, UnsupportedOperationException {
1569             synchronized (lock) {
1570                 pool.clear();
1571             }
1572         }
1573 
1574         /**
1575          * {@inheritDoc}
1576          */
1577         public void close() {
1578             try {
1579                 synchronized (lock) {
1580                     pool.close();
1581                 }
1582             } catch (Exception e) {
1583                 // swallowed as of Pool 2
1584             }
1585         }
1586 
1587         /**
1588          * Sets the factory used by the pool.
1589          * 
1590          * @param factory new PoolableObjectFactory
1591          * @deprecated to be removed in pool 2.0
1592          */
1593         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1594             synchronized (lock) {
1595                 pool.setFactory(factory);
1596             }
1597         }
1598 
1599         /**
1600          * {@inheritDoc}
1601          */
1602         public String toString() {
1603             final StringBuffer sb = new StringBuffer();
1604             sb.append("SynchronizedObjectPool");
1605             sb.append("{pool=").append(pool);
1606             sb.append('}');
1607             return sb.toString();
1608         }
1609     }
1610 
1611     /**
1612      * A synchronized (thread-safe) KeyedObjectPool backed by the specified KeyedObjectPool.
1613      *
1614      * <p><b>Note:</b>
1615      * This should not be used on pool implementations that already provide proper synchronization
1616      * such as the pools provided in the Commons Pool library. Wrapping a pool that
1617      * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
1618      * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
1619      * </p>
1620      */
1621     private static class SynchronizedKeyedObjectPool implements KeyedObjectPool {
1622         
1623         /** Object whose monitor is used to synchronize methods on the wrapped pool. */
1624         private final Object lock;
1625         
1626         /** Underlying object pool */
1627         private final KeyedObjectPool keyedPool;
1628 
1629         /**
1630          * Create a new SynchronizedKeyedObjectPool wrapping the given pool
1631          * 
1632          * @param keyedPool KeyedObjectPool to wrap
1633          * @throws IllegalArgumentException if keyedPool is null
1634          */
1635         SynchronizedKeyedObjectPool(final KeyedObjectPool keyedPool) throws IllegalArgumentException {
1636             if (keyedPool == null) {
1637                 throw new IllegalArgumentException("keyedPool must not be null.");
1638             }
1639             this.keyedPool = keyedPool;
1640             lock = new Object();
1641         }
1642 
1643         /**
1644          * {@inheritDoc}
1645          */
1646         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
1647             synchronized (lock) {
1648                 return keyedPool.borrowObject(key);
1649             }
1650         }
1651 
1652         /**
1653          * {@inheritDoc}
1654          */
1655         public void returnObject(final Object key, final Object obj) {
1656             synchronized (lock) {
1657                 try {
1658                     keyedPool.returnObject(key, obj);
1659                 } catch (Exception e) {
1660                     // swallowed
1661                 }
1662             }
1663         }
1664 
1665         /**
1666          * {@inheritDoc}
1667          */
1668         public void invalidateObject(final Object key, final Object obj) {
1669             synchronized (lock) {
1670                 try {
1671                     keyedPool.invalidateObject(key, obj);
1672                 } catch (Exception e) {
1673                     // swallowed as of Pool 2
1674                 }
1675             }
1676         }
1677 
1678         /**
1679          * {@inheritDoc}
1680          */
1681         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
1682             synchronized (lock) {
1683                 keyedPool.addObject(key);
1684             }
1685         }
1686 
1687         /**
1688          * {@inheritDoc}
1689          */
1690         public int getNumIdle(final Object key) throws UnsupportedOperationException {
1691             synchronized (lock) {
1692                 return keyedPool.getNumIdle(key);
1693             }
1694         }
1695 
1696         /**
1697          * {@inheritDoc}
1698          */
1699         public int getNumActive(final Object key) throws UnsupportedOperationException {
1700             synchronized (lock) {
1701                 return keyedPool.getNumActive(key);
1702             }
1703         }
1704 
1705         /**
1706          * {@inheritDoc}
1707          */
1708         public int getNumIdle() throws UnsupportedOperationException {
1709             synchronized (lock) {
1710                 return keyedPool.getNumIdle();
1711             }
1712         }
1713 
1714         /**
1715          * {@inheritDoc}
1716          */
1717         public int getNumActive() throws UnsupportedOperationException {
1718             synchronized (lock) {
1719                 return keyedPool.getNumActive();
1720             }
1721         }
1722 
1723         /**
1724          * {@inheritDoc}
1725          */
1726         public void clear() throws Exception, UnsupportedOperationException {
1727             synchronized (lock) {
1728                 keyedPool.clear();
1729             }
1730         }
1731 
1732         /**
1733          * {@inheritDoc}
1734          */
1735         public void clear(final Object key) throws Exception, UnsupportedOperationException {
1736             synchronized (lock) {
1737                 keyedPool.clear(key);
1738             }
1739         }
1740 
1741         /**
1742          * {@inheritDoc}
1743          */
1744         public void close() {
1745             try {
1746                 synchronized (lock) {
1747                     keyedPool.close();
1748                 }
1749             } catch (Exception e) {
1750                 // swallowed as of Pool 2
1751             }
1752         }
1753 
1754         /**
1755          * Sets the object factory used by the pool.
1756          * 
1757          * @param factory KeyedPoolableObjectFactory used by the pool
1758          * @deprecated to be removed in pool 2.0
1759          */
1760         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1761             synchronized (lock) {
1762                 keyedPool.setFactory(factory);
1763             }
1764         }
1765 
1766         /**
1767          * {@inheritDoc}
1768          */
1769         public String toString() {
1770             final StringBuffer sb = new StringBuffer();
1771             sb.append("SynchronizedKeyedObjectPool");
1772             sb.append("{keyedPool=").append(keyedPool);
1773             sb.append('}');
1774             return sb.toString();
1775         }
1776     }
1777 
1778     /**
1779      * A fully synchronized PoolableObjectFactory that wraps a PoolableObjectFactory and synchronizes
1780      * access to the wrapped factory methods.
1781      *
1782      * <p><b>Note:</b>
1783      * This should not be used on pool implementations that already provide proper synchronization
1784      * such as the pools provided in the Commons Pool library. </p>
1785      */
1786     private static class SynchronizedPoolableObjectFactory implements PoolableObjectFactory {
1787         /** Synchronization lock */
1788         private final Object lock;
1789         
1790         /** Wrapped factory */
1791         private final PoolableObjectFactory factory;
1792 
1793         /** 
1794          * Create a SynchronizedPoolableObjectFactory wrapping the given factory.
1795          * 
1796          * @param factory underlying factory to wrap
1797          * @throws IllegalArgumentException if the factory is null
1798          */
1799         SynchronizedPoolableObjectFactory(final PoolableObjectFactory factory) throws IllegalArgumentException {
1800             if (factory == null) {
1801                 throw new IllegalArgumentException("factory must not be null.");
1802             }
1803             this.factory = factory;
1804             lock = new Object();
1805         }
1806 
1807         /**
1808          * {@inheritDoc}
1809          */
1810         public Object makeObject() throws Exception {
1811             synchronized (lock) {
1812                 return factory.makeObject();
1813             }
1814         }
1815 
1816         /**
1817          * {@inheritDoc}
1818          */
1819         public void destroyObject(final Object obj) throws Exception {
1820             synchronized (lock) {
1821                 factory.destroyObject(obj);
1822             }
1823         }
1824 
1825         /**
1826          * {@inheritDoc}
1827          */
1828         public boolean validateObject(final Object obj) {
1829             synchronized (lock) {
1830                 return factory.validateObject(obj);
1831             }
1832         }
1833 
1834         /**
1835          * {@inheritDoc}
1836          */
1837         public void activateObject(final Object obj) throws Exception {
1838             synchronized (lock) {
1839                 factory.activateObject(obj);
1840             }
1841         }
1842 
1843         /**
1844          * {@inheritDoc}
1845          */
1846         public void passivateObject(final Object obj) throws Exception {
1847             synchronized (lock) {
1848                 factory.passivateObject(obj);
1849             }
1850         }
1851 
1852         /**
1853          * {@inheritDoc}
1854          */
1855         public String toString() {
1856             final StringBuffer sb = new StringBuffer();
1857             sb.append("SynchronizedPoolableObjectFactory");
1858             sb.append("{factory=").append(factory);
1859             sb.append('}');
1860             return sb.toString();
1861         }
1862     }
1863 
1864     /**
1865      * A fully synchronized KeyedPoolableObjectFactory that wraps a KeyedPoolableObjectFactory and synchronizes
1866      * access to the wrapped factory methods.
1867      *
1868      * <p><b>Note:</b>
1869      * This should not be used on pool implementations that already provide proper synchronization
1870      * such as the pools provided in the Commons Pool library. </p>
1871      */
1872     private static class SynchronizedKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory {
1873         /** Synchronization lock */
1874         private final Object lock;
1875         
1876         /** Wrapped factory */
1877         private final KeyedPoolableObjectFactory keyedFactory;
1878 
1879         /** 
1880          * Create a SynchronizedKeyedPoolableObjectFactory wrapping the given factory.
1881          * 
1882          * @param keyedFactory underlying factory to wrap
1883          * @throws IllegalArgumentException if the factory is null
1884          */
1885         SynchronizedKeyedPoolableObjectFactory(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
1886             if (keyedFactory == null) {
1887                 throw new IllegalArgumentException("keyedFactory must not be null.");
1888             }
1889             this.keyedFactory = keyedFactory;
1890             lock = new Object();
1891         }
1892 
1893         /**
1894          * {@inheritDoc}
1895          */
1896         public Object makeObject(final Object key) throws Exception {
1897             synchronized (lock) {
1898                 return keyedFactory.makeObject(key);
1899             }
1900         }
1901 
1902         /**
1903          * {@inheritDoc}
1904          */
1905         public void destroyObject(final Object key, final Object obj) throws Exception {
1906             synchronized (lock) {
1907                 keyedFactory.destroyObject(key, obj);
1908             }
1909         }
1910 
1911         /**
1912          * {@inheritDoc}
1913          */
1914         public boolean validateObject(final Object key, final Object obj) {
1915             synchronized (lock) {
1916                 return keyedFactory.validateObject(key, obj);
1917             }
1918         }
1919 
1920         /**
1921          * {@inheritDoc}
1922          */
1923         public void activateObject(final Object key, final Object obj) throws Exception {
1924             synchronized (lock) {
1925                 keyedFactory.activateObject(key, obj);
1926             }
1927         }
1928 
1929         /**
1930          * {@inheritDoc}
1931          */
1932         public void passivateObject(final Object key, final Object obj) throws Exception {
1933             synchronized (lock) {
1934                 keyedFactory.passivateObject(key, obj);
1935             }
1936         }
1937 
1938         /**
1939          * {@inheritDoc}
1940          */
1941         public String toString() {
1942             final StringBuffer sb = new StringBuffer();
1943             sb.append("SynchronizedKeyedPoolableObjectFactory");
1944             sb.append("{keyedFactory=").append(keyedFactory);
1945             sb.append('}');
1946             return sb.toString();
1947         }
1948     }
1949 
1950     /**
1951      * Encapsulate the logic for when the next poolable object should be discarded.
1952      * Each time update is called, the next time to shrink is recomputed, based on
1953      * the float factor, number of idle instances in the pool and high water mark.
1954      * Float factor is assumed to be between 0 and 1.  Values closer to 1 cause
1955      * less frequent erosion events.  Erosion event timing also depends on numIdle.
1956      * When this value is relatively high (close to previously established high water
1957      * mark), erosion occurs more frequently.
1958      */
1959     private static class ErodingFactor {
1960         /** Determines frequency of "erosion" events */
1961         private final float factor;
1962         
1963         /** Time of next shrink event */
1964         private transient volatile long nextShrink;
1965         
1966         /** High water mark - largest numIdle encountered */
1967         private transient volatile int idleHighWaterMark;
1968 
1969         /**
1970          * Create a new ErodingFactor with the given erosion factor.
1971          * 
1972          * @param factor erosion factor
1973          */
1974         public ErodingFactor(final float factor) {
1975             this.factor = factor;
1976             nextShrink = System.currentTimeMillis() + (long)(900000 * factor); // now + 15 min * factor
1977             idleHighWaterMark = 1;
1978         }
1979 
1980         /**
1981          * Updates internal state based on numIdle and the current time.
1982          * 
1983          * @param numIdle number of idle elements in the pool
1984          */
1985         public void update(final int numIdle) {
1986             update(System.currentTimeMillis(), numIdle);
1987         }
1988 
1989         /**
1990          * Updates internal state using the supplied time and numIdle.
1991          * 
1992          * @param now current time
1993          * @param numIdle number of idle elements in the pool
1994          */
1995         public void update(final long now, final int numIdle) {
1996             final int idle = Math.max(0, numIdle);
1997             idleHighWaterMark = Math.max(idle, idleHighWaterMark);
1998             final float maxInterval = 15f;
1999             final float minutes = maxInterval + ((1f-maxInterval)/idleHighWaterMark) * idle;
2000             nextShrink = now + (long)(minutes * 60000f * factor);
2001         }
2002 
2003         /**
2004          * Returns the time of the next erosion event.
2005          * 
2006          * @return next shrink time
2007          */
2008         public long getNextShrink() {
2009             return nextShrink;
2010         }
2011 
2012         /**
2013          * {@inheritDoc}
2014          */
2015         public String toString() {
2016             return "ErodingFactor{" +
2017                     "factor=" + factor +
2018                     ", idleHighWaterMark=" + idleHighWaterMark +
2019                     '}';
2020         }
2021     }
2022 
2023     /**
2024      * Decorates an object pool, adding "eroding" behavior.  Based on the
2025      * configured {@link #factor erosion factor}, objects returning to the pool
2026      * may be invalidated instead of being added to idle capacity.
2027      *
2028      */
2029     private static class ErodingObjectPool implements ObjectPool {
2030         /** Underlying object pool */
2031         private final ObjectPool pool;
2032         
2033         /** Erosion factor */
2034         private final ErodingFactor factor;
2035 
2036         /** 
2037          * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
2038          * 
2039          * @param pool underlying pool
2040          * @param factor erosion factor - determines the frequency of erosion events
2041          * @see #factor
2042          */
2043         public ErodingObjectPool(final ObjectPool pool, final float factor) {
2044             this.pool = pool;
2045             this.factor = new ErodingFactor(factor);
2046         }
2047 
2048         /**
2049          * {@inheritDoc}
2050          */
2051         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
2052             return pool.borrowObject();
2053         }
2054 
2055         /**
2056          * Returns obj to the pool, unless erosion is triggered, in which
2057          * case obj is invalidated.  Erosion is triggered when there are idle instances in 
2058          * the pool and more than the {@link #factor erosion factor}-determined time has elapsed
2059          * since the last returnObject activation. 
2060          * 
2061          * @param obj object to return or invalidate
2062          * @see #factor
2063          */
2064         public void returnObject(final Object obj) {
2065             boolean discard = false;
2066             final long now = System.currentTimeMillis();
2067             synchronized (pool) {
2068                 if (factor.getNextShrink() < now) { // XXX: Pool 3: move test out of sync block
2069                     final int numIdle = pool.getNumIdle();
2070                     if (numIdle > 0) {
2071                         discard = true;
2072                     }
2073 
2074                     factor.update(now, numIdle);
2075                 }
2076             }
2077             try {
2078                 if (discard) {
2079                     pool.invalidateObject(obj);
2080                 } else {
2081                     pool.returnObject(obj);
2082                 }
2083             } catch (Exception e) {
2084                 // swallowed
2085             }
2086         }
2087 
2088         /**
2089          * {@inheritDoc}
2090          */
2091         public void invalidateObject(final Object obj) {
2092             try {
2093                 pool.invalidateObject(obj);
2094             } catch (Exception e) {
2095                 // swallowed
2096             }
2097         }
2098 
2099         /**
2100          * {@inheritDoc}
2101          */
2102         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
2103             pool.addObject();
2104         }
2105 
2106         /**
2107          * {@inheritDoc}
2108          */
2109         public int getNumIdle() throws UnsupportedOperationException {
2110             return pool.getNumIdle();
2111         }
2112 
2113         /**
2114          * {@inheritDoc}
2115          */
2116         public int getNumActive() throws UnsupportedOperationException {
2117             return pool.getNumActive();
2118         }
2119 
2120         /**
2121          * {@inheritDoc}
2122          */
2123         public void clear() throws Exception, UnsupportedOperationException {
2124             pool.clear();
2125         }
2126 
2127         /**
2128          * {@inheritDoc}
2129          */
2130         public void close() {
2131             try {
2132                 pool.close();
2133             } catch (Exception e) {
2134                 // swallowed
2135             }
2136         }
2137 
2138         /**
2139          * {@inheritDoc}
2140          * @deprecated to be removed in pool 2.0
2141          */
2142         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
2143             pool.setFactory(factory);
2144         }
2145 
2146         /**
2147          * {@inheritDoc}
2148          */
2149         public String toString() {
2150             return "ErodingObjectPool{" +
2151                     "factor=" + factor +
2152                     ", pool=" + pool +
2153                     '}';
2154         }
2155     }
2156 
2157     /**
2158      * Decorates a keyed object pool, adding "eroding" behavior.  Based on the
2159      * configured {@link #factor erosion factor}, objects returning to the pool
2160      * may be invalidated instead of being added to idle capacity.
2161      *
2162      */
2163     private static class ErodingKeyedObjectPool implements KeyedObjectPool {
2164         /** Underlying pool */
2165         private final KeyedObjectPool keyedPool;
2166         
2167         /** Erosion factor */
2168         private final ErodingFactor erodingFactor;
2169 
2170         /** 
2171          * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
2172          * 
2173          * @param keyedPool underlying pool
2174          * @param factor erosion factor - determines the frequency of erosion events
2175          * @see #erodingFactor
2176          */
2177         public ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) {
2178             this(keyedPool, new ErodingFactor(factor));
2179         }
2180 
2181         /** 
2182          * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
2183          * 
2184          * @param keyedPool underlying pool - must not be null
2185          * @param erodingFactor erosion factor - determines the frequency of erosion events
2186          * @see #factor
2187          */
2188         protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final ErodingFactor erodingFactor) {
2189             if (keyedPool == null) {
2190                 throw new IllegalArgumentException("keyedPool must not be null.");
2191             }
2192             this.keyedPool = keyedPool;
2193             this.erodingFactor = erodingFactor;
2194         }
2195 
2196         /**
2197          * {@inheritDoc}
2198          */
2199         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
2200             return keyedPool.borrowObject(key);
2201         }
2202 
2203         /**
2204          * Returns obj to the pool, unless erosion is triggered, in which
2205          * case obj is invalidated.  Erosion is triggered when there are idle instances in 
2206          * the pool associated with the given key and more than the configured {@link #erodingFactor erosion factor}
2207          * time has elapsed since the last returnObject activation. 
2208          * 
2209          * @param obj object to return or invalidate
2210          * @param key key
2211          * @see #erodingFactor
2212          */
2213         public void returnObject(final Object key, final Object obj) throws Exception {
2214             boolean discard = false;
2215             final long now = System.currentTimeMillis();
2216             final ErodingFactor factor = getErodingFactor(key);
2217             synchronized (keyedPool) {
2218                 if (factor.getNextShrink() < now) {
2219                     final int numIdle = numIdle(key);
2220                     if (numIdle > 0) {
2221                         discard = true;
2222                     }
2223 
2224                     factor.update(now, numIdle);
2225                 }
2226             }
2227             try {
2228                 if (discard) {
2229                     keyedPool.invalidateObject(key, obj);
2230                 } else {
2231                     keyedPool.returnObject(key, obj);
2232                 }
2233             } catch (Exception e) {
2234                 // swallowed
2235             }
2236         }
2237 
2238         protected int numIdle(final Object key) {
2239             return getKeyedPool().getNumIdle();
2240         }
2241 
2242         /**
2243          * Returns the eroding factor for the given key
2244          * @param key key
2245          * @return eroding factor for the given keyed pool
2246          */
2247         protected ErodingFactor getErodingFactor(final Object key) {
2248             return erodingFactor;
2249         }
2250 
2251         /**
2252          * {@inheritDoc}
2253          */
2254         public void invalidateObject(final Object key, final Object obj) {
2255             try {
2256                 keyedPool.invalidateObject(key, obj);
2257             } catch (Exception e) {
2258                 // swallowed
2259             }
2260         }
2261 
2262         /**
2263          * {@inheritDoc}
2264          */
2265         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
2266             keyedPool.addObject(key);
2267         }
2268 
2269         /**
2270          * {@inheritDoc}
2271          */
2272         public int getNumIdle() throws UnsupportedOperationException {
2273             return keyedPool.getNumIdle();
2274         }
2275 
2276         /**
2277          * {@inheritDoc}
2278          */
2279         public int getNumIdle(final Object key) throws UnsupportedOperationException {
2280             return keyedPool.getNumIdle(key);
2281         }
2282 
2283         /**
2284          * {@inheritDoc}
2285          */
2286         public int getNumActive() throws UnsupportedOperationException {
2287             return keyedPool.getNumActive();
2288         }
2289 
2290         /**
2291          * {@inheritDoc}
2292          */
2293         public int getNumActive(final Object key) throws UnsupportedOperationException {
2294             return keyedPool.getNumActive(key);
2295         }
2296 
2297         /**
2298          * {@inheritDoc}
2299          */
2300         public void clear() throws Exception, UnsupportedOperationException {
2301             keyedPool.clear();
2302         }
2303 
2304         /**
2305          * {@inheritDoc}
2306          */
2307         public void clear(final Object key) throws Exception, UnsupportedOperationException {
2308             keyedPool.clear(key);
2309         }
2310 
2311         /**
2312          * {@inheritDoc}
2313          */
2314         public void close() {
2315             try {
2316                 keyedPool.close();
2317             } catch (Exception e) {
2318                 // swallowed
2319             }
2320         }
2321 
2322         /**
2323          * {@inheritDoc}
2324          * @deprecated to be removed in pool 2.0
2325          */
2326         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
2327             keyedPool.setFactory(factory);
2328         }
2329 
2330         /**
2331          * Returns the underlying pool
2332          * 
2333          * @return the keyed pool that this ErodingKeyedObjectPool wraps
2334          */
2335         protected KeyedObjectPool getKeyedPool() {
2336             return keyedPool;
2337         }
2338 
2339         /**
2340          * {@inheritDoc}
2341          */
2342         public String toString() {
2343             return "ErodingKeyedObjectPool{" +
2344                     "erodingFactor=" + erodingFactor +
2345                     ", keyedPool=" + keyedPool +
2346                     '}';
2347         }
2348     }
2349 
2350     /**
2351      * Extends ErodingKeyedObjectPool to allow erosion to take place on a per-key
2352      * basis.  Timing of erosion events is tracked separately for separate keyed pools.
2353      */
2354     private static class ErodingPerKeyKeyedObjectPool extends ErodingKeyedObjectPool {
2355         /** Erosion factor - same for all pools */
2356         private final float factor;
2357         
2358         /** Map of ErodingFactor instances keyed on pool keys */
2359         private final Map factors = Collections.synchronizedMap(new HashMap());
2360 
2361         /**
2362          * Create a new ErordingPerKeyKeyedObjectPool decorating the given keyed pool with
2363          * the specified erosion factor.
2364          * @param keyedPool underlying keyed pool
2365          * @param factor erosion factor
2366          */
2367         public ErodingPerKeyKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) {
2368             super(keyedPool, null);
2369             this.factor = factor;
2370         }
2371 
2372         /**
2373          * {@inheritDoc}
2374          */
2375         protected int numIdle(final Object key) {
2376             return getKeyedPool().getNumIdle(key);
2377         }
2378 
2379         /**
2380          * {@inheritDoc}
2381          */
2382         protected ErodingFactor getErodingFactor(final Object key) {
2383             ErodingFactor factor = (ErodingFactor)factors.get(key);
2384             // this may result in two ErodingFactors being created for a key
2385             // since they are small and cheap this is okay.
2386             if (factor == null) {
2387                 factor = new ErodingFactor(this.factor);
2388                 factors.put(key, factor);
2389             }
2390             return factor;
2391         }
2392 
2393         /**
2394          * {@inheritDoc}
2395          */
2396         public String toString() {
2397             return "ErodingPerKeyKeyedObjectPool{" +
2398                     "factor=" + factor +
2399                     ", keyedPool=" + getKeyedPool() +
2400                     '}';
2401         }
2402     }
2403 }