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