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.impl.GenericKeyedObjectPool;
18 
19 import hunt.pool.impl.BaseGenericObjectPool;
20 import hunt.pool.KeyedObjectPool;
21 import hunt.pool.KeyedPooledObjectFactory;
22 import hunt.pool.PoolUtils;
23 import hunt.pool.PooledObject;
24 import hunt.pool.PooledObjectState;
25 import hunt.pool.SwallowedExceptionListener;
26 
27 import hunt.collection;
28 import hunt.Exceptions;
29 import hunt.Integer;
30 import hunt.concurrency.LinkedBlockingQueue;
31 
32 import core.sync.rwmutex;
33 
34 /**
35  * A configurable <code>KeyedObjectPool</code> implementation.
36  * <p>
37  * When coupled with the appropriate {@link KeyedPooledObjectFactory},
38  * <code>GenericKeyedObjectPool</code> provides robust pooling functionality for
39  * keyed objects. A <code>GenericKeyedObjectPool</code> can be viewed as a map
40  * of sub-pools, keyed on the (unique) key values provided to the
41  * {@link #preparePool preparePool}, {@link #addObject addObject} or
42  * {@link #borrowObject borrowObject} methods. Each time a new key value is
43  * provided to one of these methods, a sub-new pool is created under the given
44  * key to be managed by the containing <code>GenericKeyedObjectPool.</code>
45  * <p>
46  * Note that the current implementation uses a ConcurrentHashMap which uses
47  * equals() to compare keys.
48  * This means that distinct instance keys must be distinguishable using equals.
49  * <p>
50  * Optionally, one may configure the pool to examine and possibly evict objects
51  * as they sit idle in the pool and to ensure that a minimum number of idle
52  * objects is maintained for each key. This is performed by an "idle object
53  * eviction" thread, which runs asynchronously. Caution should be used when
54  * configuring this optional feature. Eviction runs contend with client threads
55  * for access to objects in the pool, so if they run too frequently performance
56  * issues may result.
57  * <p>
58  * Implementation note: To prevent possible deadlocks, care has been taken to
59  * ensure that no call to a factory method will occur within a synchronization
60  * block. See POOL-125 and DBCP-44 for more information.
61  * <p>
62  * This class is intended to be thread-safe.
63  *
64  * @see GenericObjectPool
65  *
66  * @param <K> The type of keys maintained by this pool.
67  * @param <T> Type of element pooled in this pool.
68  *
69  */
70 class GenericKeyedObjectPool(K, T) : BaseGenericObjectPool!(T),
71         KeyedObjectPool!(K, T), GenericKeyedObjectPoolMXBean!(K) {
72 
73     /**
74      * Create a new <code>GenericKeyedObjectPool</code> using defaults from
75      * {@link GenericKeyedObjectPoolConfig}.
76      * @param factory the factory to be used to create entries
77      */
78     this(KeyedPooledObjectFactory!(K,T) factory) {
79         this(factory, new GenericKeyedObjectPoolConfig!(T)());
80     }
81 
82     /**
83      * Create a new <code>GenericKeyedObjectPool</code> using a specific
84      * configuration.
85      *
86      * @param factory the factory to be used to create entries
87      * @param config    The configuration to use for this pool instance. The
88      *                  configuration is used by value. Subsequent changes to
89      *                  the configuration object will not be reflected in the
90      *                  pool.
91      */
92     this(KeyedPooledObjectFactory!(K, T) factory,
93             GenericKeyedObjectPoolConfig!(T) config) {
94 
95         super(config, ONAME_BASE, config.getJmxNamePrefix());
96 
97         if (factory is null) {
98             // jmxUnregister(); // tidy up
99             throw new IllegalArgumentException("factory may not be null");
100         }
101         this.factory = factory;
102         this.fairness = config.getFairness();
103 
104         setConfig(config);
105     }
106 
107     /**
108      * Returns the limit on the number of object instances allocated by the pool
109      * (checked out or idle), per key. When the limit is reached, the sub-pool
110      * is said to be exhausted. A negative value indicates no limit.
111      *
112      * @return the limit on the number of active instances per key
113      *
114      * @see #setMaxTotalPerKey
115      */
116     override
117     int getMaxTotalPerKey() {
118         return maxTotalPerKey;
119     }
120 
121     /**
122      * Sets the limit on the number of object instances allocated by the pool
123      * (checked out or idle), per key. When the limit is reached, the sub-pool
124      * is said to be exhausted. A negative value indicates no limit.
125      *
126      * @param maxTotalPerKey the limit on the number of active instances per key
127      *
128      * @see #getMaxTotalPerKey
129      */
130     void setMaxTotalPerKey(int maxTotalPerKey) {
131         this.maxTotalPerKey = maxTotalPerKey;
132     }
133 
134 
135     /**
136      * Returns the cap on the number of "idle" instances per key in the pool.
137      * If maxIdlePerKey is set too low on heavily loaded systems it is possible
138      * you will see objects being destroyed and almost immediately new objects
139      * being created. This is a result of the active threads momentarily
140      * returning objects faster than they are requesting them, causing the
141      * number of idle objects to rise above maxIdlePerKey. The best value for
142      * maxIdlePerKey for heavily loaded system will vary but the default is a
143      * good starting point.
144      *
145      * @return the maximum number of "idle" instances that can be held in a
146      *         given keyed sub-pool or a negative value if there is no limit
147      *
148      * @see #setMaxIdlePerKey
149      */
150     override
151     int getMaxIdlePerKey() {
152         return maxIdlePerKey;
153     }
154 
155     /**
156      * Sets the cap on the number of "idle" instances per key in the pool.
157      * If maxIdlePerKey is set too low on heavily loaded systems it is possible
158      * you will see objects being destroyed and almost immediately new objects
159      * being created. This is a result of the active threads momentarily
160      * returning objects faster than they are requesting them, causing the
161      * number of idle objects to rise above maxIdlePerKey. The best value for
162      * maxIdlePerKey for heavily loaded system will vary but the default is a
163      * good starting point.
164      *
165      * @param maxIdlePerKey the maximum number of "idle" instances that can be
166      *                      held in a given keyed sub-pool. Use a negative value
167      *                      for no limit
168      *
169      * @see #getMaxIdlePerKey
170      */
171     void setMaxIdlePerKey(int maxIdlePerKey) {
172         this.maxIdlePerKey = maxIdlePerKey;
173     }
174 
175     /**
176      * Sets the target for the minimum number of idle objects to maintain in
177      * each of the keyed sub-pools. This setting only has an effect if it is
178      * positive and {@link #getTimeBetweenEvictionRunsMillis()} is greater than
179      * zero. If this is the case, an attempt is made to ensure that each
180      * sub-pool has the required minimum number of instances during idle object
181      * eviction runs.
182      * <p>
183      * If the configured value of minIdlePerKey is greater than the configured
184      * value for maxIdlePerKey then the value of maxIdlePerKey will be used
185      * instead.
186      *
187      * @param minIdlePerKey The minimum size of the each keyed pool
188      *
189      * @see #getMinIdlePerKey
190      * @see #getMaxIdlePerKey()
191      * @see #setTimeBetweenEvictionRunsMillis
192      */
193     void setMinIdlePerKey(int minIdlePerKey) {
194         this.minIdlePerKey = minIdlePerKey;
195     }
196 
197     /**
198      * Returns the target for the minimum number of idle objects to maintain in
199      * each of the keyed sub-pools. This setting only has an effect if it is
200      * positive and {@link #getTimeBetweenEvictionRunsMillis()} is greater than
201      * zero. If this is the case, an attempt is made to ensure that each
202      * sub-pool has the required minimum number of instances during idle object
203      * eviction runs.
204      * <p>
205      * If the configured value of minIdlePerKey is greater than the configured
206      * value for maxIdlePerKey then the value of maxIdlePerKey will be used
207      * instead.
208      *
209      * @return minimum size of the each keyed pool
210      *
211      * @see #setTimeBetweenEvictionRunsMillis
212      */
213     override
214     int getMinIdlePerKey() {
215         int maxIdlePerKeySave = getMaxIdlePerKey();
216         if (this.minIdlePerKey > maxIdlePerKeySave) {
217             return maxIdlePerKeySave;
218         }
219         return minIdlePerKey;
220     }
221 
222     /**
223      * Sets the configuration.
224      *
225      * @param conf the new configuration to use. This is used by value.
226      *
227      * @see GenericKeyedObjectPoolConfig
228      */
229     void setConfig(GenericKeyedObjectPoolConfig!(T) conf) {
230         super.setConfig(conf);
231         setMaxIdlePerKey(conf.getMaxIdlePerKey());
232         setMaxTotalPerKey(conf.getMaxTotalPerKey());
233         setMaxTotal(conf.getMaxTotal());
234         setMinIdlePerKey(conf.getMinIdlePerKey());
235     }
236 
237     /**
238      * Obtain a reference to the factory used to create, destroy and validate
239      * the objects used by this pool.
240      *
241      * @return the factory
242      */
243     KeyedPooledObjectFactory!(K, T) getFactory() {
244         return factory;
245     }
246 
247     /**
248      * Equivalent to <code>{@link #borrowObject(Object, long) borrowObject}(key,
249      * {@link #getMaxWaitMillis()})</code>.
250      * <p>
251      * {@inheritDoc}
252      */
253     override
254     T borrowObject(K key){
255         return borrowObject(key, getMaxWaitMillis());
256     }
257 
258     /**
259      * Borrows an object from the sub-pool associated with the given key using
260      * the specified waiting time which only applies if
261      * {@link #getBlockWhenExhausted()} is true.
262      * <p>
263      * If there is one or more idle instances available in the sub-pool
264      * associated with the given key, then an idle instance will be selected
265      * based on the value of {@link #getLifo()}, activated and returned.  If
266      * activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set to
267      * <code>true</code> and validation fails, the instance is destroyed and the
268      * next available instance is examined.  This continues until either a valid
269      * instance is returned or there are no more idle instances available.
270      * <p>
271      * If there are no idle instances available in the sub-pool associated with
272      * the given key, behavior depends on the {@link #getMaxTotalPerKey()
273      * maxTotalPerKey}, {@link #getMaxTotal() maxTotal}, and (if applicable)
274      * {@link #getBlockWhenExhausted()} and the value passed in to the
275      * <code>borrowMaxWaitMillis</code> parameter. If the number of instances checked
276      * out from the sub-pool under the given key is less than
277      * <code>maxTotalPerKey</code> and the total number of instances in
278      * circulation (under all keys) is less than <code>maxTotal</code>, a new
279      * instance is created, activated and (if applicable) validated and returned
280      * to the caller. If validation fails, a <code>NoSuchElementException</code>
281      * will be thrown.
282      * <p>
283      * If the associated sub-pool is exhausted (no available idle instances and
284      * no capacity to create new ones), this method will either block
285      * ({@link #getBlockWhenExhausted()} is true) or throw a
286      * <code>NoSuchElementException</code>
287      * ({@link #getBlockWhenExhausted()} is false).
288      * The length of time that this method will block when
289      * {@link #getBlockWhenExhausted()} is true is determined by the value
290      * passed in to the <code>borrowMaxWait</code> parameter.
291      * <p>
292      * When <code>maxTotal</code> is set to a positive value and this method is
293      * invoked when at the limit with no idle instances available under the requested
294      * key, an attempt is made to create room by clearing the oldest 15% of the
295      * elements from the keyed sub-pools.
296      * <p>
297      * When the pool is exhausted, multiple calling threads may be
298      * simultaneously blocked waiting for instances to become available. A
299      * "fairness" algorithm has been implemented to ensure that threads receive
300      * available instances in request arrival order.
301      *
302      * @param key pool key
303      * @param borrowMaxWaitMillis The time to wait in milliseconds for an object
304      *                            to become available
305      *
306      * @return object instance from the keyed pool
307      *
308      * @throws NoSuchElementException if a keyed object instance cannot be
309      *                                returned because the pool is exhausted.
310      *
311      * @throws Exception if a keyed object instance cannot be returned due to an
312      *                   error
313      */
314     T borrowObject(K key, long borrowMaxWaitMillis){
315         assertOpen();
316 
317         PooledObject!(T) p = null;
318 
319         // Get local copy of current config so it is consistent for entire
320         // method execution
321         bool blockWhenExhausted = getBlockWhenExhausted();
322 
323         bool create;
324         long waitTime = DateTimeHelper.currentTimeMillis();
325         ObjectDeque!(T) objectDeque = register(key);
326 
327         try {
328             while (p is null) {
329                 create = false;
330                 p = objectDeque.getIdleObjects().pollFirst();
331                 if (p is null) {
332                     p = create(key);
333                     if (p !is null) {
334                         create = true;
335                     }
336                 }
337                 if (blockWhenExhausted) {
338                     if (p is null) {
339                         if (borrowMaxWaitMillis < 0) {
340                             p = objectDeque.getIdleObjects().takeFirst();
341                         } else {
342                             p = objectDeque.getIdleObjects().pollFirst(
343                                     borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
344                         }
345                     }
346                     if (p is null) {
347                         throw new NoSuchElementException(
348                                 "Timeout waiting for idle object");
349                     }
350                 } else {
351                     if (p is null) {
352                         throw new NoSuchElementException("Pool exhausted");
353                     }
354                 }
355                 if (!p.allocate()) {
356                     p = null;
357                 }
358 
359                 if (p !is null) {
360                     try {
361                         factory.activateObject(key, p);
362                     } catch (Exception e) {
363                         try {
364                             destroy(key, p, true);
365                         } catch (Exception e1) {
366                             // Ignore - activation failure is more important
367                         }
368                         p = null;
369                         if (create) {
370                             NoSuchElementException nsee = new NoSuchElementException(
371                                     "Unable to activate object");
372                             nsee.initCause(e);
373                             throw nsee;
374                         }
375                     }
376                     if (p !is null && (getTestOnBorrow() || create && getTestOnCreate())) {
377                         bool validate = false;
378                         Throwable validationThrowable = null;
379                         try {
380                             validate = factory.validateObject(key, p);
381                         } catch (Throwable t) {
382                             PoolUtils.checkRethrow(t);
383                             validationThrowable = t;
384                         }
385                         if (!validate) {
386                             try {
387                                 destroy(key, p, true);
388                                 destroyedByBorrowValidationCount.incrementAndGet();
389                             } catch (Exception e) {
390                                 // Ignore - validation failure is more important
391                             }
392                             p = null;
393                             if (create) {
394                                 NoSuchElementException nsee = new NoSuchElementException(
395                                         "Unable to validate object");
396                                 nsee.initCause(validationThrowable);
397                                 throw nsee;
398                             }
399                         }
400                     }
401                 }
402             }
403         } finally {
404             deregister(key);
405         }
406 
407         updateStatsBorrow(p, DateTimeHelper.currentTimeMillis() - waitTime);
408 
409         return p.getObject();
410     }
411 
412 
413     /**
414      * Returns an object to a keyed sub-pool.
415      * <p>
416      * If {@link #getMaxIdlePerKey() maxIdle} is set to a positive value and the
417      * number of idle instances under the given key has reached this value, the
418      * returning instance is destroyed.
419      * <p>
420      * If {@link #getTestOnReturn() testOnReturn} == true, the returning
421      * instance is validated before being returned to the idle instance sub-pool
422      * under the given key. In this case, if validation fails, the instance is
423      * destroyed.
424      * <p>
425      * Exceptions encountered destroying objects for any reason are swallowed
426      * but notified via a {@link SwallowedExceptionListener}.
427      *
428      * @param key pool key
429      * @param obj instance to return to the keyed pool
430      *
431      * @throws IllegalStateException if an object is returned to the pool that
432      *                               was not borrowed from it or if an object is
433      *                               returned to the pool multiple times
434      */
435     override
436     void returnObject(K key, T obj) {
437 
438         ObjectDeque!(T) objectDeque = poolMap.get(key);
439 
440         PooledObject!(T) p = objectDeque.getAllObjects().get(new IdentityWrapper!T(obj));
441 
442         if (p is null) {
443             throw new IllegalStateException(
444                     "Returned object not currently part of this pool");
445         }
446 
447         markReturningState(p);
448 
449         long activeTime = p.getActiveTimeMillis();
450 
451         try {
452             if (getTestOnReturn() && !factory.validateObject(key, p)) {
453                 try {
454                     destroy(key, p, true);
455                 } catch (Exception e) {
456                     swallowException(e);
457                 }
458                 whenWaitersAddObject(key, objectDeque.idleObjects);
459                 return;
460             }
461 
462             try {
463                 factory.passivateObject(key, p);
464             } catch (Exception e1) {
465                 swallowException(e1);
466                 try {
467                     destroy(key, p, true);
468                 } catch (Exception e) {
469                     swallowException(e);
470                 }
471                 whenWaitersAddObject(key, objectDeque.idleObjects);
472                 return;
473             }
474 
475             if (!p.deallocate()) {
476                 throw new IllegalStateException(
477                         "Object has already been returned to this pool");
478             }
479 
480             int maxIdle = getMaxIdlePerKey();
481             LinkedBlockingDeque!(PooledObject!(T)) idleObjects =
482                     objectDeque.getIdleObjects();
483 
484             if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) {
485                 try {
486                     destroy(key, p, true);
487                 } catch (Exception e) {
488                     swallowException(e);
489                 }
490             } else {
491                 if (getLifo()) {
492                     idleObjects.addFirst(p);
493                 } else {
494                     idleObjects.addLast(p);
495                 }
496                 if (isClosed()) {
497                     // Pool closed while object was being added to idle objects.
498                     // Make sure the returned object is destroyed rather than left
499                     // in the idle object pool (which would effectively be a leak)
500                     clear(key);
501                 }
502             }
503         } finally {
504             if (hasBorrowWaiters()) {
505                 reuseCapacity();
506             }
507             updateStatsReturn(activeTime);
508         }
509     }
510 
511     /**
512      * Whether there is at least one thread waiting on this deque, add an pool object.
513      * @param key
514      * @param idleObjects
515      */
516     private void whenWaitersAddObject(K key, LinkedBlockingDeque!(PooledObject!(T)) idleObjects) {
517         if (idleObjects.hasTakeWaiters()) {
518             try {
519                 addObject(key);
520             } catch (Exception e) {
521                 swallowException(e);
522             }
523         }
524     }
525 
526     /**
527      * {@inheritDoc}
528      * <p>
529      * Activation of this method decrements the active count associated with
530      * the given keyed pool and attempts to destroy <code>obj.</code>
531      *
532      * @param key pool key
533      * @param obj instance to invalidate
534      *
535      * @throws Exception             if an exception occurs destroying the
536      *                               object
537      * @throws IllegalStateException if obj does not belong to the pool
538      *                               under the given key
539      */
540     override
541     void invalidateObject(K key, T obj){
542 
543         ObjectDeque!(T) objectDeque = poolMap.get(key);
544 
545         PooledObject!(T) p = objectDeque.getAllObjects().get(new IdentityWrapper!T(obj));
546         if (p is null) {
547             throw new IllegalStateException(
548                     "Object not currently part of this pool");
549         }
550         synchronized (p) {
551             if (p.getState() != PooledObjectState.INVALID) {
552                 destroy(key, p, true);
553             }
554         }
555         if (objectDeque.idleObjects.hasTakeWaiters()) {
556             addObject(key);
557         }
558     }
559 
560 
561     /**
562      * Clears any objects sitting idle in the pool by removing them from the
563      * idle instance sub-pools and then invoking the configured
564      * PoolableObjectFactory's
565      * {@link KeyedPooledObjectFactory#destroyObject(Object, PooledObject)}
566      * method on each idle instance.
567      * <p>
568      * Implementation notes:
569      * <ul>
570      * <li>This method does not destroy or effect in any way instances that are
571      * checked out when it is invoked.</li>
572      * <li>Invoking this method does not prevent objects being returned to the
573      * idle instance pool, even during its execution. Additional instances may
574      * be returned while removed items are being destroyed.</li>
575      * <li>Exceptions encountered destroying idle instances are swallowed
576      * but notified via a {@link SwallowedExceptionListener}.</li>
577      * </ul>
578      */
579     override
580     void clear() {
581         Iterator!(K) iter = poolMap.keySet().iterator();
582 
583         while (iter.hasNext()) {
584             clear(iter.next());
585         }
586     }
587 
588 
589     /**
590      * Clears the specified sub-pool, removing all pooled instances
591      * corresponding to the given <code>key</code>. Exceptions encountered
592      * destroying idle instances are swallowed but notified via a
593      * {@link SwallowedExceptionListener}.
594      *
595      * @param key the key to clear
596      */
597     override
598     void clear(K key) {
599 
600         ObjectDeque!(T) objectDeque = register(key);
601 
602         try {
603             LinkedBlockingDeque!(PooledObject!(T)) idleObjects =
604                     objectDeque.getIdleObjects();
605 
606             PooledObject!(T) p = idleObjects.poll();
607 
608             while (p !is null) {
609                 try {
610                     destroy(key, p, true);
611                 } catch (Exception e) {
612                     swallowException(e);
613                 }
614                 p = idleObjects.poll();
615             }
616         } finally {
617             deregister(key);
618         }
619     }
620 
621 
622     override
623     int getNumActive() {
624         return numTotal.get() - getNumIdle();
625     }
626 
627 
628     override
629     int getNumIdle() {
630         Iterator!(ObjectDeque!(T)) iter = poolMap.values().iterator();
631         int result = 0;
632 
633         while (iter.hasNext()) {
634             result += iter.next().getIdleObjects().size();
635         }
636 
637         return result;
638     }
639 
640 
641     override
642     int getNumActive(K key) {
643         ObjectDeque!(T) objectDeque = poolMap.get(key);
644         if (objectDeque !is null) {
645             return objectDeque.getAllObjects().size() -
646                     objectDeque.getIdleObjects().size();
647         }
648         return 0;
649     }
650 
651 
652     override
653     int getNumIdle(K key) {
654         ObjectDeque!(T) objectDeque = poolMap.get(key);
655         return objectDeque !is null ? objectDeque.getIdleObjects().size() : 0;
656     }
657 
658 
659     /**
660      * Closes the keyed object pool. Once the pool is closed,
661      * {@link #borrowObject(Object)} will fail with IllegalStateException, but
662      * {@link #returnObject(Object, Object)} and
663      * {@link #invalidateObject(Object, Object)} will continue to work, with
664      * returned objects destroyed on return.
665      * <p>
666      * Destroys idle instances in the pool by invoking {@link #clear()}.
667      */
668     override
669     void close() {
670         if (isClosed()) {
671             return;
672         }
673 
674         synchronized (closeLock) {
675             if (isClosed()) {
676                 return;
677             }
678 
679             // Stop the evictor before the pool is closed since evict() calls
680             // assertOpen()
681             stopEvitor();
682 
683             closed = true;
684             // This clear removes any idle objects
685             clear();
686 
687             // jmxUnregister();
688 
689             // Release any threads that were waiting for an object
690             Iterator!(ObjectDeque!(T)) iter = poolMap.values().iterator();
691             while (iter.hasNext()) {
692                 iter.next().getIdleObjects().interuptTakeWaiters();
693             }
694             // This clear cleans up the keys now any waiting threads have been
695             // interrupted
696             clear();
697         }
698     }
699 
700 
701     /**
702      * Clears oldest 15% of objects in pool.  The method sorts the objects into
703      * a TreeMap and then iterates the first 15% for removal.
704      */
705     void clearOldest() {
706 
707         // build sorted map of idle objects
708         Map!(PooledObject!(T), K) map = new TreeMap!(PooledObject!(T), K)();
709 
710         foreach (K k, ObjectDeque!(T) deque; poolMap) {
711             // Protect against possible NPE if key has been removed in another
712             // thread. Not worth locking the keys while this loop completes.
713             if (deque !is null) {
714                 LinkedBlockingDeque!(PooledObject!(T)) idleObjects =
715                         deque.getIdleObjects();
716                 foreach (PooledObject!(T) p ; idleObjects) {
717                     // each item into the map using the PooledObject object as the
718                     // key. It then gets sorted based on the idle time
719                     map.put(p, k);
720                 }
721             }
722         }
723 
724         // Now iterate created map and kill the first 15% plus one to account
725         // for zero
726         int itemsToRemove = (cast(int) (map.size() * 0.15)) + 1;
727         // Iterator<Map.Entry!(PooledObject!(T), K)> iter =
728         //         map.entrySet().iterator();
729 
730         // while (iter.hasNext() && itemsToRemove > 0) {
731         //     Map.Entry!(PooledObject!(T), K) entry = iter.next();
732         //     // kind of backwards on naming.  In the map, each key is the
733         //     // PooledObject because it has the ordering with the timestamp
734         //     // value.  Each value that the key references is the key of the
735         //     // list it belongs to.
736         //     K key = entry.getValue();
737         //     PooledObject!(T) p = entry.getKey();
738         //     // Assume the destruction succeeds
739         //     bool destroyed = true;
740         //     try {
741         //         destroyed = destroy(key, p, false);
742         //     } catch (Exception e) {
743         //         swallowException(e);
744         //     }
745         //     if (destroyed) {
746         //         itemsToRemove--;
747         //     }
748         // }
749         implementationMissing(false);
750     }
751 
752     /**
753      * Attempt to create one new instance to serve from the most heavily
754      * loaded pool that can add a new instance.
755      *
756      * This method exists to ensure liveness in the pool when threads are
757      * parked waiting and capacity to create instances under the requested keys
758      * subsequently becomes available.
759      *
760      * This method is not guaranteed to create an instance and its selection
761      * of the most loaded pool that can create an instance may not always be
762      * correct, since it does not lock the pool and instances may be created,
763      * borrowed, returned or destroyed by other threads while it is executing.
764      */
765     private void reuseCapacity() {
766         int maxTotalPerKeySave = getMaxTotalPerKey();
767 
768         // Find the most loaded pool that could take a new instance
769         int maxQueueLength = 0;
770         LinkedBlockingDeque!(PooledObject!(T)) mostLoaded = null;
771         K loadedKey = null;
772         foreach (K k, ObjectDeque!(T) deque; poolMap) {
773             if (deque !is null) {
774                 LinkedBlockingDeque!(PooledObject!(T)) pool = deque.getIdleObjects();
775                 int queueLength = pool.getTakeQueueLength();
776                 if (getNumActive(k) < maxTotalPerKeySave && queueLength > maxQueueLength) {
777                     maxQueueLength = queueLength;
778                     mostLoaded = pool;
779                     loadedKey = k;
780                 }
781             }
782         }
783 
784         // Attempt to add an instance to the most loaded pool
785         if (mostLoaded !is null) {
786             register(loadedKey);
787             try {
788                 PooledObject!(T) p = create(loadedKey);
789                 if (p !is null) {
790                     addIdleObject(loadedKey, p);
791                 }
792             } catch (Exception e) {
793                 swallowException(e);
794             } finally {
795                 deregister(loadedKey);
796             }
797         }
798     }
799 
800     /**
801      * Checks to see if there are any threads currently waiting to borrow
802      * objects but are blocked waiting for more objects to become available.
803      *
804      * @return {@code true} if there is at least one thread waiting otherwise
805      *         {@code false}
806      */
807     private bool hasBorrowWaiters() {
808         foreach (ObjectDeque!(T) deque; poolMap.byValue) {
809             if (deque !is null) {
810                 LinkedBlockingDeque!(PooledObject!(T)) pool =
811                         deque.getIdleObjects();
812                 if(pool.hasTakeWaiters()) {
813                     return true;
814                 }
815             }
816         }
817         return false;
818     }
819 
820 
821     /**
822      * {@inheritDoc}
823      * <p>
824      * Successive activations of this method examine objects in keyed sub-pools
825      * in sequence, cycling through the keys and examining objects in
826      * oldest-to-youngest order within the keyed sub-pools.
827      */
828     override
829     void evict(){
830         assertOpen();
831 
832         if (getNumIdle() == 0) {
833             return;
834         }
835 
836         PooledObject!(T) underTest = null;
837         EvictionPolicy!(T) evictionPolicy = getEvictionPolicy();
838 
839         synchronized (evictionLock) {
840             EvictionConfig evictionConfig = new EvictionConfig(
841                     getMinEvictableIdleTimeMillis(),
842                     getSoftMinEvictableIdleTimeMillis(),
843                     getMinIdlePerKey());
844 
845             bool testWhileIdle = getTestWhileIdle();
846 
847             for (int i = 0, m = getNumTests(); i < m; i++) {
848                 if(evictionIterator is null || !evictionIterator.hasNext()) {
849                     if (evictionKeyIterator is null ||
850                             !evictionKeyIterator.hasNext()) {
851                         List!(K) keyCopy = new ArrayList!K();
852                         Lock readLock = keyLock.readLock();
853                         readLock.lock();
854                         try {
855                             keyCopy.addAll(poolKeyList);
856                         } finally {
857                             readLock.unlock();
858                         }
859                         evictionKeyIterator = keyCopy.iterator();
860                     }
861                     while (evictionKeyIterator.hasNext()) {
862                         evictionKey = evictionKeyIterator.next();
863                         ObjectDeque!(T) objectDeque = poolMap.get(evictionKey);
864                         if (objectDeque is null) {
865                             continue;
866                         }
867 
868                         Deque!(PooledObject!(T)) idleObjects = objectDeque.getIdleObjects();
869                         evictionIterator = new EvictionIterator(idleObjects, getLifo());
870                         if (evictionIterator.hasNext()) {
871                             break;
872                         }
873                         evictionIterator = null;
874                     }
875                 }
876                 if (evictionIterator is null) {
877                     // Pools exhausted
878                     return;
879                 }
880                 Deque!(PooledObject!(T)) idleObjects;
881                 try {
882                     underTest = evictionIterator.next();
883                     idleObjects = evictionIterator.getIdleObjects();
884                 } catch (NoSuchElementException nsee) {
885                     // Object was borrowed in another thread
886                     // Don't count this as an eviction test so reduce i;
887                     i--;
888                     evictionIterator = null;
889                     continue;
890                 }
891 
892                 if (!underTest.startEvictionTest()) {
893                     // Object was borrowed in another thread
894                     // Don't count this as an eviction test so reduce i;
895                     i--;
896                     continue;
897                 }
898 
899                 // User provided eviction policy could throw all sorts of
900                 // crazy exceptions. Protect against such an exception
901                 // killing the eviction thread.
902                 bool evict;
903                 try {
904                     evict = evictionPolicy.evict(evictionConfig, underTest,
905                             poolMap.get(evictionKey).getIdleObjects().size());
906                 } catch (Throwable t) {
907                     // Slightly convoluted as SwallowedExceptionListener
908                     // uses Exception rather than Throwable
909                     PoolUtils.checkRethrow(t);
910                     swallowException(new Exception(t));
911                     // Don't evict on error conditions
912                     evict = false;
913                 }
914 
915                 if (evict) {
916                     destroy(evictionKey, underTest, true);
917                     destroyedByEvictorCount.incrementAndGet();
918                 } else {
919                     if (testWhileIdle) {
920                         bool active = false;
921                         try {
922                             factory.activateObject(evictionKey, underTest);
923                             active = true;
924                         } catch (Exception e) {
925                             destroy(evictionKey, underTest, true);
926                             destroyedByEvictorCount.incrementAndGet();
927                         }
928                         if (active) {
929                             if (!factory.validateObject(evictionKey, underTest)) {
930                                 destroy(evictionKey, underTest, true);
931                                 destroyedByEvictorCount.incrementAndGet();
932                             } else {
933                                 try {
934                                     factory.passivateObject(evictionKey, underTest);
935                                 } catch (Exception e) {
936                                     destroy(evictionKey, underTest, true);
937                                     destroyedByEvictorCount.incrementAndGet();
938                                 }
939                             }
940                         }
941                     }
942                     if (!underTest.endEvictionTest(idleObjects)) {
943                         // TODO - May need to add code here once additional
944                         // states are used
945                     }
946                 }
947             }
948         }
949     }
950 
951     /**
952      * Create a new pooled object.
953      *
954      * @param key Key associated with new pooled object
955      *
956      * @return The new, wrapped pooled object
957      *
958      * @throws Exception If the objection creation fails
959      */
960     private PooledObject!(T) create(K key){
961         int maxTotalPerKeySave = getMaxTotalPerKey(); // Per key
962         if (maxTotalPerKeySave < 0) {
963             maxTotalPerKeySave = Integer.MAX_VALUE;
964         }
965         int maxTotal = getMaxTotal();   // All keys
966 
967         ObjectDeque!(T) objectDeque = poolMap.get(key);
968 
969         // Check against the overall limit
970         bool loop = true;
971 
972         while (loop) {
973             int newNumTotal = numTotal.incrementAndGet();
974             if (maxTotal > -1 && newNumTotal > maxTotal) {
975                 numTotal.decrementAndGet();
976                 if (getNumIdle() == 0) {
977                     return null;
978                 }
979                 clearOldest();
980             } else {
981                 loop = false;
982             }
983         }
984 
985         // Flag that indicates if create should:
986         // - TRUE:  call the factory to create an object
987         // - FALSE: return null
988         // - null:  loop and re-test the condition that determines whether to
989         //          call the factory
990         Boolean create = null;
991         while (create is null) {
992             synchronized (objectDeque.makeObjectCountLock) {
993                 long newCreateCount = objectDeque.getCreateCount().incrementAndGet();
994                 // Check against the per key limit
995                 if (newCreateCount > maxTotalPerKeySave) {
996                     // The key is currently at capacity or in the process of
997                     // making enough new objects to take it to capacity.
998                     objectDeque.getCreateCount().decrementAndGet();
999                     if (objectDeque.makeObjectCount == 0) {
1000                         // There are no makeObject() calls in progress for this
1001                         // key so the key is at capacity. Do not attempt to
1002                         // create a new object. Return and wait for an object to
1003                         // be returned.
1004                         create = Boolean.FALSE;
1005                     } else {
1006                         // There are makeObject() calls in progress that might
1007                         // bring the pool to capacity. Those calls might also
1008                         // fail so wait until they complete and then re-test if
1009                         // the pool is at capacity or not.
1010                         objectDeque.makeObjectCountLock.wait();
1011                     }
1012                 } else {
1013                     // The pool is not at capacity. Create a new object.
1014                     objectDeque.makeObjectCount++;
1015                     create = Boolean.TRUE;
1016                 }
1017             }
1018         }
1019 
1020         if (!create.booleanValue()) {
1021             numTotal.decrementAndGet();
1022             return null;
1023         }
1024 
1025         PooledObject!(T) p = null;
1026         try {
1027             p = factory.makeObject(key);
1028         } catch (Exception e) {
1029             numTotal.decrementAndGet();
1030             objectDeque.getCreateCount().decrementAndGet();
1031             throw e;
1032         } finally {
1033             synchronized (objectDeque.makeObjectCountLock) {
1034                 objectDeque.makeObjectCount--;
1035                 objectDeque.makeObjectCountLock.notifyAll();
1036             }
1037         }
1038 
1039         createdCount.incrementAndGet();
1040         objectDeque.getAllObjects().put(new IdentityWrapper!T(p.getObject()), p);
1041         return p;
1042     }
1043 
1044     /**
1045      * Destroy the wrapped, pooled object.
1046      *
1047      * @param key The key associated with the object to destroy.
1048      * @param toDestroy The wrapped object to be destroyed
1049      * @param always Should the object be destroyed even if it is not currently
1050      *               in the set of idle objects for the given key
1051      * @return {@code true} if the object was destroyed, otherwise {@code false}
1052      * @throws Exception If the object destruction failed
1053      */
1054     private bool destroy(K key, PooledObject!(T) toDestroy, bool always) {
1055 
1056         ObjectDeque!(T) objectDeque = register(key);
1057 
1058         try {
1059             bool isIdle = objectDeque.getIdleObjects().remove(toDestroy);
1060 
1061             if (isIdle || always) {
1062                 objectDeque.getAllObjects().remove(new IdentityWrapper!T(toDestroy.getObject()));
1063                 toDestroy.invalidate();
1064 
1065                 try {
1066                     factory.destroyObject(key, toDestroy);
1067                 } finally {
1068                     objectDeque.getCreateCount().decrementAndGet();
1069                     destroyedCount.incrementAndGet();
1070                     numTotal.decrementAndGet();
1071                 }
1072                 return true;
1073             }
1074             return false;
1075         } finally {
1076             deregister(key);
1077         }
1078     }
1079 
1080 
1081     /**
1082      * Register the use of a key by an object.
1083      * <p>
1084      * register() and deregister() must always be used as a pair.
1085      *
1086      * @param k The key to register
1087      *
1088      * @return The objects currently associated with the given key. If this
1089      *         method returns without throwing an exception then it will never
1090      *         return null.
1091      */
1092     private ObjectDeque!(T) register(K k) {
1093         Lock lock = keyLock.readLock();
1094         ObjectDeque!(T) objectDeque = null;
1095         try {
1096             lock.lock();
1097             objectDeque = poolMap.get(k);
1098             if (objectDeque is null) {
1099                 // Upgrade to write lock
1100                 lock.unlock();
1101                 lock = keyLock.writeLock();
1102                 lock.lock();
1103                 objectDeque = poolMap.get(k);
1104                 if (objectDeque is null) {
1105                     objectDeque = new ObjectDeque!T(fairness);
1106                     objectDeque.getNumInterested().incrementAndGet();
1107                     // NOTE: Keys must always be added to both poolMap and
1108                     //       poolKeyList at the same time while protected by
1109                     //       keyLock.writeLock()
1110                     poolMap.put(k, objectDeque);
1111                     poolKeyList.add(k);
1112                 } else {
1113                     objectDeque.getNumInterested().incrementAndGet();
1114                 }
1115             } else {
1116                 objectDeque.getNumInterested().incrementAndGet();
1117             }
1118         } finally {
1119             lock.unlock();
1120         }
1121         return objectDeque;
1122     }
1123 
1124     /**
1125      * De-register the use of a key by an object.
1126      * <p>
1127      * register() and deregister() must always be used as a pair.
1128      *
1129      * @param k The key to de-register
1130      */
1131     private void deregister(K k) {
1132         Lock lock = keyLock.readLock();
1133         ObjectDeque!(T) objectDeque;
1134         try {
1135             lock.lock();
1136             objectDeque = poolMap.get(k);
1137             long numInterested = objectDeque.getNumInterested().decrementAndGet();
1138             if (numInterested == 0 && objectDeque.getCreateCount().get() == 0) {
1139                 // Potential to remove key
1140                 // Upgrade to write lock
1141                 lock.unlock();
1142                 lock = keyLock.writeLock();
1143                 lock.lock();
1144                 if (objectDeque.getCreateCount().get() == 0 && objectDeque.getNumInterested().get() == 0) {
1145                     // NOTE: Keys must always be removed from both poolMap and
1146                     //       poolKeyList at the same time while protected by
1147                     //       keyLock.writeLock()
1148                     poolMap.remove(k);
1149                     poolKeyList.remove(k);
1150                 }
1151             }
1152         } finally {
1153             lock.unlock();
1154         }
1155     }
1156 
1157     override
1158     void ensureMinIdle(){
1159         int minIdlePerKeySave = getMinIdlePerKey();
1160         if (minIdlePerKeySave < 1) {
1161             return;
1162         }
1163 
1164         foreach(K k ; poolMap.keySet()) {
1165             ensureMinIdle(k);
1166         }
1167     }
1168 
1169     /**
1170      * Ensure that the configured number of minimum idle objects is available in
1171      * the pool for the given key.
1172      *
1173      * @param key The key to check for idle objects
1174      *
1175      * @throws Exception If a new object is required and cannot be created
1176      */
1177     private void ensureMinIdle(K key){
1178         // Calculate current pool objects
1179         ObjectDeque!(T) objectDeque = poolMap.get(key);
1180 
1181         // objectDeque is null is OK here. It is handled correctly by both
1182         // methods called below.
1183 
1184         // this method isn't synchronized so the
1185         // calculateDeficit is done at the beginning
1186         // as a loop limit and a second time inside the loop
1187         // to stop when another thread already returned the
1188         // needed objects
1189         int deficit = calculateDeficit(objectDeque);
1190 
1191         for (int i = 0; i < deficit && calculateDeficit(objectDeque) > 0; i++) {
1192             addObject(key);
1193             // If objectDeque was null, it won't be any more. Obtain a reference
1194             // to it so the deficit can be correctly calculated. It needs to
1195             // take account of objects created in other threads.
1196             if (objectDeque is null) {
1197                 objectDeque = poolMap.get(key);
1198             }
1199         }
1200     }
1201 
1202     /**
1203      * Create an object using the {@link KeyedPooledObjectFactory#makeObject
1204      * factory}, passivate it, and then place it in the idle object pool.
1205      * <code>addObject</code> is useful for "pre-loading" a pool with idle
1206      * objects.
1207      *
1208      * @param key the key a new instance should be added to
1209      *
1210      * @throws Exception when {@link KeyedPooledObjectFactory#makeObject}
1211      *                   fails.
1212      */
1213     override
1214     void addObject(K key){
1215         assertOpen();
1216         register(key);
1217         try {
1218             PooledObject!(T) p = create(key);
1219             addIdleObject(key, p);
1220         } finally {
1221             deregister(key);
1222         }
1223     }
1224 
1225     /**
1226      * Add an object to the set of idle objects for a given key.
1227      *
1228      * @param key The key to associate with the idle object
1229      * @param p The wrapped object to add.
1230      *
1231      * @throws Exception If the associated factory fails to passivate the object
1232      */
1233     private void addIdleObject(K key, PooledObject!(T) p){
1234 
1235         if (p !is null) {
1236             factory.passivateObject(key, p);
1237             LinkedBlockingDeque!(PooledObject!(T)) idleObjects =
1238                     poolMap.get(key).getIdleObjects();
1239             if (getLifo()) {
1240                 idleObjects.addFirst(p);
1241             } else {
1242                 idleObjects.addLast(p);
1243             }
1244         }
1245     }
1246 
1247     /**
1248      * Registers a key for pool control and ensures that
1249      * {@link #getMinIdlePerKey()} idle instances are created.
1250      *
1251      * @param key - The key to register for pool control.
1252      *
1253      * @throws Exception If the associated factoryexception
1254      */
1255     void preparePool(K key){
1256         int minIdlePerKeySave = getMinIdlePerKey();
1257         if (minIdlePerKeySave < 1) {
1258             return;
1259         }
1260         ensureMinIdle(key);
1261     }
1262 
1263     /**
1264      * Calculate the number of objects to test in a run of the idle object
1265      * evictor.
1266      *
1267      * @return The number of objects to test for validity
1268      */
1269     private int getNumTests() {
1270         int totalIdle = getNumIdle();
1271         int numTests = getNumTestsPerEvictionRun();
1272         implementationMissing(false);
1273         return 0;        
1274         // if (numTests >= 0) {
1275         //     return Math.min(numTests, totalIdle);
1276         // }
1277         // return cast(int)(Math.ceil(totalIdle/Math.abs((double)numTests)));
1278     }
1279 
1280     /**
1281      * Calculate the number of objects that need to be created to attempt to
1282      * maintain the minimum number of idle objects while not exceeded the limits
1283      * on the maximum number of objects either per key or totally.
1284      *
1285      * @param objectDeque   The set of objects to check
1286      *
1287      * @return The number of new objects to create
1288      */
1289     private int calculateDeficit(ObjectDeque!(T) objectDeque) {
1290 
1291         if (objectDeque is null) {
1292             return getMinIdlePerKey();
1293         }
1294 
1295         // Used more than once so keep a local copy so the value is consistent
1296         int maxTotal = getMaxTotal();
1297         int maxTotalPerKeySave = getMaxTotalPerKey();
1298 
1299         int objectDefecit = 0;
1300 
1301         // Calculate no of objects needed to be created, in order to have
1302         // the number of pooled objects < maxTotalPerKey();
1303         objectDefecit = getMinIdlePerKey() - objectDeque.getIdleObjects().size();
1304         if (maxTotalPerKeySave > 0) {
1305             int growLimit = Math.max(0,
1306                     maxTotalPerKeySave - objectDeque.getIdleObjects().size());
1307             objectDefecit = Math.min(objectDefecit, growLimit);
1308         }
1309 
1310         // Take the maxTotal limit into account
1311         if (maxTotal > 0) {
1312             int growLimit = Math.max(0, maxTotal - getNumActive() - getNumIdle());
1313             objectDefecit = Math.min(objectDefecit, growLimit);
1314         }
1315 
1316         return objectDefecit;
1317     }
1318 
1319 
1320     //--- JMX support ----------------------------------------------------------
1321 
1322     override
1323     Map!(string,Integer) getNumActivePerKey() {
1324         HashMap!(string,Integer) result = new HashMap!(string,Integer)();
1325         implementationMissing(false);
1326 
1327         // Iterator!(Entry!(K,ObjectDeque!(T))) iter = poolMap.entrySet().iterator();
1328         // while (iter.hasNext()) {
1329         //     Entry!(K,ObjectDeque!(T)) entry = iter.next();
1330         //     if (entry !is null) {
1331         //         K key = entry.getKey();
1332         //         ObjectDeque!(T) objectDequeue = entry.getValue();
1333         //         if (key !is null && objectDequeue !is null) {
1334         //             result.put(key.toString(), Integer.valueOf(
1335         //                     objectDequeue.getAllObjects().size() -
1336         //                     objectDequeue.getIdleObjects().size()));
1337         //         }
1338         //     }
1339         // }
1340         return result;
1341     }
1342 
1343     /**
1344      * Return an estimate of the number of threads currently blocked waiting for
1345      * an object from the pool. This is intended for monitoring only, not for
1346      * synchronization control.
1347      *
1348      * @return The estimate of the number of threads currently blocked waiting
1349      *         for an object from the pool
1350      */
1351     override
1352     int getNumWaiters() {
1353         int result = 0;
1354 
1355         if (getBlockWhenExhausted()) {
1356             Iterator!(ObjectDeque!(T)) iter = poolMap.values().iterator();
1357 
1358             while (iter.hasNext()) {
1359                 // Assume no overflow
1360                 result += iter.next().getIdleObjects().getTakeQueueLength();
1361             }
1362         }
1363 
1364         return result;
1365     }
1366 
1367     /**
1368      * Return an estimate of the number of threads currently blocked waiting for
1369      * an object from the pool for each key. This is intended for
1370      * monitoring only, not for synchronization control.
1371      *
1372      * @return The estimate of the number of threads currently blocked waiting
1373      *         for an object from the pool for each key
1374      */
1375     override
1376     Map!(string,Integer) getNumWaitersByKey() {
1377         Map!(string,Integer) result = new HashMap!(string,Integer)();
1378 
1379         foreach (K k, ObjectDeque!(T) deque; poolMap) {
1380             if (deque !is null) {
1381                 if (getBlockWhenExhausted()) {
1382                     result.put(k.toString(), Integer.valueOf(
1383                             deque.getIdleObjects().getTakeQueueLength()));
1384                 } else {
1385                     result.put(k.toString(), Integer.valueOf(0));
1386                 }
1387             }
1388         }
1389         return result;
1390     }
1391 
1392     /**
1393      * Provides information on all the objects in the pool, both idle (waiting
1394      * to be borrowed) and active (currently borrowed).
1395      * <p>
1396      * Note: This is named listAllObjects so it is presented as an operation via
1397      * JMX. That means it won't be invoked unless the explicitly requested
1398      * whereas all attributes will be automatically requested when viewing the
1399      * attributes for an object in a tool like JConsole.
1400      *
1401      * @return Information grouped by key on all the objects in the pool
1402      */
1403     override
1404     Map!(string,List!(DefaultPooledObjectInfo)) listAllObjects() {
1405         Map!(string, List!(DefaultPooledObjectInfo)) result =
1406                 new HashMap!(string, List!(DefaultPooledObjectInfo))();
1407 
1408         foreach (K k, ObjectDeque!(T) deque; poolMap) {
1409             if (deque !is null) {
1410                 List!(DefaultPooledObjectInfo) list =
1411                         new ArrayList!(DefaultPooledObjectInfo)();
1412                 result.put(k.toString(), list);
1413                 foreach (PooledObject!(T) p ; deque.getAllObjects().values()) {
1414                     list.add(new DefaultPooledObjectInfo(p));
1415                 }
1416             }
1417         }
1418         return result;
1419     }
1420 
1421 
1422     //--- inner classes ----------------------------------------------
1423 
1424     /**
1425      * Maintains information on the per key queue for a given key.
1426      *
1427      * @param <S> type of objects in the pool
1428      */
1429     private class ObjectDeque(S) {
1430 
1431         private LinkedBlockingDeque!(PooledObject!(S)) idleObjects;
1432 
1433         /*
1434          * Number of instances created - number destroyed.
1435          * Invariant: createCount <= maxTotalPerKey
1436          */
1437         private shared int createCount = 0;
1438 
1439         private long makeObjectCount = 0;
1440         private Object makeObjectCountLock; // = new Object();
1441 
1442         /*
1443          * The map is keyed on pooled instances, wrapped to ensure that
1444          * they work properly as keys.
1445          */
1446         private Map!(IdentityWrapper!(S), PooledObject!(S)) allObjects;
1447 
1448         /*
1449          * Number of threads with registered interest in this key.
1450          * register(K) increments this counter and deRegister(K) decrements it.
1451          * Invariant: empty keyed pool will not be dropped unless numInterested
1452          *            is 0.
1453          */
1454         private shared long numInterested = 0;
1455 
1456         /**
1457          * Create a new ObjecDeque with the given fairness policy.
1458          * @param fairness true means client threads waiting to borrow / return instances
1459          * will be served as if waiting in a FIFO queue.
1460          */
1461         this(bool fairness) {
1462             makeObjectCountLock = new Object();
1463             // allObjects = new ConcurrentHashMap!(IdentityWrapper!(S), PooledObject!(S))();
1464             allObjects = new HashMap!(IdentityWrapper!(S), PooledObject!(S))();
1465             idleObjects = new LinkedBlockingDeque!(PooledObject!(S))(fairness);
1466             // poolMap = new ConcurrentHashMap<>();
1467             poolMap = new HashMap!(K,ObjectDeque!(T))();
1468             poolKeyList = new ArrayList!(K)();
1469             keyLock = new ReadWriteMutex();
1470         }
1471 
1472         /**
1473          * Obtain the idle objects for the current key.
1474          *
1475          * @return The idle objects
1476          */
1477         LinkedBlockingDeque!(PooledObject!(S)) getIdleObjects() {
1478             return idleObjects;
1479         }
1480 
1481         /**
1482          * Obtain the count of the number of objects created for the current
1483          * key.
1484          *
1485          * @return The number of objects created for this key
1486          */
1487         AtomicInteger getCreateCount() {
1488             return createCount;
1489         }
1490 
1491         /**
1492          * Obtain the number of threads with an interest registered in this key.
1493          *
1494          * @return The number of threads with a registered interest in this key
1495          */
1496         AtomicLong getNumInterested() {
1497             return numInterested;
1498         }
1499 
1500         /**
1501          * Obtain all the objects for the current key.
1502          *
1503          * @return All the objects
1504          */
1505         Map!(IdentityWrapper!(S), PooledObject!(S)) getAllObjects() {
1506             return allObjects;
1507         }
1508 
1509         override
1510         string toString() {
1511             StringBuilder builder = new StringBuilder();
1512             builder.append("ObjectDeque [idleObjects=");
1513             builder.append(idleObjects);
1514             builder.append(", createCount=");
1515             builder.append(createCount);
1516             builder.append(", allObjects=");
1517             builder.append(allObjects);
1518             builder.append(", numInterested=");
1519             builder.append(numInterested);
1520             builder.append("]");
1521             return builder.toString();
1522         }
1523 
1524     }
1525 
1526     //--- configuration attributes ---------------------------------------------
1527     private shared int maxIdlePerKey =
1528             GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY;
1529     private shared int minIdlePerKey =
1530             GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY;
1531     private shared int maxTotalPerKey =
1532             GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
1533     private KeyedPooledObjectFactory!(K,T) factory;
1534     private bool fairness;
1535 
1536 
1537     //--- internal attributes --------------------------------------------------
1538 
1539     /*
1540      * My hash of sub-pools (ObjectQueue). The list of keys <b>must</b> be kept
1541      * in step with {@link #poolKeyList} using {@link #keyLock} to ensure any
1542      * changes to the list of current keys is made in a thread-safe manner.
1543      */
1544     private Map!(K,ObjectDeque!(T)) poolMap;
1545             // new ConcurrentHashMap<>(); // @GuardedBy("keyLock") for write access (and some read access)
1546     /*
1547      * List of pool keys - used to control eviction order. The list of keys
1548      * <b>must</b> be kept in step with {@link #poolMap} using {@link #keyLock}
1549      * to ensure any changes to the list of current keys is made in a
1550      * thread-safe manner.
1551      */
1552     private List!(K) poolKeyList; // = new ArrayList<>(); // @GuardedBy("keyLock")
1553     // private ReadWriteLock keyLock = new ReentrantReadWriteLock(true);
1554     private ReadWriteMutex keyLock; // = new ReentrantReadWriteLock(true);
1555     /*
1556      * The combined count of the currently active objects for all keys and those
1557      * in the process of being created. Under load, it may exceed
1558      * {@link #maxTotal} but there will never be more than {@link #maxTotal}
1559      * created at any one time.
1560      */
1561     private shared int numTotal = 0; // new AtomicInteger(0);
1562     private Iterator!(K) evictionKeyIterator = null; // @GuardedBy("evictionLock")
1563     private K evictionKey = null; // @GuardedBy("evictionLock")
1564 
1565     // JMX specific attributes
1566     private enum string ONAME_BASE =
1567             "hunt.pool:type=GenericKeyedObjectPool,name=";
1568 
1569     override
1570     protected void toStringAppendFields(StringBuilder builder) {
1571         super.toStringAppendFields(builder);
1572         builder.append(", maxIdlePerKey=");
1573         builder.append(maxIdlePerKey);
1574         builder.append(", minIdlePerKey=");
1575         builder.append(minIdlePerKey);
1576         builder.append(", maxTotalPerKey=");
1577         builder.append(maxTotalPerKey);
1578         builder.append(", factory=");
1579         builder.append(factory);
1580         builder.append(", fairness=");
1581         builder.append(fairness);
1582         builder.append(", poolMap=");
1583         builder.append(poolMap);
1584         builder.append(", poolKeyList=");
1585         builder.append(poolKeyList);
1586         builder.append(", keyLock=");
1587         builder.append(keyLock);
1588         builder.append(", numTotal=");
1589         builder.append(numTotal);
1590         builder.append(", evictionKeyIterator=");
1591         builder.append(evictionKeyIterator);
1592         builder.append(", evictionKey=");
1593         builder.append(evictionKey);
1594     }
1595 }