Package org.datanucleus.store.rdbms.datasource.dbcp.pool.impl

Source Code of org.datanucleus.store.rdbms.datasource.dbcp.pool.impl.GenericKeyedObjectPool$ObjectQueue

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.datanucleus.store.rdbms.datasource.dbcp.pool.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeMap;
import java.util.TimerTask;
import java.util.Map.Entry;

import org.datanucleus.store.rdbms.datasource.dbcp.pool.BaseKeyedObjectPool;
import org.datanucleus.store.rdbms.datasource.dbcp.pool.KeyedObjectPool;
import org.datanucleus.store.rdbms.datasource.dbcp.pool.KeyedPoolableObjectFactory;
import org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolUtils;

/**
* A configurable <code>KeyedObjectPool</code> implementation.
* <p>
* When coupled with the appropriate {@link KeyedPoolableObjectFactory},
* <code>GenericKeyedObjectPool</code> provides robust pooling functionality for
* keyed objects. A <code>GenericKeyedObjectPool</code> can be viewed as a map
* of pools, keyed on the (unique) key values provided to the
* {@link #preparePool preparePool}, {@link #addObject addObject} or
* {@link #borrowObject borrowObject} methods. Each time a new key value is
* provided to one of these methods, a new pool is created under the given key
* to be managed by the containing <code>GenericKeyedObjectPool.</code>
* </p>
* <p>A <code>GenericKeyedObjectPool</code> provides a number of configurable
* parameters:</p>
* <ul>
<li>
*    {@link #setMaxActive maxActive} controls the maximum number of objects
*    (per key) that can allocated by the pool (checked out to client threads,
*    or idle in the pool) at one time.  When non-positive, there is no limit
*    to the number of objects per key. When {@link #setMaxActive maxActive} is
*    reached, the keyed pool is said to be exhausted.  The default setting for
*    this parameter is 8.
</li>
<li>
*    {@link #setMaxTotal maxTotal} sets a global limit on the number of objects
*    that can be in circulation (active or idle) within the combined set of
*    pools.  When non-positive, there is no limit to the total number of
*    objects in circulation. When {@link #setMaxTotal maxTotal} is exceeded,
*    all keyed pools are exhausted. When <code>maxTotal</code> is set to a
*    positive value and {@link #borrowObject borrowObject} is invoked
*    when at the limit with no idle instances available, an attempt is made to
*    create room by clearing the oldest 15% of the elements from the keyed
*    pools. The default setting for this parameter is -1 (no limit).
</li>
<li>
*    {@link #setMaxIdle maxIdle} controls the maximum number of objects that can
*    sit idle in the pool (per key) at any time.  When negative, there
*    is no limit to the number of objects that may be idle per key. The
*    default setting for this parameter is 8.
</li>
<li>
*    {@link #setWhenExhaustedAction whenExhaustedAction} specifies the
*    behavior of the {@link #borrowObject borrowObject} method when a keyed
*    pool is exhausted:
*    <ul>
*    <li>
*      When {@link #setWhenExhaustedAction whenExhaustedAction} is
*      {@link #WHEN_EXHAUSTED_FAIL}, {@link #borrowObject borrowObject} will throw
*      a {@link NoSuchElementException}
*    </li>
*    <li>
*      When {@link #setWhenExhaustedAction whenExhaustedAction} is
*      {@link #WHEN_EXHAUSTED_GROW}, {@link #borrowObject borrowObject} will create a new
*      object and return it (essentially making {@link #setMaxActive maxActive}
*      meaningless.)
*    </li>
*    <li>
*      When {@link #setWhenExhaustedAction whenExhaustedAction}
*      is {@link #WHEN_EXHAUSTED_BLOCK}, {@link #borrowObject borrowObject} will block
*      (invoke {@link Object#wait() wait} until a new or idle object is available.
*      If a positive {@link #setMaxWait maxWait}
*      value is supplied, the {@link #borrowObject borrowObject} will block for at
*      most that many milliseconds, after which a {@link NoSuchElementException}
*      will be thrown.  If {@link #setMaxWait maxWait} is non-positive,
*      the {@link #borrowObject borrowObject} method will block indefinitely.
*    </li>
*    </ul>
*    The default <code>whenExhaustedAction</code> setting is
*    {@link #WHEN_EXHAUSTED_BLOCK}.
</li>
<li>
*    When {@link #setTestOnBorrow testOnBorrow} is set, the pool will
*    attempt to validate each object before it is returned from the
*    {@link #borrowObject borrowObject} method. (Using the provided factory's
*    {@link KeyedPoolableObjectFactory#validateObject validateObject} method.)
*    Objects that fail to validate will be dropped from the pool, and a
*    different object will be borrowed. The default setting for this parameter
*    is <code>false.</code>
</li>
<li>
*    When {@link #setTestOnReturn testOnReturn} is set, the pool will
*    attempt to validate each object before it is returned to the pool in the
*    {@link #returnObject returnObject} method. (Using the provided factory's
*    {@link KeyedPoolableObjectFactory#validateObject validateObject}
*    method.)  Objects that fail to validate will be dropped from the pool.
*    The default setting for this parameter is <code>false.</code>
</li>
* </ul>
* <p>
* Optionally, one may configure the pool to examine and possibly evict objects
* as they sit idle in the pool and to ensure that a minimum number of idle
* objects is maintained for each key. This is performed by an
* "idle object eviction" thread, which runs asynchronously. Caution should be
* used when configuring this optional feature. Eviction runs contend with client
* threads for access to objects in the pool, so if they run too frequently
* performance issues may result.  The idle object eviction thread may be
* configured using the following attributes:
* <ul>
<li>
*   {@link #setTimeBetweenEvictionRunsMillis timeBetweenEvictionRunsMillis}
*   indicates how long the eviction thread should sleep before "runs" of examining
*   idle objects.  When non-positive, no eviction thread will be launched. The
*   default setting for this parameter is -1 (i.e., by default, idle object
*   eviction is disabled).
</li>
<li>
*   {@link #setMinEvictableIdleTimeMillis minEvictableIdleTimeMillis}
*   specifies the minimum amount of time that an object may sit idle in the
*   pool before it is eligible for eviction due to idle time.  When
*   non-positive, no object will be dropped from the pool due to idle time
*   alone.  This setting has no effect unless
*   <code>timeBetweenEvictionRunsMillis > 0.</code>  The default setting
*   for this parameter is 30 minutes.
</li>
<li>
*   {@link #setTestWhileIdle testWhileIdle} indicates whether or not idle
*   objects should be validated using the factory's
*   {@link KeyedPoolableObjectFactory#validateObject validateObject} method
*   during idle object eviction runs.  Objects that fail to validate will be
*   dropped from the pool. This setting has no effect unless
*   <code>timeBetweenEvictionRunsMillis > 0.</code>  The default setting
*   for this parameter is <code>false.</code>
</li>
<li>
*    {@link #setMinIdle minIdle} sets a target value for the minimum number of
*    idle objects (per key) that should always be available. If this parameter
*    is set to a positive number and
*    <code>timeBetweenEvictionRunsMillis > 0,</code> each time the idle object
*    eviction thread runs, it will try to create enough idle instances so that
*    there will be <code>minIdle</code> idle instances available under each
*    key. This parameter is also used by {@link #preparePool preparePool}
*    if <code>true</code> is provided as that method's
*    <code>populateImmediately</code> parameter. The default setting for this
*    parameter is 0.
</li>
* </ul>
* <p>
* The pools can be configured to behave as LIFO queues with respect to idle
* objects - always returning the most recently used object from the pool,
* or as FIFO queues, where borrowObject always returns the oldest object
* in the idle object pool.
* <ul>
<li>
*   {@link #setLifo <i>Lifo</i>}
*   determines whether or not the pools return idle objects in
*   last-in-first-out order. The default setting for this parameter is
*   <code>true.</code>
</li>
* </ul>
* <p>
* GenericKeyedObjectPool is not usable without a {@link KeyedPoolableObjectFactory}.  A
* non-<code>null</code> factory must be provided either as a constructor argument
* or via a call to {@link #setFactory setFactory} before the pool is used.
* </p>
* <p>
* Implementation note: To prevent possible deadlocks, care has been taken to
* ensure that no call to a factory method will occur within a synchronization
* block. See POOL-125 and DBCP-44 for more information.
* </p>
* @see GenericObjectPool
* @author Rodney Waldhoff
* @author Dirk Verbeeck
* @author Sandy McArthur
* @version $Revision: 990683 $ $Date: 2010-08-29 21:40:36 -0400 (Sun, 29 Aug 2010) $
* @since Pool 1.0
*/
public class GenericKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {

    //--- public constants -------------------------------------------

    /**
     * A "when exhausted action" type indicating that when the pool is
     * exhausted (i.e., the maximum number of active objects has
     * been reached), the {@link #borrowObject}
     * method should fail, throwing a {@link NoSuchElementException}.
     * @see #WHEN_EXHAUSTED_BLOCK
     * @see #WHEN_EXHAUSTED_GROW
     * @see #setWhenExhaustedAction
     */
    public static final byte WHEN_EXHAUSTED_FAIL   = 0;

    /**
     * A "when exhausted action" type indicating that when the pool
     * is exhausted (i.e., the maximum number
     * of active objects has been reached), the {@link #borrowObject}
     * method should block until a new object is available, or the
     * {@link #getMaxWait maximum wait time} has been reached.
     * @see #WHEN_EXHAUSTED_FAIL
     * @see #WHEN_EXHAUSTED_GROW
     * @see #setMaxWait
     * @see #getMaxWait
     * @see #setWhenExhaustedAction
     */
    public static final byte WHEN_EXHAUSTED_BLOCK  = 1;

    /**
     * A "when exhausted action" type indicating that when the pool is
     * exhausted (i.e., the maximum number
     * of active objects has been reached), the {@link #borrowObject}
     * method should simply create a new object anyway.
     * @see #WHEN_EXHAUSTED_FAIL
     * @see #WHEN_EXHAUSTED_GROW
     * @see #setWhenExhaustedAction
     */
    public static final byte WHEN_EXHAUSTED_GROW   = 2;

    /**
     * The default cap on the number of idle instances (per key) in the pool.
     * @see #getMaxIdle
     * @see #setMaxIdle
     */
    public static final int DEFAULT_MAX_IDLE  = 8;

    /**
     * The default cap on the total number of active instances (per key)
     * from the pool.
     * @see #getMaxActive
     * @see #setMaxActive
     */
    public static final int DEFAULT_MAX_ACTIVE  = 8;

    /**
     * The default cap on the the overall maximum number of objects that can
     * exist at one time.
     * @see #getMaxTotal
     * @see #setMaxTotal
     */
    public static final int DEFAULT_MAX_TOTAL  = -1;

    /**
     * The default "when exhausted action" for the pool.
     * @see #WHEN_EXHAUSTED_BLOCK
     * @see #WHEN_EXHAUSTED_FAIL
     * @see #WHEN_EXHAUSTED_GROW
     * @see #setWhenExhaustedAction
     */
    public static final byte DEFAULT_WHEN_EXHAUSTED_ACTION = WHEN_EXHAUSTED_BLOCK;

    /**
     * The default maximum amount of time (in milliseconds) the
     * {@link #borrowObject} method should block before throwing
     * an exception when the pool is exhausted and the
     * {@link #getWhenExhaustedAction "when exhausted" action} is
     * {@link #WHEN_EXHAUSTED_BLOCK}.
     * @see #getMaxWait
     * @see #setMaxWait
     */
    public static final long DEFAULT_MAX_WAIT = -1L;

    /**
     * The default "test on borrow" value.
     * @see #getTestOnBorrow
     * @see #setTestOnBorrow
     */
    public static final boolean DEFAULT_TEST_ON_BORROW = false;

    /**
     * The default "test on return" value.
     * @see #getTestOnReturn
     * @see #setTestOnReturn
     */
    public static final boolean DEFAULT_TEST_ON_RETURN = false;

    /**
     * The default "test while idle" value.
     * @see #getTestWhileIdle
     * @see #setTestWhileIdle
     * @see #getTimeBetweenEvictionRunsMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public static final boolean DEFAULT_TEST_WHILE_IDLE = false;

    /**
     * The default "time between eviction runs" value.
     * @see #getTimeBetweenEvictionRunsMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L;

    /**
     * The default number of objects to examine per run in the
     * idle object evictor.
     * @see #getNumTestsPerEvictionRun
     * @see #setNumTestsPerEvictionRun
     * @see #getTimeBetweenEvictionRunsMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3;

    /**
     * The default value for {@link #getMinEvictableIdleTimeMillis}.
     * @see #getMinEvictableIdleTimeMillis
     * @see #setMinEvictableIdleTimeMillis
     */
    public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L;

    /**
     * The default minimum level of idle objects in the pool.
     * @since Pool 1.3
     * @see #setMinIdle
     * @see #getMinIdle
     */
    public static final int DEFAULT_MIN_IDLE = 0;

    /**
     * The default LIFO status. True means that borrowObject returns the
     * most recently used ("last in") idle object in a pool (if there are
     * idle instances available).  False means that pools behave as FIFO
     * queues - objects are taken from idle object pools in the order that
     * they are returned.
     * @see #setLifo
     */
    public static final boolean DEFAULT_LIFO = true;

    //--- constructors -----------------------------------------------

    /**
     * Create a new <code>GenericKeyedObjectPool</code> with no factory.
     *
     * @see #GenericKeyedObjectPool(KeyedPoolableObjectFactory)
     * @see #setFactory(KeyedPoolableObjectFactory)
     */
    public GenericKeyedObjectPool() {
        this(null, DEFAULT_MAX_ACTIVE, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE,
                DEFAULT_TEST_ON_BORROW, DEFAULT_TEST_ON_RETURN, DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                DEFAULT_NUM_TESTS_PER_EVICTION_RUN, DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy
     * objects if not <code>null</code>
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory) {
        this(factory, DEFAULT_MAX_ACTIVE, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE,
                DEFAULT_TEST_ON_BORROW, DEFAULT_TEST_ON_RETURN, DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                DEFAULT_NUM_TESTS_PER_EVICTION_RUN, DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param config a non-<code>null</code> {@link GenericKeyedObjectPool.Config} describing the configuration
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, GenericKeyedObjectPool.Config config) {
        this(factory, config.maxActive, config.whenExhaustedAction, config.maxWait, config.maxIdle, config.maxTotal,
                config.minIdle, config.testOnBorrow, config.testOnReturn, config.timeBetweenEvictionRunsMillis,
                config.numTestsPerEvictionRun, config.minEvictableIdleTimeMillis, config.testWhileIdle, config.lifo);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive) {
        this(factory,maxActive, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE,
                DEFAULT_TEST_ON_BORROW, DEFAULT_TEST_ON_RETURN, DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                DEFAULT_NUM_TESTS_PER_EVICTION_RUN, DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     *  <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait) {
        this(factory, maxActive, whenExhaustedAction, maxWait, DEFAULT_MAX_IDLE, DEFAULT_TEST_ON_BORROW,
                DEFAULT_TEST_ON_RETURN, DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
                DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject}
     * method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
     * method (see {@link #setTestOnReturn})
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, boolean testOnBorrow, boolean testOnReturn) {
        this(factory, maxActive, whenExhaustedAction, maxWait, DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,
                DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
                DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed from me at one time
     * (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle) {
        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, DEFAULT_TEST_ON_BORROW, DEFAULT_TEST_ON_RETURN,
                DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
                DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed from me at one time
     * (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject}
     * method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
     * method (see {@link #setTestOnReturn})
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, testOnBorrow, testOnReturn,
                DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
                DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_TEST_WHILE_IDLE);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed from me at one time
     * (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted
     * (see {@link #setWhenExhaustedAction})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject}
     * method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
     * method (see {@link #setTestOnReturn})
     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle
     * objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction
     * thread (if any) (see {@link #setNumTestsPerEvictionRun})
     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before
     * it is eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
     * (see {@link #setTestWhileIdle})
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
            int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,
                testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
                minEvictableIdleTimeMillis, testWhileIdle);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed from me at one time
     * (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param maxTotal the maximum number of objects that can exists at one time (see {@link #setMaxTotal})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject}
     * method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
     * method (see {@link #setTestOnReturn})
     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle
     * objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction
     * thread (if any) (see {@link #setNumTestsPerEvictionRun})
     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool
     * before it is eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
     * (see {@link #setTestWhileIdle})
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle, int maxTotal, boolean testOnBorrow, boolean testOnReturn,
            long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
            boolean testWhileIdle) {
        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal,
                GenericKeyedObjectPool.DEFAULT_MIN_IDLE, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis,
                numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param maxTotal the maximum number of objects that can exists at one time (see {@link #setMaxTotal})
     * @param minIdle the minimum number of idle objects to have in the pool at any one time (see {@link #setMinIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject}
     * method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
     * method (see {@link #setTestOnReturn})
     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle
     * objects
     * for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction
     * thread (if any) (see {@link #setNumTestsPerEvictionRun})
     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before
     * it is eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
     * (see {@link #setTestWhileIdle})
     * @since Pool 1.3
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn,
            long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
            boolean testWhileIdle) {
        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal, minIdle, testOnBorrow, testOnReturn,
                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle,
                DEFAULT_LIFO);
    }

    /**
     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
     * if not <code>null</code>
     * @param maxActive the maximum number of objects that can be borrowed at one time
     *  (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param maxTotal the maximum number of objects that can exists at one time (see {@link #setMaxTotal})
     * @param minIdle the minimum number of idle objects to have in the pool at any one time (see {@link #setMinIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject}
     * method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject}
     * method (see {@link #setTestOnReturn})
     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle
     * objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction
     * thread (if any) (see {@link #setNumTestsPerEvictionRun})
     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before
     * it is eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
     * (see {@link #setTestWhileIdle})
     * @param lifo whether or not the pools behave as LIFO (last in first out) queues (see {@link #setLifo})
     * @since Pool 1.4
     */
    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn,
            long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
            boolean testWhileIdle, boolean lifo) {
        _factory = factory;
        _maxActive = maxActive;
        _lifo = lifo;
        switch (whenExhaustedAction) {
            case WHEN_EXHAUSTED_BLOCK:
            case WHEN_EXHAUSTED_FAIL:
            case WHEN_EXHAUSTED_GROW:
                _whenExhaustedAction = whenExhaustedAction;
                break;
            default:
                throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized.");
        }
        _maxWait = maxWait;
        _maxIdle = maxIdle;
        _maxTotal = maxTotal;
        _minIdle = minIdle;
        _testOnBorrow = testOnBorrow;
        _testOnReturn = testOnReturn;
        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        _numTestsPerEvictionRun = numTestsPerEvictionRun;
        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
        _testWhileIdle = testWhileIdle;

        _poolMap = new HashMap();
        _poolList = new CursorableLinkedList();

        startEvictor(_timeBetweenEvictionRunsMillis);
    }

    //--- public methods ---------------------------------------------

    //--- configuration methods --------------------------------------

    /**
     * Returns the cap on the number of object instances allocated by the pool
     * (checked out or idle),  per key.
     * A negative value indicates no limit.
     *
     * @return the cap on the number of active instances per key.
     * @see #setMaxActive
     */
    public synchronized int getMaxActive() {
        return _maxActive;
    }

    /**
     * Sets the cap on the number of object instances managed by the pool per key.
     * @param maxActive The cap on the number of object instances per key.
     * Use a negative value for no limit.
     *
     * @see #getMaxActive
     */
    public synchronized void setMaxActive(int maxActive) {
        _maxActive = maxActive;
        allocate();
    }

    /**
     * Returns the overall maximum number of objects (across pools) that can
     * exist at one time. A negative value indicates no limit.
     * @return the maximum number of instances in circulation at one time.
     * @see #setMaxTotal
     */
    public synchronized int getMaxTotal() {
        return _maxTotal;
    }

    /**
     * Sets the cap on the total number of instances from all pools combined.
     * When <code>maxTotal</code> is set to a
     * positive value and {@link #borrowObject borrowObject} is invoked
     * when at the limit with no idle instances available, an attempt is made to
     * create room by clearing the oldest 15% of the elements from the keyed
     * pools.
     *
     * @param maxTotal The cap on the total number of instances across pools.
     * Use a negative value for no limit.
     * @see #getMaxTotal
     */
    public synchronized void setMaxTotal(int maxTotal) {
        _maxTotal = maxTotal;
        allocate();
    }

    /**
     * Returns the action to take when the {@link #borrowObject} method
     * is invoked when the pool is exhausted (the maximum number
     * of "active" objects has been reached).
     *
     * @return one of {@link #WHEN_EXHAUSTED_BLOCK},
     * {@link #WHEN_EXHAUSTED_FAIL} or {@link #WHEN_EXHAUSTED_GROW}
     * @see #setWhenExhaustedAction
     */
    public synchronized byte getWhenExhaustedAction() {
        return _whenExhaustedAction;
    }

    /**
     * Sets the action to take when the {@link #borrowObject} method
     * is invoked when the pool is exhausted (the maximum number
     * of "active" objects has been reached).
     *
     * @param whenExhaustedAction the action code, which must be one of
     *        {@link #WHEN_EXHAUSTED_BLOCK}, {@link #WHEN_EXHAUSTED_FAIL},
     *        or {@link #WHEN_EXHAUSTED_GROW}
     * @see #getWhenExhaustedAction
     */
    public synchronized void setWhenExhaustedAction(byte whenExhaustedAction) {
        switch(whenExhaustedAction) {
            case WHEN_EXHAUSTED_BLOCK:
            case WHEN_EXHAUSTED_FAIL:
            case WHEN_EXHAUSTED_GROW:
                _whenExhaustedAction = whenExhaustedAction;
                allocate();
                break;
            default:
                throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized.");
        }
    }


    /**
     * Returns the maximum amount of time (in milliseconds) the
     * {@link #borrowObject} method should block before throwing
     * an exception when the pool is exhausted and the
     * {@link #setWhenExhaustedAction "when exhausted" action} is
     * {@link #WHEN_EXHAUSTED_BLOCK}.
     *
     * When less than or equal to 0, the {@link #borrowObject} method
     * may block indefinitely.
     *
     * @return the maximum number of milliseconds borrowObject will block.
     * @see #setMaxWait
     * @see #setWhenExhaustedAction
     * @see #WHEN_EXHAUSTED_BLOCK
     */
    public synchronized long getMaxWait() {
        return _maxWait;
    }

    /**
     * Sets the maximum amount of time (in milliseconds) the
     * {@link #borrowObject} method should block before throwing
     * an exception when the pool is exhausted and the
     * {@link #setWhenExhaustedAction "when exhausted" action} is
     * {@link #WHEN_EXHAUSTED_BLOCK}.
     *
     * When less than or equal to 0, the {@link #borrowObject} method
     * may block indefinitely.
     *
     * @param maxWait the maximum number of milliseconds borrowObject will block or negative for indefinitely.
     * @see #getMaxWait
     * @see #setWhenExhaustedAction
     * @see #WHEN_EXHAUSTED_BLOCK
     */
    public synchronized void setMaxWait(long maxWait) {
        _maxWait = maxWait;
    }

    /**
     * Returns the cap on the number of "idle" instances per key.
     * @return the maximum number of "idle" instances that can be held
     * in a given keyed pool.
     * @see #setMaxIdle
     */
    public synchronized int getMaxIdle() {
        return _maxIdle;
    }

    /**
     * Sets the cap on the number of "idle" instances in the pool.
     * If maxIdle is set too low on heavily loaded systems it is possible you
     * will see objects being destroyed and almost immediately new objects
     * being created. This is a result of the active threads momentarily
     * returning objects faster than they are requesting them them, causing the
     * number of idle objects to rise above maxIdle. The best value for maxIdle
     * for heavily loaded system will vary but the default is a good starting
     * point.
     * @param maxIdle the maximum number of "idle" instances that can be held
     * in a given keyed pool. Use a negative value for no limit.
     * @see #getMaxIdle
     * @see #DEFAULT_MAX_IDLE
     */
    public synchronized void setMaxIdle(int maxIdle) {
        _maxIdle = maxIdle;
        allocate();
    }

    /**
     * Sets the minimum number of idle objects to maintain in each of the keyed
     * pools. This setting has no effect unless
     * <code>timeBetweenEvictionRunsMillis > 0</code> and attempts to ensure
     * that each pool has the required minimum number of instances are only
     * made during idle object eviction runs.
     * @param poolSize - The minimum size of the each keyed pool
     * @since Pool 1.3
     * @see #getMinIdle
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public void setMinIdle(int poolSize) {
        _minIdle = poolSize;
    }

    /**
     * Returns the minimum number of idle objects to maintain in each of the keyed
     * pools. This setting has no effect unless
     * <code>timeBetweenEvictionRunsMillis > 0</code> and attempts to ensure
     * that each pool has the required minimum number of instances are only
     * made during idle object eviction runs.
     * @return minimum size of the each keyed pool
     * @since Pool 1.3
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public int getMinIdle() {
        return _minIdle;
    }

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * before being returned by the {@link #borrowObject}
     * method.  If the object fails to validate,
     * it will be dropped from the pool, and we will attempt
     * to borrow another.
     *
     * @return <code>true</code> if objects are validated before being borrowed.
     * @see #setTestOnBorrow
     */
    public boolean getTestOnBorrow() {
        return _testOnBorrow;
    }

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * before being returned by the {@link #borrowObject}
     * method.  If the object fails to validate,
     * it will be dropped from the pool, and we will attempt
     * to borrow another.
     *
     * @param testOnBorrow whether object should be validated before being returned by borrowObject.
     * @see #getTestOnBorrow
     */
    public void setTestOnBorrow(boolean testOnBorrow) {
        _testOnBorrow = testOnBorrow;
    }

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * before being returned to the pool within the
     * {@link #returnObject}.
     *
     * @return <code>true</code> when objects will be validated before being returned.
     * @see #setTestOnReturn
     */
    public boolean getTestOnReturn() {
        return _testOnReturn;
    }

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * before being returned to the pool within the
     * {@link #returnObject}.
     *
     * @param testOnReturn <code>true</code> so objects will be validated before being returned.
     * @see #getTestOnReturn
     */
    public void setTestOnReturn(boolean testOnReturn) {
        _testOnReturn = testOnReturn;
    }

    /**
     * Returns the number of milliseconds to sleep between runs of the
     * idle object evictor thread.
     * When non-positive, no idle object evictor thread will be
     * run.
     *
     * @return milliseconds to sleep between evictor runs.
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public synchronized long getTimeBetweenEvictionRunsMillis() {
        return _timeBetweenEvictionRunsMillis;
    }

    /**
     * Sets the number of milliseconds to sleep between runs of the
     * idle object evictor thread.
     * When non-positive, no idle object evictor thread will be
     * run.
     *
     * @param timeBetweenEvictionRunsMillis milliseconds to sleep between evictor runs.
     * @see #getTimeBetweenEvictionRunsMillis
     */
    public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        startEvictor(_timeBetweenEvictionRunsMillis);
    }

    /**
     * Returns the max number of objects to examine during each run of the
     * idle object evictor thread (if any).
     *
     * @return number of objects to examine each eviction run.
     * @see #setNumTestsPerEvictionRun
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public synchronized int getNumTestsPerEvictionRun() {
        return _numTestsPerEvictionRun;
    }

    /**
     * Sets the max number of objects to examine during each run of the
     * idle object evictor thread (if any).
     * <p>
     * When a negative value is supplied,
     * <code>ceil({@link #getNumIdle()})/abs({@link #getNumTestsPerEvictionRun})</code>
     * tests will be run.  I.e., when the value is <code>-n</code>, roughly one <code>n</code>th of the
     * idle objects will be tested per run.  When the value is positive, the number of tests
     * actually performed in each run will be the minimum of this value and the number of instances
     * idle in the pools.
     *
     * @param numTestsPerEvictionRun number of objects to examine each eviction run.
     * @see #setNumTestsPerEvictionRun
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
        _numTestsPerEvictionRun = numTestsPerEvictionRun;
    }

    /**
     * Returns the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor
     * (if any).
     *
     * @return minimum amount of time an object may sit idle in the pool before it is eligible for eviction.
     * @see #setMinEvictableIdleTimeMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public synchronized long getMinEvictableIdleTimeMillis() {
        return _minEvictableIdleTimeMillis;
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor
     * (if any).
     * When non-positive, no objects will be evicted from the pool
     * due to idle time alone.
     *
     * @param minEvictableIdleTimeMillis minimum amount of time an object may sit idle in the pool before
     * it is eligible for eviction.
     * @see #getMinEvictableIdleTimeMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    }

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * by the idle object evictor (if any).  If an object
     * fails to validate, it will be dropped from the pool.
     *
     * @return <code>true</code> when objects are validated when borrowed.
     * @see #setTestWhileIdle
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public synchronized boolean getTestWhileIdle() {
        return _testWhileIdle;
    }

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * by the idle object evictor (if any).  If an object
     * fails to validate, it will be dropped from the pool.
     *
     * @param testWhileIdle <code>true</code> so objects are validated when borrowed.
     * @see #getTestWhileIdle
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public synchronized void setTestWhileIdle(boolean testWhileIdle) {
        _testWhileIdle = testWhileIdle;
    }

    /**
     * Sets the configuration.
     * @param conf the new configuration to use.
     * @see GenericKeyedObjectPool.Config
     */
    public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {
        setMaxIdle(conf.maxIdle);
        setMaxActive(conf.maxActive);
        setMaxTotal(conf.maxTotal);
        setMinIdle(conf.minIdle);
        setMaxWait(conf.maxWait);
        setWhenExhaustedAction(conf.whenExhaustedAction);
        setTestOnBorrow(conf.testOnBorrow);
        setTestOnReturn(conf.testOnReturn);
        setTestWhileIdle(conf.testWhileIdle);
        setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
        setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
        setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
    }

    /**
     * Whether or not the idle object pools act as LIFO queues. True means
     * that borrowObject returns the most recently used ("last in") idle object
     * in a pool (if there are idle instances available).  False means that
     * the pools behave as FIFO queues - objects are taken from idle object
     * pools in the order that they are returned.
     *
     * @return <code>true</code> if the pools are configured to act as LIFO queues
     * @since 1.4
     */
     public synchronized boolean getLifo() {
         return _lifo;
     }

     /**
      * Sets the LIFO property of the pools. True means that borrowObject returns
      * the most recently used ("last in") idle object in a pool (if there are
      * idle instances available).  False means that the pools behave as FIFO
      * queues - objects are taken from idle object pools in the order that
      * they are returned.
      *
      * @param lifo the new value for the lifo property
      * @since 1.4
      */
     public synchronized void setLifo(boolean lifo) {
         this._lifo = lifo;
     }

    //-- ObjectPool methods ------------------------------------------

    /**
     * <p>Borrows an object from the keyed pool associated with the given key.</p>
     *
     * <p>If there is an idle instance available in the pool associated with the given key, then
     * either the most-recently returned (if {@link #getLifo() lifo} == true) or "oldest" (lifo == false)
     * instance sitting idle in the pool will be activated and returned.  If activation fails, or
     * {@link #getTestOnBorrow() testOnBorrow} is set to true and validation fails, the instance is destroyed and the
     * next available instance is examined.  This continues until either a valid instance is returned or there
     * are no more idle instances available.</p>
     *
     * <p>If there are no idle instances available in the pool associated with the given key, behavior
     * depends on the {@link #getMaxActive() maxActive}, {@link #getMaxTotal() maxTotal}, and (if applicable)
     * {@link #getWhenExhaustedAction() whenExhaustedAction} and {@link #getMaxWait() maxWait} properties. If the
     * number of instances checked out from the pool under the given key is less than <code>maxActive</code> and
     * the total number of instances in circulation (under all keys) is less than <code>maxTotal</code>, a new instance
     * is created, activated and (if applicable) validated and returned to the caller.</p>
     *
     * <p>If the associated keyed pool is exhausted (no available idle instances and no capacity to create new ones),
     * this method will either block ({@link #WHEN_EXHAUSTED_BLOCK}), throw a <code>NoSuchElementException</code>
     * ({@link #WHEN_EXHAUSTED_FAIL}), or grow ({@link #WHEN_EXHAUSTED_GROW} - ignoring maxActive, maxTotal properties).
     * The length of time that this method will block when <code>whenExhaustedAction == WHEN_EXHAUSTED_BLOCK</code>
     * is determined by the {@link #getMaxWait() maxWait} property.</p>
     *
     * <p>When the pool is exhausted, multiple calling threads may be simultaneously blocked waiting for instances
     * to become available.  As of pool 1.5, a "fairness" algorithm has been implemented to ensure that threads receive
     * available instances in request arrival order.</p>
     *
     * @param key pool key
     * @return object instance from the keyed pool
     * @throws NoSuchElementException if a keyed object instance cannot be returned.
     */
     public Object borrowObject(Object key) throws Exception {
        long starttime = System.currentTimeMillis();
        Latch latch = new Latch(key);
        byte whenExhaustedAction;
        long maxWait;
        synchronized (this) {
            // Get local copy of current config. Can't sync when used later as
            // it can result in a deadlock. Has the added advantage that config
            // is consistent for entire method execution
            whenExhaustedAction = _whenExhaustedAction;
            maxWait = _maxWait;

            // Add this request to the queue
            _allocationQueue.add(latch);

            // Work the allocation queue, allocating idle instances and
            // instance creation permits in request arrival order
            allocate();
        }

        for(;;) {
            synchronized (this) {
                assertOpen();
            }
            // If no object was allocated
            if (null == latch.getPair()) {
                // Check to see if we were allowed to create one
                if (latch.mayCreate()) {
                    // allow new object to be created
                } else {
                    // the pool is exhausted
                    switch(whenExhaustedAction) {
                        case WHEN_EXHAUSTED_GROW:
                            // allow new object to be created
                            synchronized (this) {
                                // Make sure another thread didn't allocate us an object
                                // or permit a new object to be created
                                if (latch.getPair() == null && !latch.mayCreate()) {
                                    _allocationQueue.remove(latch);
                                    latch.getPool().incrementInternalProcessingCount();
                                }
                            }
                        break;
                        case WHEN_EXHAUSTED_FAIL:
                            synchronized (this) {
                                // Make sure allocate hasn't already assigned an object
                                // in a different thread or permitted a new object to be created
                                if (latch.getPair() != null || latch.mayCreate()) {
                                    break;
                                }
                                _allocationQueue.remove(latch);
                            }
                            throw new NoSuchElementException("Pool exhausted");
                        case WHEN_EXHAUSTED_BLOCK:
                            try {
                                synchronized (latch) {
                                    // Before we wait, make sure another thread didn't allocate us an object
                                    // or permit a new object to be created
                                    if (latch.getPair() == null && !latch.mayCreate()) {
                                        if (maxWait <= 0) {
                                            latch.wait();
                                        } else {
                                            // this code may be executed again after a notify then continue cycle
                                            // so, need to calculate the amount of time to wait
                                            final long elapsed = (System.currentTimeMillis() - starttime);
                                            final long waitTime = maxWait - elapsed;
                                            if (waitTime > 0)
                                            {
                                                latch.wait(waitTime);
                                            }
                                        }
                                    } else {
                                        break;
                                    }
                                }
                            } catch(InterruptedException e) {
                                synchronized (this) {
                                    // Make sure allocate hasn't already assigned an object
                                    // in a different thread or permitted a new object to be created
                                    if (latch.getPair() == null && !latch.mayCreate()) {
                                        _allocationQueue.remove(latch);
                                    } else {
                                        break;
                                    }
                                }
                                Thread.currentThread().interrupt();
                                throw e;
                            }
                            if (maxWait > 0 && ((System.currentTimeMillis() - starttime) >= maxWait)) {
                                synchronized (this) {
                                    // Make sure allocate hasn't already assigned an object
                                    // in a different thread or permitted a new object to be created
                                    if (latch.getPair() == null && !latch.mayCreate()) {
                                        _allocationQueue.remove(latch);
                                    } else {
                                        break;
                                    }
                                }
                                throw new NoSuchElementException("Timeout waiting for idle object");
                            } else {
                                continue; // keep looping
                            }
                        default:
                            throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction +
                                    " not recognized.");
                    }
                }
            }

            boolean newlyCreated = false;
            if (null == latch.getPair()) {
                try {
                    Object obj = _factory.makeObject(key);
                    latch.setPair(new ObjectTimestampPair(obj));
                    newlyCreated = true;
                } finally {
                    if (!newlyCreated) {
                        // object cannot be created
                        synchronized (this) {
                            latch.getPool().decrementInternalProcessingCount();
                            // No need to reset latch - about to throw exception
                            allocate();
                        }
                    }
                }
            }

            // activate & validate the object
            try {
                _factory.activateObject(key, latch.getPair().value);
                if (_testOnBorrow && !_factory.validateObject(key, latch.getPair().value)) {
                    throw new Exception("ValidateObject failed");
                }
                synchronized (this) {
                    latch.getPool().decrementInternalProcessingCount();
                    latch.getPool().incrementActiveCount();
                }
                return latch.getPair().value;
            } catch (Throwable e) {
                PoolUtils.checkRethrow(e);
                // object cannot be activated or is invalid
                try {
                    _factory.destroyObject(key, latch.getPair().value);
                } catch (Throwable e2) {
                    PoolUtils.checkRethrow(e2);
                    // cannot destroy broken object
                }
                synchronized (this) {
                    latch.getPool().decrementInternalProcessingCount();
                    if (!newlyCreated) {
                        latch.reset();
                        _allocationQueue.add(0, latch);
                    }
                    allocate();
                }
                if (newlyCreated) {
                    throw new NoSuchElementException(
                       "Could not create a validated object, cause: " +
                            e.getMessage());
                }
                else {
                    continue; // keep looping
                }
            }
        }
    }

    /**
     * Allocate available instances to latches in the allocation queue.  Then
     * set _mayCreate to true for as many additional latches remaining in queue
     * as _maxActive allows for each key.
     */
    private void allocate() {
        boolean clearOldest = false;

        synchronized (this) {
            if (isClosed()) return;
           
            Iterator allocationQueueIter = _allocationQueue.iterator();
           
            while (allocationQueueIter.hasNext()) {
                // First use any objects in the pool to clear the queue
                Latch latch = (Latch) allocationQueueIter.next();
                ObjectQueue pool = (ObjectQueue)(_poolMap.get(latch.getkey()));
                if (null == pool) {
                    pool = new ObjectQueue();
                    _poolMap.put(latch.getkey(), pool);
                    _poolList.add(latch.getkey());
                }
                latch.setPool(pool);
                if (!pool.queue.isEmpty()) {
                    allocationQueueIter.remove();
                    latch.setPair(
                            (ObjectTimestampPair) pool.queue.removeFirst());
                    pool.incrementInternalProcessingCount();
                    _totalIdle--;
                    synchronized (latch) {
                        latch.notify();
                    }
                    // Next item in queue
                    continue;
                }

                // If there is a totalMaxActive and we are at the limit then
                // we have to make room
                if ((_maxTotal > 0) &&
                        (_totalActive + _totalIdle + _totalInternalProcessing >= _maxTotal)) {
                    clearOldest = true;
                    break;
                }

                // Second utilise any spare capacity to create new objects
                if ((_maxActive < 0 || pool.activeCount + pool.internalProcessingCount < _maxActive) &&
                        (_maxTotal < 0 || _totalActive + _totalIdle + _totalInternalProcessing < _maxTotal)) {
                    // allow new object to be created
                    allocationQueueIter.remove();
                    latch.setMayCreate(true);
                    pool.incrementInternalProcessingCount();
                    synchronized (latch) {
                        latch.notify();
                    }
                    // Next item in queue
                    continue;
                }

                // If there is no per-key limit and we reach this point we
                // must have allocated all the objects we possibly can and there
                // is no point looking at the rest of the allocation queue
                if (_maxActive < 0) {
                    break;
                }
            }
        }
       
        if (clearOldest) {
            /* Clear oldest calls factory methods so it must be called from
             * outside the sync block.
             * It also needs to be outside the sync block as it calls
             * allocate(). If called inside the sync block, the call to
             * allocate() would be able to enter the sync block (since the
             * thread already has the lock) which may have unexpected,
             * unpleasant results.
             */
            clearOldest();
        }
    }
   
    /**
     * Clears any objects sitting idle in the pool by removing them from the
     * idle instance pool and then invoking the configured PoolableObjectFactory's
     * {@link KeyedPoolableObjectFactory#destroyObject(Object, Object)} method on
     * each idle instance.
     * 
     * <p> Implementation notes:
     * <ul><li>This method does not destroy or effect in any way instances that are
     * checked out when it is invoked.</li>
     * <li>Invoking this method does not prevent objects being
     * returned to the idle instance pool, even during its execution. It locks
     * the pool only during instance removal. Additional instances may be returned
     * while removed items are being destroyed.</li>
     * <li>Exceptions encountered destroying idle instances are swallowed.</li></ul></p>
     */
    public void clear() {
        Map toDestroy = new HashMap();
        synchronized (this) {
            for (Iterator it = _poolMap.keySet().iterator(); it.hasNext();) {
                Object key = it.next();
                ObjectQueue pool = (ObjectQueue)_poolMap.get(key);
                // Copy objects to new list so pool.queue can be cleared inside
                // the sync
                List objects = new ArrayList();
                objects.addAll(pool.queue);
                toDestroy.put(key, objects);
                it.remove();
                _poolList.remove(key);
                _totalIdle = _totalIdle - pool.queue.size();
                _totalInternalProcessing =
                    _totalInternalProcessing + pool.queue.size();
                pool.queue.clear();
            }
        }
        destroy(toDestroy, _factory);
    }

    /**
     * Clears oldest 15% of objects in pool.  The method sorts the
     * objects into a TreeMap and then iterates the first 15% for removal.
     *
     * @since Pool 1.3
     */
    public void clearOldest() {
        // Map of objects to destroy my key
        final Map toDestroy = new HashMap();

        // build sorted map of idle objects
        final Map map = new TreeMap();
        synchronized (this) {
            for (Iterator keyiter = _poolMap.keySet().iterator(); keyiter.hasNext();) {
                final Object key = keyiter.next();
                final CursorableLinkedList list = ((ObjectQueue)_poolMap.get(key)).queue;
                for (Iterator it = list.iterator(); it.hasNext();) {
                    // each item into the map uses the objectimestamppair object
                    // as the key.  It then gets sorted based on the timstamp field
                    // each value in the map is the parent list it belongs in.
                    map.put(it.next(), key);
                }
            }

            // Now iterate created map and kill the first 15% plus one to account for zero
            Set setPairKeys = map.entrySet();
            int itemsToRemove = ((int) (map.size() * 0.15)) + 1;

            Iterator iter = setPairKeys.iterator();
            while (iter.hasNext() && itemsToRemove > 0) {
                Map.Entry entry = (Map.Entry) iter.next();
                // kind of backwards on naming.  In the map, each key is the objecttimestamppair
                // because it has the ordering with the timestamp value.  Each value that the
                // key references is the key of the list it belongs to.
                Object key = entry.getValue();
                ObjectTimestampPair pairTimeStamp = (ObjectTimestampPair) entry.getKey();
                final CursorableLinkedList list =
                    ((ObjectQueue)(_poolMap.get(key))).queue;
                list.remove(pairTimeStamp);

                if (toDestroy.containsKey(key)) {
                    ((List)toDestroy.get(key)).add(pairTimeStamp);
                } else {
                    List listForKey = new ArrayList();
                    listForKey.add(pairTimeStamp);
                    toDestroy.put(key, listForKey);
                }
                // if that was the last object for that key, drop that pool
                if (list.isEmpty()) {
                    _poolMap.remove(key);
                    _poolList.remove(key);
                }
                _totalIdle--;
                _totalInternalProcessing++;
                itemsToRemove--;
            }

        }
        destroy(toDestroy, _factory);
    }

    /**
     * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
     *
     * @param key the key to clear
     */
    public void clear(Object key) {
        Map toDestroy = new HashMap();

        final ObjectQueue pool;
        synchronized (this) {
            pool = (ObjectQueue)(_poolMap.remove(key));
            if (pool == null) {
                return;
            } else {
                _poolList.remove(key);
            }
            // Copy objects to new list so pool.queue can be cleared inside
            // the sync
            List objects = new ArrayList();
            objects.addAll(pool.queue);
            toDestroy.put(key, objects);
            _totalIdle = _totalIdle - pool.queue.size();
            _totalInternalProcessing =
                _totalInternalProcessing + pool.queue.size();
            pool.queue.clear();
        }
        destroy(toDestroy, _factory);
    }

    /**
     * Assuming Map<Object,Collection<ObjectTimestampPair>>, destroy all
     * ObjectTimestampPair.value using the supplied factory.
     *
     * @param m Map containing keyed pools to clear
     * @param factory KeyedPoolableObjectFactory used to destroy the objects
     */
    private void destroy(Map m, KeyedPoolableObjectFactory factory) {
        for (Iterator entries = m.entrySet().iterator(); entries.hasNext();) {
            Map.Entry entry = (Entry) entries.next();
            Object key = entry.getKey();
            Collection c = (Collection) entry.getValue();
            for (Iterator it = c.iterator(); it.hasNext();) {
                try {
                    factory.destroyObject(
                            key,((ObjectTimestampPair)(it.next())).value);
                } catch(Exception e) {
                    // ignore error, keep destroying the rest
                } finally {
                    synchronized(this) {
                        _totalInternalProcessing--;
                        allocate();
                    }
                }
            }

        }
    }

    /**
     * Returns the total number of instances current borrowed from this pool but not yet returned.
     *
     * @return the total number of instances currently borrowed from this pool
     */
    public synchronized int getNumActive() {
        return _totalActive;
    }

    /**
     * Returns the total number of instances currently idle in this pool.
     *
     * @return the total number of instances currently idle in this pool
     */
    public synchronized int getNumIdle() {
        return _totalIdle;
    }

    /**
     * Returns the number of instances currently borrowed from but not yet returned
     * to the pool corresponding to the given <code>key</code>.
     *
     * @param key the key to query
     * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
     */
    public synchronized int getNumActive(Object key) {
        final ObjectQueue pool = (ObjectQueue)(_poolMap.get(key));
        return pool != null ? pool.activeCount : 0;
    }

    /**
     * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
     *
     * @param key the key to query
     * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
     */
    public synchronized int getNumIdle(Object key) {
        final ObjectQueue pool = (ObjectQueue)(_poolMap.get(key));
        return pool != null ? pool.queue.size() : 0;
    }

    /**
     * <p>Returns an object to a keyed pool.</p>
     *
     * <p>For the pool to function correctly, the object instance <strong>must</strong> have been borrowed
     * from the pool (under the same key) and not yet returned. Repeated <code>returnObject</code> calls on
     * the same object/key pair (with no <code>borrowObject</code> calls in between) will result in multiple
     * references to the object in the idle instance pool.</p>
     *
     * <p>If {@link #getMaxIdle() maxIdle} is set to a positive value and the number of idle instances under the given
     * key has reached this value, the returning instance is destroyed.</p>
     *
     * <p>If {@link #getTestOnReturn() testOnReturn} == true, the returning instance is validated before being returned
     * to the idle instance pool under the given key.  In this case, if validation fails, the instance is destroyed.</p>
     *
     * @param key pool key
     * @param obj instance to return to the keyed pool
     * @throws Exception
     */
    public void returnObject(Object key, Object obj) throws Exception {
        try {
            addObjectToPool(key, obj, true);
        } catch (Exception e) {
            if (_factory != null) {
                try {
                    _factory.destroyObject(key, obj);
                } catch (Exception e2) {
                    // swallowed
                }
                // TODO: Correctness here depends on control in addObjectToPool.
                // These two methods should be refactored, removing the
                // "behavior flag", decrementNumActive, from addObjectToPool.
                ObjectQueue pool = (ObjectQueue) (_poolMap.get(key));
                if (pool != null) {
                    synchronized(this) {
                        pool.decrementActiveCount();
                        allocate();
                    }
                }
            }
        }
    }

    /**
     * <p>Adds an object to the keyed pool.</p>
     *
     * <p>Validates the object if testOnReturn == true and passivates it before returning it to the pool.
     * if validation or passivation fails, or maxIdle is set and there is no room in the pool, the instance
     * is destroyed.</p>
     *
     * <p>Calls {@link #allocate()} on successful completion</p>
     *
     * @param key pool key
     * @param obj instance to add to the keyed pool
     * @param decrementNumActive whether or not to decrement the active count associated with the keyed pool
     * @throws Exception
     */
    private void addObjectToPool(Object key, Object obj,
            boolean decrementNumActive) throws Exception {

        // if we need to validate this object, do so
        boolean success = true; // whether or not this object passed validation
        if (_testOnReturn && !_factory.validateObject(key, obj)) {
            success = false;
        } else {
            _factory.passivateObject(key, obj);
        }

        boolean shouldDestroy = !success;
        ObjectQueue pool;

        // Add instance to pool if there is room and it has passed validation
        // (if testOnreturn is set)
        synchronized (this) {
            // grab the pool (list) of objects associated with the given key
            pool = (ObjectQueue) (_poolMap.get(key));
            // if it doesn't exist, create it
            if (null == pool) {
                pool = new ObjectQueue();
                _poolMap.put(key, pool);
                _poolList.add(key);
            }
            if (isClosed()) {
                shouldDestroy = true;
            } else {
                // if there's no space in the pool, flag the object for destruction
                // else if we passivated successfully, return it to the pool
                if (_maxIdle >= 0 && (pool.queue.size() >= _maxIdle)) {
                    shouldDestroy = true;
                } else if (success) {
                    // borrowObject always takes the first element from the queue,
                    // so for LIFO, push on top, FIFO add to end
                    if (_lifo) {
                        pool.queue.addFirst(new ObjectTimestampPair(obj));
                    } else {
                        pool.queue.addLast(new ObjectTimestampPair(obj));
                    }
                    _totalIdle++;
                    if (decrementNumActive) {
                        pool.decrementActiveCount();
                    }
                    allocate();
                }
            }
        }

        // Destroy the instance if necessary
        if (shouldDestroy) {
            try {
                _factory.destroyObject(key, obj);
            } catch(Exception e) {
                // ignored?
            }
            // Decrement active count *after* destroy if applicable
            if (decrementNumActive) {
                synchronized(this) {
                    pool.decrementActiveCount();
                    allocate();
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     * <p>Activation of this method decrements the active count associated with the given keyed pool
     * and attempts to destroy <code>obj.</code></p>
     *
     * @param key pool key
     * @param obj instance to invalidate
     * @throws Exception if an exception occurs destroying the object
     */
    public void invalidateObject(Object key, Object obj) throws Exception {
        try {
            _factory.destroyObject(key, obj);
        } finally {
            synchronized (this) {
                ObjectQueue pool = (ObjectQueue) (_poolMap.get(key));
                if (null == pool) {
                    pool = new ObjectQueue();
                    _poolMap.put(key, pool);
                    _poolList.add(key);
                }
                pool.decrementActiveCount();
                allocate(); // _totalActive has changed
            }
        }
    }

    /**
     * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
     * passivate it, and then place it in the idle object pool.
     * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
     *
     * @param key the key a new instance should be added to
     * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
     * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been
     * called on this pool.
     */
    public void addObject(Object key) throws Exception {
        assertOpen();
        if (_factory == null) {
            throw new IllegalStateException("Cannot add objects without a factory.");
        }
        Object obj = _factory.makeObject(key);
        try {
            assertOpen();
            addObjectToPool(key, obj, false);
        } catch (IllegalStateException ex) { // Pool closed
            try {
                _factory.destroyObject(key, obj);
            } catch (Exception ex2) {
                // swallow
            }
            throw ex;
        }
    }

    /**
     * Registers a key for pool control.
     *
     * If <code>populateImmediately</code> is <code>true</code> and
     * <code>minIdle > 0,</code> the pool under the given key will be
     * populated immediately with <code>minIdle</code> idle instances.
     *
     * @param key - The key to register for pool control.
     * @param populateImmediately - If this is <code>true</code>, the pool
     * will be populated immediately.
     * @since Pool 1.3
     */
    public synchronized void preparePool(Object key, boolean populateImmediately) {
        ObjectQueue pool = (ObjectQueue)(_poolMap.get(key));
        if (null == pool) {
            pool = new ObjectQueue();
            _poolMap.put(key,pool);
            _poolList.add(key);
        }

        if (populateImmediately) {
            try {
                // Create the pooled objects
                ensureMinIdle(key);
            }
            catch (Exception e) {
                //Do nothing
            }
        }
    }

    /**
     * <p>Closes the keyed object pool.  Once the pool is closed, {@link #borrowObject(Object)}
     * will fail with IllegalStateException, but {@link #returnObject(Object, Object)} and
     * {@link #invalidateObject(Object, Object)} will continue to work, with returned objects
     * destroyed on return.</p>
     *
     * <p>Destroys idle instances in the pool by invoking {@link #clear()}.</p>
     *
     * @throws Exception
     */
    public void close() throws Exception {
        super.close();
        synchronized (this) {
            clear();
            if (null != _evictionCursor) {
                _evictionCursor.close();
                _evictionCursor = null;
            }
            if (null != _evictionKeyCursor) {
                _evictionKeyCursor.close();
                _evictionKeyCursor = null;
            }
            startEvictor(-1L);
        }
    }

    /**
     * <p>Sets the keyed poolable object factory associated with this pool.</p>
     *
     * <p>If this method is called when objects are checked out of any of the keyed pools,
     * an IllegalStateException is thrown.  Calling this method also has the side effect of
     * destroying any idle instances in existing keyed pools, using the original factory.</p>
     *
     * @param factory KeyedPoolableObjectFactory to use when creating keyed object pool instances
     * @throws IllegalStateException if there are active (checked out) instances associated with this keyed object pool
     * @deprecated to be removed in version 2.0
     */
    public void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException {
        Map toDestroy = new HashMap();
        final KeyedPoolableObjectFactory oldFactory = _factory;
        synchronized (this) {
            assertOpen();
            if (0 < getNumActive()) {
                throw new IllegalStateException("Objects are already active");
            } else {
                for (Iterator it = _poolMap.keySet().iterator(); it.hasNext();) {
                    Object key = it.next();
                    ObjectQueue pool = (ObjectQueue)_poolMap.get(key);
                    if (pool != null) {
                        // Copy objects to new list so pool.queue can be cleared
                        // inside the sync
                        List objects = new ArrayList();
                        objects.addAll(pool.queue);
                        toDestroy.put(key, objects);
                        it.remove();
                        _poolList.remove(key);
                        _totalIdle = _totalIdle - pool.queue.size();
                        _totalInternalProcessing =
                            _totalInternalProcessing + pool.queue.size();
                        pool.queue.clear();
                    }
                }
                _factory = factory;
            }
        }
        destroy(toDestroy, oldFactory);
    }

    /**
     * <p>Perform <code>numTests</code> idle object eviction tests, evicting
     * examined objects that meet the criteria for eviction. If
     * <code>testWhileIdle</code> is true, examined objects are validated
     * when visited (and removed if invalid); otherwise only objects that
     * have been idle for more than <code>minEvicableIdletimeMillis</code>
     * are removed.</p>
     *
     * <p>Successive activations of this method examine objects in keyed pools
     * in sequence, cycling through the keys and examining objects in
     * oldest-to-youngest order within the keyed pools.</p>
     *
     * @throws Exception when there is a problem evicting idle objects.
     */
    public void evict() throws Exception {
        Object key = null;
        boolean testWhileIdle;
        long minEvictableIdleTimeMillis;

        synchronized (this) {
            // Get local copy of current config. Can't sync when used later as
            // it can result in a deadlock. Has the added advantage that config
            // is consistent for entire method execution
            testWhileIdle = _testWhileIdle;
            minEvictableIdleTimeMillis = _minEvictableIdleTimeMillis;

            // Initialize key to last key value
            if (_evictionKeyCursor != null &&
                    _evictionKeyCursor._lastReturned != null) {
                key = _evictionKeyCursor._lastReturned.value();
            }
        }

        for (int i=0, m=getNumTests(); i<m; i++) {
            final ObjectTimestampPair pair;
            synchronized (this) {
                // make sure pool map is not empty; otherwise do nothing
                if (_poolMap == null || _poolMap.size() == 0) {
                    continue;
                }

                // if we don't have a key cursor, then create one
                if (null == _evictionKeyCursor) {
                    resetEvictionKeyCursor();
                    key = null;
                }

                // if we don't have an object cursor, create one
                if (null == _evictionCursor) {
                    // if the _evictionKeyCursor has a next value, use this key
                    if (_evictionKeyCursor.hasNext()) {
                        key = _evictionKeyCursor.next();
                        resetEvictionObjectCursor(key);
                    } else {
                        // Reset the key cursor and try again
                        resetEvictionKeyCursor();
                        if (_evictionKeyCursor != null) {
                            if (_evictionKeyCursor.hasNext()) {
                                key = _evictionKeyCursor.next();
                                resetEvictionObjectCursor(key);
                            }
                        }
                    }
                }

                if (_evictionCursor == null) {
                    continue; // should never happen; do nothing
                }

                // If eviction cursor is exhausted, try to move
                // to the next key and reset
                if ((_lifo && !_evictionCursor.hasPrevious()) ||
                        (!_lifo && !_evictionCursor.hasNext())) {
                    if (_evictionKeyCursor != null) {
                        if (_evictionKeyCursor.hasNext()) {
                            key = _evictionKeyCursor.next();
                            resetEvictionObjectCursor(key);
                        } else { // Need to reset Key cursor
                            resetEvictionKeyCursor();
                            if (_evictionKeyCursor != null) {
                                if (_evictionKeyCursor.hasNext()) {
                                    key = _evictionKeyCursor.next();
                                    resetEvictionObjectCursor(key);
                                }
                            }
                        }
                    }
                }

                if ((_lifo && !_evictionCursor.hasPrevious()) ||
                        (!_lifo && !_evictionCursor.hasNext())) {
                    continue; // reset failed, do nothing
                }

                // if LIFO and the _evictionCursor has a previous object,
                // or FIFO and _evictionCursor has a next object, test it
                pair = _lifo ?
                        (ObjectTimestampPair) _evictionCursor.previous() :
                        (ObjectTimestampPair) _evictionCursor.next();
                _evictionCursor.remove();
                _totalIdle--;
                _totalInternalProcessing++;
            }

            boolean removeObject=false;
            if ((minEvictableIdleTimeMillis > 0) &&
               (System.currentTimeMillis() - pair.tstamp >
               minEvictableIdleTimeMillis)) {
                removeObject=true;
            }
            if (testWhileIdle && removeObject == false) {
                boolean active = false;
                try {
                    _factory.activateObject(key,pair.value);
                    active = true;
                } catch(Exception e) {
                    removeObject=true;
                }
                if (active) {
                    if (!_factory.validateObject(key,pair.value)) {
                        removeObject=true;
                    } else {
                        try {
                            _factory.passivateObject(key,pair.value);
                        } catch(Exception e) {
                            removeObject=true;
                        }
                    }
                }
            }

            if (removeObject) {
                try {
                    _factory.destroyObject(key, pair.value);
                } catch(Exception e) {
                    // ignored
                } finally {
                    // Do not remove the key from the _poolList or _poolmap,
                    // even if the list stored in the _poolMap for this key is
                    // empty when minIdle > 0.
                    //
                    // Otherwise if it was the last object for that key,
                    // drop that pool
                    if (_minIdle == 0) {
                        synchronized (this) {
                            ObjectQueue objectQueue =
                                (ObjectQueue)_poolMap.get(key);
                            if (objectQueue != null &&
                                    objectQueue.queue.isEmpty()) {
                                _poolMap.remove(key);
                                _poolList.remove(key);
                            }
                        }
                    }
                }
            }
            synchronized (this) {
                if (!removeObject) {
                    _evictionCursor.add(pair);
                    _totalIdle++;
                    if (_lifo) {
                        // Skip over the element we just added back
                        _evictionCursor.previous();
                    }
                }
                _totalInternalProcessing--;
            }
        }
    }

    /**
     * Resets the eviction key cursor and closes any
     * associated eviction object cursor
     */
    private void resetEvictionKeyCursor() {
        if (_evictionKeyCursor != null) {
            _evictionKeyCursor.close();
        }
        _evictionKeyCursor = _poolList.cursor();
        if (null != _evictionCursor) {
            _evictionCursor.close();
            _evictionCursor = null;
        }
    }

    /**
     * Resets the eviction object cursor for the given key
     *
     * @param key eviction key
     */
    private void resetEvictionObjectCursor(Object key) {
        if (_evictionCursor != null) {
            _evictionCursor.close();
        }
        if (_poolMap == null) {
            return;
        }
        ObjectQueue pool = (ObjectQueue) (_poolMap.get(key));
        if (pool != null) {
            CursorableLinkedList queue = pool.queue;
            _evictionCursor = queue.cursor(_lifo ? queue.size() : 0);
        }
    }

    /**
     * Iterates through all the known keys and creates any necessary objects to maintain
     * the minimum level of pooled objects.
     * @see #getMinIdle
     * @see #setMinIdle
     * @throws Exception If there was an error whilst creating the pooled objects.
     */
    private void ensureMinIdle() throws Exception {
        //Check if should sustain the pool
        if (_minIdle > 0) {
            Object[] keysCopy;
            synchronized(this) {
                // Get the current set of keys
                keysCopy = _poolMap.keySet().toArray();
            }

            // Loop through all elements in _poolList
            // Find out the total number of max active and max idle for that class
            // If the number is less than the minIdle, do creation loop to boost numbers
            for (int i=0; i < keysCopy.length; i++) {
                //Get the next key to process
                ensureMinIdle(keysCopy[i]);
            }
        }
    }

    /**
     * Re-creates any needed objects to maintain the minimum levels of
     * pooled objects for the specified key.
     *
     * This method uses {@link #calculateDeficit} to calculate the number
     * of objects to be created. {@link #calculateDeficit} can be overridden to
     * provide a different method of calculating the number of objects to be
     * created.
     * @param key The key to process
     * @throws Exception If there was an error whilst creating the pooled objects
     */
    private void ensureMinIdle(Object key) throws Exception {
        // Calculate current pool objects
        ObjectQueue pool;
        synchronized(this) {
            pool = (ObjectQueue)(_poolMap.get(key));
        }
        if (pool == null) {
            return;
        }

        // this method isn't synchronized so the
        // calculateDeficit is done at the beginning
        // as a loop limit and a second time inside the loop
        // to stop when another thread already returned the
        // needed objects
        int objectDeficit = calculateDeficit(pool, false);

        for (int i = 0; i < objectDeficit && calculateDeficit(pool, true) > 0; i++) {
            try {
                addObject(key);
            } finally {
                synchronized (this) {
                    pool.decrementInternalProcessingCount();
                    allocate();
                }
            }
        }
    }

    //--- non-public methods ----------------------------------------

    /**
     * Start the eviction thread or service, or when
     * <code>delay</code> is non-positive, stop it
     * if it is already running.
     *
     * @param delay milliseconds between evictor runs.
     */
    protected synchronized void startEvictor(long delay) {
        if (null != _evictor) {
            EvictionTimer.cancel(_evictor);
            _evictor = null;
        }
        if (delay > 0) {
            _evictor = new Evictor();
            EvictionTimer.schedule(_evictor, delay, delay);
        }
    }

    /**
     * Returns pool info including {@link #getNumActive()}, {@link #getNumIdle()}
     * and currently defined keys.
     *
     * @return string containing debug information
     */
    synchronized String debugInfo() {
        StringBuffer buf = new StringBuffer();
        buf.append("Active: ").append(getNumActive()).append("\n");
        buf.append("Idle: ").append(getNumIdle()).append("\n");
        Iterator it = _poolMap.keySet().iterator();
        while (it.hasNext()) {
            Object key = it.next();
            buf.append("\t").append(key).append(" ").append(_poolMap.get(key)).append("\n");
        }
        return buf.toString();
    }

    /**
     * Returns the number of tests to be performed in an Evictor run,
     * based on the current values of <code>_numTestsPerEvictionRun</code>
     * and <code>_totalIdle</code>.
     *
     * @see #setNumTestsPerEvictionRun
     * @return the number of tests for the Evictor to run
     */
    private synchronized int getNumTests() {
        if (_numTestsPerEvictionRun >= 0) {
            return Math.min(_numTestsPerEvictionRun, _totalIdle);
        } else {
            return(int)(Math.ceil(_totalIdle/Math.abs((double)_numTestsPerEvictionRun)));
        }
    }

    /**
     * This returns the number of objects to create during the pool
     * sustain cycle. This will ensure that the minimum number of idle
     * instances is maintained without going past the maxActive value.
     *
     * @param pool the ObjectPool to calculate the deficit for
     * @param incrementInternal - Should the count of objects currently under
     *                            some form of internal processing be
     *                            incremented?
     * @return The number of objects to be created
     */
    private synchronized int calculateDeficit(ObjectQueue pool,
            boolean incrementInternal) {
        int objectDefecit = 0;

        //Calculate no of objects needed to be created, in order to have
        //the number of pooled objects < maxActive();
        objectDefecit = getMinIdle() - pool.queue.size();
        if (getMaxActive() > 0) {
            int growLimit = Math.max(0, getMaxActive() - pool.activeCount - pool.queue.size() - pool.internalProcessingCount);
            objectDefecit = Math.min(objectDefecit, growLimit);
        }

        // Take the maxTotal limit into account
        if (getMaxTotal() > 0) {
            int growLimit = Math.max(0, getMaxTotal() - getNumActive() - getNumIdle() - _totalInternalProcessing);
            objectDefecit = Math.min(objectDefecit, growLimit);
        }

        if (incrementInternal && objectDefecit > 0) {
            pool.incrementInternalProcessingCount();
        }
        return objectDefecit;
    }

    //--- inner classes ----------------------------------------------

    /**
     * A "struct" that keeps additional information about the actual queue of pooled objects.
     */
    private class ObjectQueue {
        /** Number of instances checked out to clients from this queue */
        private int activeCount = 0;
       
        /** Idle instance queue */
        private final CursorableLinkedList queue = new CursorableLinkedList();
       
        /** Number of instances in process of being created */
        private int internalProcessingCount = 0;

        /** Increment the active count for this queue */
        void incrementActiveCount() {
            synchronized (GenericKeyedObjectPool.this) {
                _totalActive++;
            }
            activeCount++;
        }

        /** Decrement the active count for this queue */
        void decrementActiveCount() {
            synchronized (GenericKeyedObjectPool.this) {
                _totalActive--;
            }
            if (activeCount > 0) {
                activeCount--;
            }
        }

        /** Record the fact that one more instance is queued for creation */
        void incrementInternalProcessingCount() {
            synchronized (GenericKeyedObjectPool.this) {
                _totalInternalProcessing++;
            }
            internalProcessingCount++;
        }

        /** Decrement the number of instances in process of being created */
        void decrementInternalProcessingCount() {
            synchronized (GenericKeyedObjectPool.this) {
                _totalInternalProcessing--;
            }
            internalProcessingCount--;
        }
    }

    /**
     * A simple "struct" encapsulating an object instance and a timestamp.
     *
     * Implements Comparable, objects are sorted from old to new.
     *
     * This is also used by {@link GenericObjectPool}.
     */
    static class ObjectTimestampPair implements Comparable {
        //CHECKSTYLE: stop VisibilityModifier
        /**
         * Object instance
         */
        Object value;
       
        /**
         * timestamp
         */
        long tstamp;
        //CHECKSTYLE: resume VisibilityModifier

        /**
         * Create a new ObjectTimestampPair using the given object and the current system time.
         * @param val object instance
         */
        ObjectTimestampPair(Object val) {
            this(val, System.currentTimeMillis());
        }

        /**
         * Create a new ObjectTimeStampPair using the given object and timestamp value.
         * @param val object instance
         * @param time long representation of timestamp
         */
        ObjectTimestampPair(Object val, long time) {
            value = val;
            tstamp = time;
        }

        /**
         * Returns a string representation.
         *
         * @return String representing this ObjectTimestampPair
         */
        public String toString() {
            return value + ";" + tstamp;
        }

        /**
         * Compares this to another object by casting the argument to an
         * ObjectTimestampPair.
         *
         * @param obj object to cmpare
         * @return result of comparison
         */
        public int compareTo(Object obj) {
            return compareTo((ObjectTimestampPair) obj);
        }

        /**
         * Compares this to another ObjectTimestampPair, using the timestamp as basis for comparison.
         * Implementation is consistent with equals.
         *
         * @param other object to compare
         * @return result of comparison
         */
        public int compareTo(ObjectTimestampPair other) {
            final long tstampdiff = this.tstamp - other.tstamp;
            if (tstampdiff == 0) {
                // make sure the natural ordering is consistent with equals
                // see java.lang.Comparable Javadocs
                return System.identityHashCode(this) - System.identityHashCode(other);
            } else {
                // handle int overflow
                return (int)Math.min(Math.max(tstampdiff, Integer.MIN_VALUE), Integer.MAX_VALUE);
            }
        }

        /**
         * @return the value
         */
        public Object getValue() {
            return value;
        }

        /**
         * @return the tstamp
         */
        public long getTstamp() {
            return tstamp;
        }
    }

    /**
     * The idle object evictor {@link TimerTask}.
     * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
     */
    private class Evictor extends TimerTask {
        /**
         * Run pool maintenance.  Evict objects qualifying for eviction and then
         * invoke {@link GenericKeyedObjectPool#ensureMinIdle()}.
         */
        public void run() {
            //Evict from the pool
            try {
                evict();
            } catch(Exception e) {
                // ignored
            } catch(OutOfMemoryError oome) {
                // Log problem but give evictor thread a chance to continue in
                // case error is recoverable
                oome.printStackTrace(System.err);
            }
            //Re-create idle instances.
            try {
                ensureMinIdle();
            } catch (Exception e) {
                // ignored
            }
        }
    }

    /**
     * A simple "struct" encapsulating the
     * configuration information for a <code>GenericKeyedObjectPool</code>.
     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory,GenericKeyedObjectPool.Config)
     * @see GenericKeyedObjectPool#setConfig
     */
    public static class Config {
        //CHECKSTYLE: stop VisibilityModifier
        /**
         * @see GenericKeyedObjectPool#setMaxIdle
         */
        public int maxIdle = GenericKeyedObjectPool.DEFAULT_MAX_IDLE;
        /**
         * @see GenericKeyedObjectPool#setMaxActive
         */
        public int maxActive = GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE;
        /**
         * @see GenericKeyedObjectPool#setMaxTotal
         */
        public int maxTotal = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
        /**
         * @see GenericKeyedObjectPool#setMinIdle
         */
        public int minIdle = GenericKeyedObjectPool.DEFAULT_MIN_IDLE;
        /**
         * @see GenericKeyedObjectPool#setMaxWait
         */
        public long maxWait = GenericKeyedObjectPool.DEFAULT_MAX_WAIT;
        /**
         * @see GenericKeyedObjectPool#setWhenExhaustedAction
         */
        public byte whenExhaustedAction = GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
        /**
         * @see GenericKeyedObjectPool#setTestOnBorrow
         */
        public boolean testOnBorrow = GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW;
        /**
         * @see GenericKeyedObjectPool#setTestOnReturn
         */
        public boolean testOnReturn = GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN;
        /**
         * @see GenericKeyedObjectPool#setTestWhileIdle
         */
        public boolean testWhileIdle = GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE;
        /**
         * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
         */
        public long timeBetweenEvictionRunsMillis = GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
        /**
         * @see GenericKeyedObjectPool#setNumTestsPerEvictionRun
         */
        public int numTestsPerEvictionRun =  GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
        /**
         * @see GenericKeyedObjectPool#setMinEvictableIdleTimeMillis
         */
        public long minEvictableIdleTimeMillis = GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
        /**
         * @see GenericKeyedObjectPool#setLifo
         */
        public boolean lifo = GenericKeyedObjectPool.DEFAULT_LIFO;
        //CHECKSTYLE: resume VisibilityModifier
    }

    /**
     * Latch used to control allocation order of objects to threads to ensure
     * fairness. That is, for each key, objects are allocated to threads in the order
     * that threads request objects.
     *
     * @since 1.5
     */
    private static final class Latch {
       
        /** key of associated pool */
        private final Object _key;
       
        /** keyed pool associated with this latch */
        private ObjectQueue _pool;
       
        /** holds an ObjectTimestampPair when this latch has been allocated an instance */
        private ObjectTimestampPair _pair;
       
        /** indicates that this latch can create an instance */
        private boolean _mayCreate = false;

        /**
         * Create a latch with the given key
         * @param key key of the pool associated with this latch
         */
        private Latch(Object key) {
            _key = key;
        }

        /**
         * Retuns the key of the associated pool
         * @return associated pool key
         */
        private synchronized Object getkey() {
            return _key;
        }

        /**
         * Returns the pool associated with this latch
         * @return pool
         */
        private synchronized ObjectQueue getPool() {
            return _pool;
        }
       
        /**
         * Sets the pool associated with this latch
         * @param pool the pool
         */
        private synchronized void setPool(ObjectQueue pool) {
            _pool = pool;
        }

        /**
         * Gets the ObjectTimestampPair allocated to this latch.
         * Returns null if this latch does not have an instance allocated to it.
         * @return the associated ObjectTimestampPair
         */
        private synchronized ObjectTimestampPair getPair() {
            return _pair;
        }
       
        /**
         * Allocate an ObjectTimestampPair to this latch.
         * @param pair ObjectTimestampPair on this latch
         */
        private synchronized void setPair(ObjectTimestampPair pair) {
            _pair = pair;
        }

        /**
         * Whether or not this latch can create an instance
         * @return true if this latch has an instance creation permit
         */
        private synchronized boolean mayCreate() {
            return _mayCreate;
        }
       
        /**
         * Sets the mayCreate property
         *
         * @param mayCreate true means this latch can create an instance
         */
        private synchronized void setMayCreate(boolean mayCreate) {
            _mayCreate = mayCreate;
        }

        /**
         * Reset the latch data. Used when an allocation fails and the latch
         * needs to be re-added to the queue.
         */
        private synchronized void reset() {
            _pair = null;
            _mayCreate = false;
        }
    }

    //--- protected attributes ---------------------------------------

    /**
     * The cap on the number of idle instances in the pool.
     * @see #setMaxIdle
     * @see #getMaxIdle
     */
    private int _maxIdle = DEFAULT_MAX_IDLE;

    /**
     * The minimum no of idle objects to keep in the pool.
     * @see #setMinIdle
     * @see #getMinIdle
     */
    private volatile int _minIdle = DEFAULT_MIN_IDLE;

    /**
     * The cap on the number of active instances from the pool.
     * @see #setMaxActive
     * @see #getMaxActive
     */
    private int _maxActive = DEFAULT_MAX_ACTIVE;

    /**
     * The cap on the total number of instances from the pool if non-positive.
     * @see #setMaxTotal
     * @see #getMaxTotal
     */
    private int _maxTotal = DEFAULT_MAX_TOTAL;

    /**
     * The maximum amount of time (in millis) the
     * {@link #borrowObject} method should block before throwing
     * an exception when the pool is exhausted and the
     * {@link #getWhenExhaustedAction "when exhausted" action} is
     * {@link #WHEN_EXHAUSTED_BLOCK}.
     *
     * When less than or equal to 0, the {@link #borrowObject} method
     * may block indefinitely.
     *
     * @see #setMaxWait
     * @see #getMaxWait
     * @see #WHEN_EXHAUSTED_BLOCK
     * @see #setWhenExhaustedAction
     * @see #getWhenExhaustedAction
     */
    private long _maxWait = DEFAULT_MAX_WAIT;

    /**
     * The action to take when the {@link #borrowObject} method
     * is invoked when the pool is exhausted (the maximum number
     * of "active" objects has been reached).
     *
     * @see #WHEN_EXHAUSTED_BLOCK
     * @see #WHEN_EXHAUSTED_FAIL
     * @see #WHEN_EXHAUSTED_GROW
     * @see #DEFAULT_WHEN_EXHAUSTED_ACTION
     * @see #setWhenExhaustedAction
     * @see #getWhenExhaustedAction
     */
    private byte _whenExhaustedAction = DEFAULT_WHEN_EXHAUSTED_ACTION;

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * before being returned by the {@link #borrowObject}
     * method.  If the object fails to validate,
     * it will be dropped from the pool, and we will attempt
     * to borrow another.
     *
     * @see #setTestOnBorrow
     * @see #getTestOnBorrow
     */
    private volatile boolean _testOnBorrow = DEFAULT_TEST_ON_BORROW;

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * before being returned to the pool within the
     * {@link #returnObject}.
     *
     * @see #getTestOnReturn
     * @see #setTestOnReturn
     */
    private volatile boolean _testOnReturn = DEFAULT_TEST_ON_RETURN;

    /**
     * When <code>true</code>, objects will be
     * {@link org.datanucleus.store.rdbms.datasource.dbcp.pool.PoolableObjectFactory#validateObject validated}
     * by the idle object evictor (if any).  If an object
     * fails to validate, it will be dropped from the pool.
     *
     * @see #setTestWhileIdle
     * @see #getTestWhileIdle
     * @see #getTimeBetweenEvictionRunsMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    private boolean _testWhileIdle = DEFAULT_TEST_WHILE_IDLE;

    /**
     * The number of milliseconds to sleep between runs of the
     * idle object evictor thread.
     * When non-positive, no idle object evictor thread will be
     * run.
     *
     * @see #setTimeBetweenEvictionRunsMillis
     * @see #getTimeBetweenEvictionRunsMillis
     */
    private long _timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;

    /**
     * The number of objects to examine during each run of the
     * idle object evictor thread (if any).
     * <p>
     * When a negative value is supplied, <code>ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun})</code>
     * tests will be run.  I.e., when the value is <code>-n</code>, roughly one <code>n</code>th of the
     * idle objects will be tested per run.
     *
     * @see #setNumTestsPerEvictionRun
     * @see #getNumTestsPerEvictionRun
     * @see #getTimeBetweenEvictionRunsMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    private int _numTestsPerEvictionRun =  DEFAULT_NUM_TESTS_PER_EVICTION_RUN;

    /**
     * The minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor
     * (if any).
     * When non-positive, no objects will be evicted from the pool
     * due to idle time alone.
     *
     * @see #setMinEvictableIdleTimeMillis
     * @see #getMinEvictableIdleTimeMillis
     * @see #getTimeBetweenEvictionRunsMillis
     * @see #setTimeBetweenEvictionRunsMillis
     */
    private long _minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;

    /** My hash of pools (ObjectQueue). */
    private Map _poolMap = null;

    /** The total number of active instances. */
    private int _totalActive = 0;

    /** The total number of idle instances. */
    private int _totalIdle = 0;

    /**
     * The number of objects subject to some form of internal processing
     * (usually creation or destruction) that should be included in the total
     * number of objects but are neither active nor idle.
     */
    private int _totalInternalProcessing = 0;

    /** My {@link KeyedPoolableObjectFactory}. */
    private KeyedPoolableObjectFactory _factory = null;

    /**
     * My idle object eviction {@link TimerTask}, if any.
     */
    private Evictor _evictor = null;

    /**
     * A cursorable list of my pools.
     * @see GenericKeyedObjectPool.Evictor#run
     */
    private CursorableLinkedList _poolList = null;

    /** Eviction cursor (over instances within-key) */
    private CursorableLinkedList.Cursor _evictionCursor = null;
   
    /** Eviction cursor (over keys) */
    private CursorableLinkedList.Cursor _evictionKeyCursor = null;

    /** Whether or not the pools behave as LIFO queues (last in first out) */
    private boolean _lifo = DEFAULT_LIFO;

    /**
     * Used to track the order in which threads call {@link #borrowObject()} so
     * that objects can be allocated in the order in which the threads requested
     * them.
     */
    private LinkedList _allocationQueue = new LinkedList();

}
TOP

Related Classes of org.datanucleus.store.rdbms.datasource.dbcp.pool.impl.GenericKeyedObjectPool$ObjectQueue

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.