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