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