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