All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.pool.impl.GenericKeyedObjectPool Maven / Gradle / Ivy

The newest version!
/*
 * 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.apache.commons.pool.impl;

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

import org.apache.commons.pool.BaseKeyedObjectPool;
import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.apache.commons.pool.PoolUtils;

/**
 * A configurable KeyedObjectPool implementation.
 * 

* When coupled with the appropriate {@link KeyedPoolableObjectFactory}, * GenericKeyedObjectPool provides robust pooling functionality for * keyed objects. A GenericKeyedObjectPool 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 GenericKeyedObjectPool. *

*

A GenericKeyedObjectPool provides a number of configurable * parameters:

*
    *
  • * {@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. *
  • *
  • * {@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 maxTotal 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). *
  • *
  • * {@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. *
  • *
  • * {@link #setWhenExhaustedAction whenExhaustedAction} specifies the * behavior of the {@link #borrowObject borrowObject} method when a keyed * pool is exhausted: *
      *
    • * When {@link #setWhenExhaustedAction whenExhaustedAction} is * {@link #WHEN_EXHAUSTED_FAIL}, {@link #borrowObject borrowObject} will throw * a {@link NoSuchElementException} *
    • *
    • * 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.) *
    • *
    • * 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. *
    • *
    * The default whenExhaustedAction setting is * {@link #WHEN_EXHAUSTED_BLOCK}. *
  • *
  • * 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 false. *
  • *
  • * 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 false. *
  • *
*

* 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: *

    *
  • * {@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). *
  • *
  • * {@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 * timeBetweenEvictionRunsMillis > 0. The default setting * for this parameter is 30 minutes. *
  • *
  • * {@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 * timeBetweenEvictionRunsMillis > 0. The default setting * for this parameter is false. *
  • *
  • * {@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 * timeBetweenEvictionRunsMillis > 0, each time the idle object * eviction thread runs, it will try to create enough idle instances so that * there will be minIdle idle instances available under each * key. This parameter is also used by {@link #preparePool preparePool} * if true is provided as that method's * populateImmediately parameter. The default setting for this * parameter is 0. *
  • *
*

* 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. *

    *
  • * {@link #setLifo Lifo} * determines whether or not the pools return idle objects in * last-in-first-out order. The default setting for this parameter is * true. *
  • *
*

* GenericKeyedObjectPool is not usable without a {@link KeyedPoolableObjectFactory}. A * non-null factory must be provided either as a constructor argument * or via a call to {@link #setFactory setFactory} before the pool is used. *

*

* 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. *

* * @param the type of keys in this pool * @param the type of objects held in this pool * * @see GenericObjectPool * @author Rodney Waldhoff * @author Dirk Verbeeck * @author Sandy McArthur * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $ * @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 GenericKeyedObjectPool 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy * objects if not null */ 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @param config a non-null {@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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 GenericKeyedObjectPool using the specified values. * @param factory the KeyedPoolableObjectFactory to use to create, validate, and destroy objects * if not null * @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 * whenExhaustedAction 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 void setMaxActive(int maxActive) { synchronized(this) { _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 maxTotal 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 void setMaxTotal(int maxTotal) { synchronized(this) { _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 void setWhenExhaustedAction(byte whenExhaustedAction) { synchronized(this) { 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."); } } allocate(); } /** * 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 void setMaxWait(long maxWait) { synchronized(this) { _maxWait = maxWait; } allocate(); } /** * 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 void setMaxIdle(int maxIdle) { synchronized(this) { _maxIdle = maxIdle; } allocate(); } /** * Sets the minimum number of idle objects to maintain in each of the keyed * pools. This setting has no effect unless * timeBetweenEvictionRunsMillis > 0 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 * timeBetweenEvictionRunsMillis > 0 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 true, objects will be * {@link org.apache.commons.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 true if objects are validated before being borrowed. * @see #setTestOnBorrow */ public boolean getTestOnBorrow() { return _testOnBorrow; } /** * When true, objects will be * {@link org.apache.commons.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 true, objects will be * {@link org.apache.commons.pool.PoolableObjectFactory#validateObject validated} * before being returned to the pool within the * {@link #returnObject}. * * @return true when objects will be validated before being returned. * @see #setTestOnReturn */ public boolean getTestOnReturn() { return _testOnReturn; } /** * When true, objects will be * {@link org.apache.commons.pool.PoolableObjectFactory#validateObject validated} * before being returned to the pool within the * {@link #returnObject}. * * @param testOnReturn true 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). *

* When a negative value is supplied, * ceil({@link #getNumIdle()})/abs({@link #getNumTestsPerEvictionRun}) * tests will be run. I.e., when the value is -n, roughly one nth 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 true, objects will be * {@link org.apache.commons.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 true when objects are validated when borrowed. * @see #setTestWhileIdle * @see #setTimeBetweenEvictionRunsMillis */ public synchronized boolean getTestWhileIdle() { return _testWhileIdle; } /** * When true, objects will be * {@link org.apache.commons.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 true 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 true 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 ------------------------------------------ /** *

Borrows an object from the keyed pool associated with the given key.

* *

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.

* *

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 maxActive and * the total number of instances in circulation (under all keys) is less than maxTotal, a new instance * is created, activated and (if applicable) validated and returned to the caller.

* *

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 NoSuchElementException * ({@link #WHEN_EXHAUSTED_FAIL}), or grow ({@link #WHEN_EXHAUSTED_GROW} - ignoring maxActive, maxTotal properties). * The length of time that this method will block when whenExhaustedAction == WHEN_EXHAUSTED_BLOCK * is determined by the {@link #getMaxWait() maxWait} property.

* *

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.

* * @param key pool key * @return object instance from the keyed pool * @throws NoSuchElementException if a keyed object instance cannot be returned. */ @Override public V borrowObject(K 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; } } // see if we were awakened by a closing pool if(isClosed() == true) { throw new IllegalStateException("Pool closed"); } } catch(InterruptedException e) { boolean doAllocate = false; synchronized (this) { // Need to handle the all three possibilities if (latch.getPair() == null && !latch.mayCreate()) { // Case 1: latch still in allocation queue // Remove latch from the allocation queue _allocationQueue.remove(latch); } else if (latch.getPair() == null && latch.mayCreate()) { // Case 2: latch has been given permission to create // a new object latch.getPool().decrementInternalProcessingCount(); doAllocate = true; } else { // Case 3: An object has been allocated latch.getPool().decrementInternalProcessingCount(); latch.getPool().incrementActiveCount(); returnObject(latch.getkey(), latch.getPair().getValue()); } } if (doAllocate) { allocate(); } 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 { V 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. This method MUST NOT be called * from inside a sync block. */ 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 = allocationQueueIter.next(); ObjectQueue pool = (_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( 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. * *

Implementation notes: *

  • This method does not destroy or effect in any way instances that are * checked out when it is invoked.
  • *
  • 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.
  • *
  • Exceptions encountered destroying idle instances are swallowed.

*/ @Override public void clear() { Map>> toDestroy = new HashMap>>(); synchronized (this) { for (Iterator it = _poolMap.keySet().iterator(); it.hasNext();) { K key = it.next(); ObjectQueue pool = _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, K> map = new TreeMap, K>(); synchronized (this) { for (Iterator keyiter = _poolMap.keySet().iterator(); keyiter.hasNext();) { final K key = keyiter.next(); final List> list = _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, K>> setPairKeys = map.entrySet(); int itemsToRemove = ((int) (map.size() * 0.15)) + 1; Iterator, K>> iter = setPairKeys.iterator(); while (iter.hasNext() && itemsToRemove > 0) { Entry, K> 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. K key = entry.getValue(); ObjectTimestampPair pairTimeStamp = entry.getKey(); ObjectQueue objectQueue = _poolMap.get(key); final List> list = objectQueue.queue; list.remove(pairTimeStamp); if (toDestroy.containsKey(key)) { toDestroy.get(key).add(pairTimeStamp); } else { List> listForKey = new ArrayList>(); listForKey.add(pairTimeStamp); toDestroy.put(key, listForKey); } objectQueue.incrementInternalProcessingCount(); _totalIdle--; itemsToRemove--; } } destroy(toDestroy, _factory); } /** * Clears the specified pool, removing all pooled instances corresponding to the given key. * * @param key the key to clear */ @Override public void clear(K key) { Map>> toDestroy = new HashMap>>(); final ObjectQueue pool; synchronized (this) { pool = _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>, 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();) { Entry>> entry = entries.next(); K key = entry.getKey(); List> c = entry.getValue(); for (Iterator> it = c.iterator(); it.hasNext();) { try { factory.destroyObject( key,it.next().value); } catch(Exception e) { // ignore error, keep destroying the rest } finally { synchronized(this) { ObjectQueue objectQueue = _poolMap.get(key); if (objectQueue != null) { objectQueue.decrementInternalProcessingCount(); if (objectQueue.internalProcessingCount == 0 && objectQueue.activeCount == 0 && objectQueue.queue.isEmpty()) { _poolMap.remove(key); _poolList.remove(key); } } else { _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 */ @Override 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 */ @Override 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 key. * * @param key the key to query * @return the number of instances corresponding to the given key currently borrowed in this pool */ @Override public synchronized int getNumActive(Object key) { final ObjectQueue pool = (_poolMap.get(key)); return pool != null ? pool.activeCount : 0; } /** * Returns the number of instances corresponding to the given key currently idle in this pool. * * @param key the key to query * @return the number of instances corresponding to the given key currently idle in this pool */ @Override public synchronized int getNumIdle(Object key) { final ObjectQueue pool = (_poolMap.get(key)); return pool != null ? pool.queue.size() : 0; } /** *

Returns an object to a keyed pool.

* *

For the pool to function correctly, the object instance must have been borrowed * from the pool (under the same key) and not yet returned. Repeated returnObject calls on * the same object/key pair (with no borrowObject calls in between) will result in multiple * references to the object in the idle instance pool.

* *

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.

* *

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.

* * @param key pool key * @param obj instance to return to the keyed pool * @throws Exception */ @Override public void returnObject(K key, V 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 = (_poolMap.get(key)); if (pool != null) { synchronized(this) { pool.decrementActiveCount(); if (pool.queue.isEmpty() && pool.activeCount == 0 && pool.internalProcessingCount == 0) { _poolMap.remove(key); _poolList.remove(key); } } allocate(); } } } } /** *

Adds an object to the keyed pool.

* *

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.

* *

Calls {@link #allocate()} on successful completion

* * @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(K key, V 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) boolean doAllocate = false; synchronized (this) { // grab the pool (list) of objects associated with the given key pool = _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(); } doAllocate = true; } } } if (doAllocate) { 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(); if (pool.queue.isEmpty() && pool.activeCount == 0 && pool.internalProcessingCount == 0) { _poolMap.remove(key); _poolList.remove(key); } } allocate(); } } } /** * {@inheritDoc} *

Activation of this method decrements the active count associated with the given keyed pool * and attempts to destroy obj.

* * @param key pool key * @param obj instance to invalidate * @throws Exception if an exception occurs destroying the object */ @Override public void invalidateObject(K key, V obj) throws Exception { try { _factory.destroyObject(key, obj); } finally { synchronized (this) { ObjectQueue pool = (_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. * addObject 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. */ @Override public void addObject(K key) throws Exception { assertOpen(); if (_factory == null) { throw new IllegalStateException("Cannot add objects without a factory."); } V 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 populateImmediately is true and * minIdle > 0, the pool under the given key will be * populated immediately with minIdle idle instances. * * @param key - The key to register for pool control. * @param populateImmediately - If this is true, the pool * will be populated immediately. * @since Pool 1.3 */ public synchronized void preparePool(K key, boolean populateImmediately) { ObjectQueue pool = (_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 } } } /** *

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.

* *

Destroys idle instances in the pool by invoking {@link #clear()}.

* * @throws Exception */ @Override 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); while(_allocationQueue.size() > 0) { Latch l = _allocationQueue.removeFirst(); synchronized (l) { // notify the waiting thread l.notify(); } } } } /** *

Sets the keyed poolable object factory associated with this pool.

* *

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.

* * @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 */ @Deprecated @Override 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();) { K key = it.next(); ObjectQueue pool = _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); } /** *

Perform numTests idle object eviction tests, evicting * examined objects that meet the criteria for eviction. If * testWhileIdle is true, examined objects are validated * when visited (and removed if invalid); otherwise only objects that * have been idle for more than minEvicableIdletimeMillis * are removed.

* *

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.

* * @throws Exception when there is a problem evicting idle objects. */ public void evict() throws Exception { K 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 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 ? _evictionCursor.previous() : _evictionCursor.next(); _evictionCursor.remove(); ObjectQueue objectQueue = _poolMap.get(key); objectQueue.incrementInternalProcessingCount(); _totalIdle--; } 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 } } synchronized (this) { ObjectQueue objectQueue = _poolMap.get(key); objectQueue.decrementInternalProcessingCount(); if (removeObject) { if (objectQueue.queue.isEmpty() && objectQueue.activeCount == 0 && objectQueue.internalProcessingCount == 0) { _poolMap.remove(key); _poolList.remove(key); } } else { _evictionCursor.add(pair); _totalIdle++; if (_lifo) { // Skip over the element we just added back _evictionCursor.previous(); } } } } allocate(); } /** * 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 = _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. */ @SuppressWarnings("unchecked") 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((K)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(K key) throws Exception { // Calculate current pool objects ObjectQueue pool; synchronized(this) { pool = (_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 * delay 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()) { K 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 _numTestsPerEvictionRun * and _totalIdle. * * @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 * @deprecated this field will be made private and final in version 2.0 */ @Deprecated T value; /** * timestamp * @deprecated this field will be made private and final in version 2.0 */ @Deprecated long tstamp; //CHECKSTYLE: resume VisibilityModifier /** * Create a new ObjectTimestampPair using the given object and the current system time. * @param val object instance */ ObjectTimestampPair(T 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(T val, long time) { value = val; tstamp = time; } /** * Returns a string representation. * * @return String representing this ObjectTimestampPair */ @Override 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 */ @SuppressWarnings("unchecked") 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 T 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()}. */ @Override 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 GenericKeyedObjectPool. * @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 final class Latch { /** key of associated pool */ private final LK _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(LK key) { _key = key; } /** * Retuns the key of the associated pool * @return associated pool key */ private synchronized LK 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 true, objects will be * {@link org.apache.commons.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 true, objects will be * {@link org.apache.commons.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 true, objects will be * {@link org.apache.commons.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). *

* When a negative value is supplied, ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun}) * tests will be run. I.e., when the value is -n, roughly one nth 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>(); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy