/*
* Copyright 1999-2004,2006 The Apache Software Foundation.
*
* Licensed 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 com.frameworkset.commons.pool.impl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TimerTask;
import java.util.TreeMap;
import com.frameworkset.commons.pool.BaseKeyedObjectPool;
import com.frameworkset.commons.pool.KeyedObjectPool;
import com.frameworkset.commons.pool.KeyedPoolableObjectFactory;
/**
* A configurable {@link KeyedObjectPool} implementation.
* <p>
* When coupled with the appropriate {@link KeyedPoolableObjectFactory},
* <tt>GenericKeyedObjectPool</tt> provides robust pooling functionality for
* arbitrary objects.
* <p>
* A <tt>GenericKeyedObjectPool</tt> provides a number of configurable parameters:
* <ul>
* <li>
* {@link #setMaxActive <i>maxActive</i>} controls the maximum number of objects (per key)
* that can be borrowed from the pool at one time. When non-positive, there
* is no limit to the number of objects that may be active at one time.
* When {@link #setMaxActive <i>maxActive</i>} is exceeded, the pool is said to be exhausted.
* </li>
* <li>
* {@link #setMaxIdle <i>maxIdle</i>} 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 at one time.
* </li>
* <li>
* {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} specifies the
* behaviour of the {@link #borrowObject} method when the pool is exhausted:
* <ul>
* <li>
* When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} is
* {@link #WHEN_EXHAUSTED_FAIL}, {@link #borrowObject} will throw
* a {@link NoSuchElementException}
* </li>
* <li>
* When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} is
* {@link #WHEN_EXHAUSTED_GROW}, {@link #borrowObject} will create a new
* object and return it(essentially making {@link #setMaxActive <i>maxActive</i>}
* meaningless.)
* </li>
* <li>
* When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>}
* is {@link #WHEN_EXHAUSTED_BLOCK}, {@link #borrowObject} will block
* (invoke {@link Object#wait} until a new or idle object is available.
* If a positive {@link #setMaxWait <i>maxWait</i>}
* value is supplied, the {@link #borrowObject} will block for at
* most that many milliseconds, after which a {@link NoSuchElementException}
* will be thrown. If {@link #setMaxWait <i>maxWait</i>} is non-positive,
* the {@link #borrowObject} method will block indefinitely.
* </li>
* </ul>
* </li>
* <li>
* When {@link #setTestOnBorrow <i>testOnBorrow</i>} is set, the pool will
* attempt to validate each object before it is returned from the
* {@link #borrowObject} method. (Using the provided factory's
* {@link KeyedPoolableObjectFactory#validateObject} method.) Objects that fail
* to validate will be dropped from the pool, and a different object will
* be borrowed.
* </li>
* <li>
* When {@link #setTestOnReturn <i>testOnReturn</i>} is set, the pool will
* attempt to validate each object before it is returned to the pool in the
* {@link #returnObject} method. (Using the provided factory's
* {@link KeyedPoolableObjectFactory#validateObject}
* method.) Objects that fail to validate will be dropped from the pool.
* </li>
* </ul>
* <p>
* Optionally, one may configure the pool to examine and possibly evict objects as they
* sit idle in the pool. This is performed by an "idle object eviction" thread, which
* runs asychronously. The idle object eviction thread may be configured using the
* following attributes:
* <ul>
* <li>
* {@link #setTimeBetweenEvictionRunsMillis <i>timeBetweenEvictionRunsMillis</i>}
* indicates how long the eviction thread should sleep before "runs" of examining
* idle objects. When non-positive, no eviction thread will be launched.
* </li>
* <li>
* {@link #setMinEvictableIdleTimeMillis <i>minEvictableIdleTimeMillis</i>}
* specifies the minimum amount of time that an object may sit idle in the pool
* before it is eligable for eviction due to idle time. When non-positive, no object
* will be dropped from the pool due to idle time alone.
* </li>
* <li>
* {@link #setTestWhileIdle <i>testWhileIdle</i>} indicates whether or not idle
* objects should be validated using the factory's
* {@link KeyedPoolableObjectFactory#validateObject} method. Objects
* that fail to validate will be dropped from the pool.
* </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} before the pool is used.
* </p>
* @see GenericObjectPool
* @author Rodney Waldhoff
* @author Dirk Verbeeck
* @version $Revision: 386116 $ $Date: 2006-03-15 12:15:58 -0500 (Wed, 15 Mar 2006) $
*/
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 in the pool
* (per key).
* @see #getMaxIdle
* @see #setMaxIdle
*/
public static final int DEFAULT_MAX_IDLE = 8;
/**
* The default cap on the total number of active instances from the pool
* (per key).
* @see #getMaxActive
* @see #setMaxActive
*/
public static final int DEFAULT_MAX_ACTIVE = 8;
/**
* The default cap on the the maximum number of objects that can exists 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 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}.
* @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.
* @see #setMinIdle
* @see #getMinIdle
*/
public static final int DEFAULT_MIN_IDLE = 0;
//--- constructors -----------------------------------------------
/**
* Create a new <tt>GenericKeyedObjectPool</tt>..
*/
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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
*/
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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
* @param config a non-<tt>null</tt> {@link GenericKeyedObjectPool.Config} describing my configuration
*/
public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, GenericKeyedObjectPool.Config config) {
this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.maxTotal,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
}
/**
* Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (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 an and <i>whenExhaustedAction</i> 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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (see {@link #setMaxActive})
* @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> 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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (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 an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
* @param maxIdle the maximum number of idle objects in my pool (per key) (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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (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 an and <i>whenExhaustedAction</i> 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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (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 an and <i>whenExhaustedAction</i> 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 eligable for evcition (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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (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 an and <i>whenExhaustedAction</i> 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 eligable for evcition (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 <tt>GenericKeyedObjectPool</tt> using the specified values.
* @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
* @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (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 an and <i>whenExhaustedAction</i> 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 eligable for evcition (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, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
_factory = factory;
_maxActive = maxActive;
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();
_activeMap = new HashMap();
startEvictor(_timeBetweenEvictionRunsMillis);
}
//--- public methods ---------------------------------------------
//--- configuration methods --------------------------------------
/**
* Returns the cap on the number of active instances from my pool (per key).
* @return the cap on the number of active instances from my pool (per key).
* @see #setMaxActive
*/
public synchronized int getMaxActive() {
return _maxActive;
}
/**
* Sets the cap on the number of active instances from my pool (per key).
* @param maxActive The cap on the number of active instances from my pool (per key).
* Use a negative value for an infinite number of instances.
* @see #getMaxActive
*/
public synchronized void setMaxActive(int maxActive) {
_maxActive = maxActive;
notifyAll();
}
/**
* Returns the cap on the total number of instances from my pool if non-positive.
* @return the cap on the total number of instances from my pool if non-positive.
* @see #setMaxTotal
*/
public synchronized int getMaxTotal() {
return _maxTotal;
}
/**
* Sets the cap on the total number of instances from my pool if non-positive.
* @param maxTotal The cap on the total number of instances from my pool.
* Use a non-positive value for an infinite number of instances.
* @see #getMaxTotal
*/
public synchronized void setMaxTotal(int maxTotal) {
_maxTotal = maxTotal;
notifyAll();
}
/**
* 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;
notifyAll();
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 0, the {@link #borrowObject} method
* may block indefinitely.
*
* @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 0, the {@link #borrowObject} method
* may block 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 in the pool.
* @return the cap on the number of "idle" instances in the pool.
* @see #setMaxIdle
*/
public synchronized int getMaxIdle() {
return _maxIdle;
}
/**
* Sets the cap on the number of "idle" instances in the pool.
* @param maxIdle The cap on the number of "idle" instances in the pool.
* Use a negative value to indicate an unlimited number
* of idle instances.
* @see #getMaxIdle
*/
public synchronized void setMaxIdle(int maxIdle) {
_maxIdle = maxIdle;
notifyAll();
}
/**
* Sets the minimum number of idle objects in pool to maintain (per key)
* @param poolSize - The minimum size of the pool
* @see #getMinIdle
*/
public synchronized void setMinIdle(int poolSize) {
_minIdle = poolSize;
}
/**
* Returns the minimum number of idle objects in pool to maintain (per key)
* @return the minimum number of idle objects in pool to maintain (per key)
* @see #setMinIdle
*/
public synchronized int getMinIdle() {
return _minIdle;
}
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.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
*/
public synchronized boolean getTestOnBorrow() {
return _testOnBorrow;
}
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.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 #getTestOnBorrow
*/
public synchronized void setTestOnBorrow(boolean testOnBorrow) {
_testOnBorrow = testOnBorrow;
}
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.commons.pool.PoolableObjectFactory#validateObject validated}
* before being returned to the pool within the
* {@link #returnObject}.
*
* @see #setTestOnReturn
*/
public synchronized boolean getTestOnReturn() {
return _testOnReturn;
}
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.commons.pool.PoolableObjectFactory#validateObject validated}
* before being returned to the pool within the
* {@link #returnObject}.
*
* @see #getTestOnReturn
*/
public synchronized 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.
*
* @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.
*
* @see #getTimeBetweenEvictionRunsMillis
*/
public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
_timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
startEvictor(_timeBetweenEvictionRunsMillis);
}
/**
* Returns the number of objects to examine during each run of the
* idle object evictor thread (if any).
*
* @see #setNumTestsPerEvictionRun
* @see #setTimeBetweenEvictionRunsMillis
*/
public synchronized int getNumTestsPerEvictionRun() {
return _numTestsPerEvictionRun;
}
/**
* Sets the number of objects to examine during each run of the
* idle object evictor thread (if any).
* <p>
* When a negative value is supplied, <tt>ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun})</tt>
* tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
* idle objects will be tested per run.
*
* @see #getNumTestsPerEvictionRun
* @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 eligable for eviction by the idle object evictor
* (if any).
*
* @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 eligable 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 #getMinEvictableIdleTimeMillis
* @see #setTimeBetweenEvictionRunsMillis
*/
public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
_minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.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 #setTimeBetweenEvictionRunsMillis
*/
public synchronized boolean getTestWhileIdle() {
return _testWhileIdle;
}
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.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 #getTestWhileIdle
* @see #setTimeBetweenEvictionRunsMillis
*/
public synchronized void setTestWhileIdle(boolean testWhileIdle) {
_testWhileIdle = testWhileIdle;
}
/**
* Sets my configuration.
* @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);
}
//-- ObjectPool methods ------------------------------------------
public synchronized Object borrowObject(Object key) throws Exception {
long starttime = System.currentTimeMillis();
boolean newlyCreated = false;
for(;;) {
LinkedList pool = (LinkedList)(_poolMap.get(key));
if(null == pool) {
pool = new LinkedList();
_poolMap.put(key,pool);
}
ObjectTimestampPair pair = null;
// if there are any sleeping, just grab one of those
try {
pair = (ObjectTimestampPair)(pool.removeFirst());
if(null != pair) {
_totalIdle--;
}
} catch(NoSuchElementException e) { /* ignored */
}
// otherwise
if(null == pair) {
// if there is a totalMaxActive and we are at the limit then
// we have to make room
if ((_maxTotal > 0) && (_totalActive + _totalIdle >= _maxTotal)) {
clearOldest();
}
// check if we can create one
// (note we know that the num sleeping is 0, else we wouldn't be here)
int active = getActiveCount(key);
if ((_maxActive < 0 || active < _maxActive) &&
(_maxTotal < 0 || _totalActive + _totalIdle < _maxTotal)) {
Object obj = _factory.makeObject(key);
pair = new ObjectTimestampPair(obj);
newlyCreated = true;
} else {
// the pool is exhausted
switch(_whenExhaustedAction) {
case WHEN_EXHAUSTED_GROW:
Object obj = _factory.makeObject(key);
pair = new ObjectTimestampPair(obj);
break;
case WHEN_EXHAUSTED_FAIL:
throw new NoSuchElementException();
case WHEN_EXHAUSTED_BLOCK:
try {
if(_maxWait <= 0) {
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)
{
wait(waitTime);
}
}
} catch(InterruptedException e) {
// ignored
}
if(_maxWait > 0 && ((System.currentTimeMillis() - starttime) >= _maxWait)) {
throw new NoSuchElementException("Timeout waiting for idle object");
} else {
continue; // keep looping
}
default:
throw new IllegalArgumentException("whenExhaustedAction " + _whenExhaustedAction + " not recognized.");
}
}
}
_factory.activateObject(key,pair.value);
if(_testOnBorrow && !_factory.validateObject(key,pair.value)) {
_factory.destroyObject(key,pair.value);
if(newlyCreated) {
throw new NoSuchElementException("Could not create a validated object");
} // else keep looping
} else {
incrementActiveCount(key);
return pair.value;
}
}
}
public synchronized void clear() {
for(Iterator keyiter = _poolMap.keySet().iterator(); keyiter.hasNext(); ) {
Object key = keyiter.next();
final LinkedList list = (LinkedList)(_poolMap.get(key));
for(Iterator it = list.iterator(); it.hasNext(); ) {
try {
_factory.destroyObject(key,((ObjectTimestampPair)(it.next())).value);
} catch(Exception e) {
// ignore error, keep destroying the rest
}
it.remove();
}
}
_poolMap.clear();
if (_recentlyEvictedKeys != null) {
_recentlyEvictedKeys.clear();
}
_totalIdle = 0;
notifyAll();
}
/**
* Method clears oldest 15% of objects in pool. The method sorts the
* objects into a TreeMap and then iterates the first 15% for removal
*/
public synchronized void clearOldest() {
// build sorted map of idle objects
TreeMap map = new TreeMap();
for (Iterator keyiter = _poolMap.keySet().iterator(); keyiter.hasNext();) {
Object key = keyiter.next();
LinkedList list = (LinkedList) _poolMap.get(key);
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.
ObjectTimestampPair pair = (ObjectTimestampPair) it.next();
map.put(pair, 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();
LinkedList list = (LinkedList) _poolMap.get(key);
list.remove(pairTimeStamp);
try {
_factory.destroyObject(key, pairTimeStamp.value);
} catch (Exception e) {
// ignore error, keep destroying the rest
}
// if that was the last object for that key, drop that pool
if (list.isEmpty()) {
_poolMap.remove(key);
}
_totalIdle--;
itemsToRemove--;
}
notifyAll();
}
public synchronized void clear(Object key) {
LinkedList pool = (LinkedList)(_poolMap.remove(key));
if(null == pool) {
return;
} else {
for(Iterator it = pool.iterator(); it.hasNext(); ) {
try {
_factory.destroyObject(key,((ObjectTimestampPair)(it.next())).value);
} catch(Exception e) {
// ignore error, keep destroying the rest
}
it.remove();
_totalIdle--;
}
}
notifyAll();
}
public synchronized int getNumActive() {
return _totalActive;
}
public synchronized int getNumIdle() {
return _totalIdle;
}
public synchronized int getNumActive(Object key) {
return getActiveCount(key);
}
public synchronized int getNumIdle(Object key) {
try {
return((LinkedList)(_poolMap.get(key))).size();
} catch(Exception e) {
return 0;
}
}
public synchronized void returnObject(Object key, Object obj) 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;
try {
_factory.destroyObject(key, obj);
} catch(Exception e) {
// ignored
}
} else {
try {
_factory.passivateObject(key, obj);
} catch(Exception e) {
success = false;
}
}
boolean shouldDestroy = false;
// grab the pool (list) of objects associated with the given key
LinkedList pool = (LinkedList) (_poolMap.get(key));
// if it doesn't exist, create it
if(null == pool) {
pool = new LinkedList();
_poolMap.put(key, pool);
}
decrementActiveCount(key);
// if there's no space in the pool, flag the object for destruction
// else if we passivated succesfully, return it to the pool
if(_maxIdle >= 0 && (pool.size() >= _maxIdle)) {
shouldDestroy = true;
} else if(success) {
pool.addLast(new ObjectTimestampPair(obj));
_totalIdle++;
}
notifyAll();
if(shouldDestroy) {
try {
_factory.destroyObject(key, obj);
} catch(Exception e) {
// ignored?
}
}
}
public synchronized void invalidateObject(Object key, Object obj) throws Exception {
try {
_factory.destroyObject(key, obj);
}
finally {
decrementActiveCount(key);
notifyAll(); // _totalActive has changed
}
}
public synchronized void addObject(Object key) throws Exception {
Object obj = _factory.makeObject(key);
incrementActiveCount(key); // returnObject will decrement this
returnObject(key,obj);
}
/**
* Registers a key for pool control.
*
* If <i>populateImmediately</i> is <code>true</code>, the pool will immediately commence
* a sustain cycle. If <i>populateImmediately</i> is <code>false</code>, the pool will be
* populated when the next schedules sustain task is run.
*
* @param key - The key to register for pool control.
* @param populateImmediately - If this is <code>true</code>, the pool
* will start a sustain cycle immediately.
*/
public synchronized void preparePool(Object key, boolean populateImmediately) {
LinkedList pool = (LinkedList)(_poolMap.get(key));
if (null == pool) {
pool = new LinkedList();
_poolMap.put(key,pool);
}
if (populateImmediately) {
try {
// Create the pooled objects
ensureMinIdle(key);
}
catch (Exception e) {
//Do nothing
}
}
}
public synchronized void close() throws Exception {
clear();
_poolMap = null;
_activeMap = null;
_recentlyEvictedKeys = null;
if(null != _evictor) {
_evictor.cancel();
_evictor = null;
}
}
public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException {
if(0 < getNumActive()) {
throw new IllegalStateException("Objects are already active");
} else {
clear();
_factory = factory;
}
}
public synchronized void evict() throws Exception {
Object key = null;
if (_recentlyEvictedKeys == null) {
_recentlyEvictedKeys = new HashSet(_poolMap.size());
}
Set remainingKeys = new HashSet(_poolMap.keySet());
remainingKeys.removeAll(_recentlyEvictedKeys);
Iterator keyIter = remainingKeys.iterator();
ListIterator objIter = null;
for(int i=0,m=getNumTests();i<m;i++) {
if(_poolMap.size() > 0) {
// Find next idle object pool key to work on
if (key == null) {
if (!keyIter.hasNext()) {
_recentlyEvictedKeys.clear();
remainingKeys = new HashSet(_poolMap.keySet());
keyIter = remainingKeys.iterator();
}
if (!keyIter.hasNext()) {
// done, there are no keyed pools
return;
}
key = keyIter.next();
}
// if we don't have a keyed object pool iterator
if (objIter == null) {
final LinkedList list = (LinkedList)_poolMap.get(key);
if (_evictLastIndex < 0 || _evictLastIndex > list.size()) {
_evictLastIndex = list.size();
}
objIter = list.listIterator(_evictLastIndex);
}
// if the _evictionCursor has a previous object, then test it
if(objIter.hasPrevious()) {
ObjectTimestampPair pair = (ObjectTimestampPair)(objIter.previous());
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 {
objIter.remove();
_totalIdle--;
_factory.destroyObject(key,pair.value);
// Do not remove the key from the _poolList or _poolmap, even if the list
// stored in the _poolMap for this key is empty when the
// {@link #getMinIdle <i>minIdle</i>} is > 0.
//
// Otherwise if it was the last object for that key, drop that pool
if ((_minIdle == 0) && (((LinkedList)(_poolMap.get(key))).isEmpty())) {
_poolMap.remove(key);
}
} catch(Exception e) {
; // ignored
}
}
} else {
// else done evicting keyed pool
_recentlyEvictedKeys.add(key);
_evictLastIndex = -1;
objIter = null;
}
}
}
}
/**
* 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 synchronized void ensureMinIdle() throws Exception {
Iterator iterator = _poolMap.keySet().iterator();
//Check if should sustain the pool
if (_minIdle > 0) {
// 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
// Increment idle count + 1
while (iterator.hasNext()) {
//Get the next key to process
Object key = iterator.next();
ensureMinIdle(key);
}
}
}
/**
* Re-creates any needed objects to maintain the minimum levels of
* pooled objects for the specified key.
*
* This method uses {@link #calculateDefecit} to calculate the number
* of objects to be created. {@link #calculateDefecit} 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 synchronized void ensureMinIdle(Object key) throws Exception {
// Calculate current pool objects
int numberToCreate = calculateDefecit(key);
//Create required pool objects, if none to create, this loop will not be run.
for (int i = 0; i < numberToCreate; i++) {
addObject(key);
}
}
//--- non-public methods ----------------------------------------
/**
* Start the eviction thread or service, or when
* <i>delay</i> is non-positive, stop it
* if it is already running.
*/
protected synchronized void startEvictor(long delay) {
if(null != _evictor) {
_evictor.cancel();
_evictor = null;
}
if(delay > 0) {
_evictor = new Evictor();
GenericObjectPool.EVICTION_TIMER.schedule(_evictor, delay, delay);
}
}
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()) {
buf.append("\t").append(_poolMap.get(it.next())).append("\n");
}
return buf.toString();
}
private int getNumTests() {
if(_numTestsPerEvictionRun >= 0) {
return _numTestsPerEvictionRun;
} else {
return(int)(Math.ceil((double)_totalIdle/Math.abs((double)_numTestsPerEvictionRun)));
}
}
private void incrementActiveCount(Object key) {
_totalActive++;
Integer active = (Integer)(_activeMap.get(key));
if(null == active) {
_activeMap.put(key,new Integer(1));
} else {
_activeMap.put(key,new Integer(active.intValue() + 1));
}
}
private void decrementActiveCount(Object key) {
_totalActive--;
Integer active = (Integer)(_activeMap.get(key));
if(null == active) {
// do nothing, either null or zero is OK
} else if(active.intValue() <= 1) {
_activeMap.remove(key);
} else {
_activeMap.put(key, new Integer(active.intValue() - 1));
}
}
private int getActiveCount(Object key) {
int active = 0;
Integer act = (Integer)(_activeMap.get(key));
if(null != act) {
active = act.intValue();
}
return active;
}
/**
* This returns the number of objects to create during the pool
* sustain cycle. This will ensure that the minimum number of idle
* connections is maintained without going past the maxPool value.
* <p>
* This method has been left public so derived classes can override
* the way the defecit is calculated. ie... Increase/decrease the pool
* size at certain times of day to accomodate for usage patterns.
*
* @param key - The key of the pool to calculate the number of
* objects to be re-created
* @return The number of objects to be created
*/
private int calculateDefecit(Object key) {
int objectDefecit = 0;
//Calculate no of objects needed to be created, in order to have
//the number of pooled objects < maxActive();
objectDefecit = getMinIdle() - getNumIdle(key);
if (getMaxActive() > 0) {
int growLimit = Math.max(0, getMaxActive() - getNumActive(key) - getNumIdle(key));
objectDefecit = Math.min(objectDefecit, growLimit);
}
// Take the maxTotal limit into account
if (getMaxTotal() > 0) {
int growLimit = Math.max(0, getMaxTotal() - getNumActive() - getNumIdle());
objectDefecit = Math.min(objectDefecit, growLimit);
}
return objectDefecit;
}
//--- inner classes ----------------------------------------------
/**
* 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 {
Object value;
long tstamp;
ObjectTimestampPair(Object val) {
this(val, System.currentTimeMillis());
}
ObjectTimestampPair(Object val, long time) {
value = val;
tstamp = time;
}
public String toString() {
return value + ";" + tstamp;
}
public int compareTo(Object obj) {
return compareTo((ObjectTimestampPair) obj);
}
public int compareTo(ObjectTimestampPair other) {
return (int) (this.tstamp - other.tstamp);
}
}
/**
* The idle object evictor {@link TimerTask}.
* @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
*/
private class Evictor extends TimerTask {
public void run() {
//Evict from the pool
try {
evict();
} catch(Exception e) {
// ignored
}
//Re-create the connections.
try {
ensureMinIdle();
} catch (Exception e) {
// ignored
}
}
}
/**
* A simple "struct" encapsulating the
* configuration information for a {@link GenericKeyedObjectPool}.
* @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory,GenericKeyedObjectPool.Config)
* @see GenericKeyedObjectPool#setConfig
*/
public static class Config {
public int maxIdle = GenericKeyedObjectPool.DEFAULT_MAX_IDLE;
public int maxActive = GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE;
public int maxTotal = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
public int minIdle = GenericKeyedObjectPool.DEFAULT_MIN_IDLE;
public long maxWait = GenericKeyedObjectPool.DEFAULT_MAX_WAIT;
public byte whenExhaustedAction = GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
public boolean testOnBorrow = GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW;
public boolean testOnReturn = GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN;
public boolean testWhileIdle = GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE;
public long timeBetweenEvictionRunsMillis = GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
public int numTestsPerEvictionRun = GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
public long minEvictableIdleTimeMillis = GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
}
//--- protected attributes ---------------------------------------
/**
* The cap on the number of idle instances in the pool (per key).
* @see #setMaxIdle
* @see #getMaxIdle
*/
private int _maxIdle = DEFAULT_MAX_IDLE;
/**
* The minimum no of idle objects to keep in the pool (per key)
* @see #setMinIdle
* @see #getMinIdle
*/
private int _minIdle = DEFAULT_MIN_IDLE;
/**
* The cap on the number of active instances from the pool (per key).
* @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 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 <tt>true</tt>, objects will be
* {@link com.frameworkset.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 boolean _testOnBorrow = DEFAULT_TEST_ON_BORROW;
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.commons.pool.PoolableObjectFactory#validateObject validated}
* before being returned to the pool within the
* {@link #returnObject}.
*
* @see #getTestOnReturn
* @see #setTestOnReturn
*/
private boolean _testOnReturn = DEFAULT_TEST_ON_RETURN;
/**
* When <tt>true</tt>, objects will be
* {@link com.frameworkset.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).
* <p>
* When a negative value is supplied, <tt>ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun})</tt>
* tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>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 eligable 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 (CursorableLinkedLists). */
private HashMap _poolMap = null;
/** Count of active objects, per key. */
private HashMap _activeMap = null;
/** The total number of active instances. */
private int _totalActive = 0;
/** The total number of idle instances. */
private int _totalIdle = 0;
/** My {@link KeyedPoolableObjectFactory}. */
private KeyedPoolableObjectFactory _factory = null;
/**
* My idle object eviction {@link TimerTask}, if any.
*/
private Evictor _evictor = null;
/**
* Idle object pool keys that have been evicted recently.
*/
private Set _recentlyEvictedKeys = null;
/**
* Position in the _pool where the _evictor last stopped.
*/
private int _evictLastIndex = -1;
}