    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}