| /* |
| * 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.pool2.impl; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| import java.util.TimerTask; |
| import java.util.TreeMap; |
| |
| import org.apache.commons.pool2.BaseKeyedObjectPool; |
| import org.apache.commons.pool2.KeyedPoolableObjectFactory; |
| import org.apache.commons.pool2.PoolUtils; |
| |
| /** |
| * A configurable <code>KeyedObjectPool</code> implementation. |
| * <p> |
| * When coupled with the appropriate {@link KeyedPoolableObjectFactory}, |
| * <code>GenericKeyedObjectPool</code> provides robust pooling functionality for |
| * keyed objects. A <code>GenericKeyedObjectPool</code> can be viewed as a map |
| * of pools, keyed on the (unique) key values provided to the |
| * {@link #preparePool preparePool}, {@link #addObject addObject} or |
| * {@link #borrowObject borrowObject} methods. Each time a new key value is |
| * provided to one of these methods, a new pool is created under the given key |
| * to be managed by the containing <code>GenericKeyedObjectPool.</code> |
| * </p> |
| * <p>A <code>GenericKeyedObjectPool</code> provides a number of configurable |
| * parameters:</p> |
| * <ul> |
| * <li> |
| * {@link #setMaxActive maxActive} controls the maximum number of objects |
| * (per key) that can allocated by the pool (checked out to client threads, |
| * or idle in the pool) at one time. When non-positive, there is no limit |
| * to the number of objects per key. When {@link #setMaxActive maxActive} is |
| * reached, the keyed pool is said to be exhausted. The default setting for |
| * this parameter is 8. |
| * </li> |
| * <li> |
| * {@link #setMaxTotal maxTotal} sets a global limit on the number of objects |
| * that can be in circulation (active or idle) within the combined set of |
| * pools. When non-positive, there is no limit to the total number of |
| * objects in circulation. When {@link #setMaxTotal maxTotal} is exceeded, |
| * all keyed pools are exhausted. When <code>maxTotal</code> is set to a |
| * positive value and {@link #borrowObject borrowObject} is invoked |
| * when at the limit with no idle instances available, an attempt is made to |
| * create room by clearing the oldest 15% of the elements from the keyed |
| * pools. The default setting for this parameter is -1 (no limit). |
| * </li> |
| * <li> |
| * {@link #setMaxIdle maxIdle} controls the maximum number of objects that can |
| * sit idle in the pool (per key) at any time. When negative, there |
| * is no limit to the number of objects that may be idle per key. The |
| * default setting for this parameter is 8. |
| * </li> |
| * <li> |
| * {@link #setWhenExhaustedAction whenExhaustedAction} specifies the |
| * behavior of the {@link #borrowObject borrowObject} method when a keyed |
| * pool is exhausted: |
| * <ul> |
| * <li> |
| * When {@link #setWhenExhaustedAction whenExhaustedAction} is |
| * {@link WhenExhaustedAction#FAIL}, {@link #borrowObject borrowObject} will throw |
| * a {@link NoSuchElementException} |
| * </li> |
| * <li> |
| * When {@link #setWhenExhaustedAction whenExhaustedAction} is |
| * {@link WhenExhaustedAction#GROW}, {@link #borrowObject borrowObject} will create a new |
| * object and return it (essentially making {@link #setMaxActive maxActive} |
| * meaningless.) |
| * </li> |
| * <li> |
| * When {@link #setWhenExhaustedAction whenExhaustedAction} |
| * is {@link WhenExhaustedAction#BLOCK}, {@link #borrowObject borrowObject} will block |
| * (invoke {@link Object#wait() wait} until a new or idle object is available. |
| * If a positive {@link #setMaxWait maxWait} |
| * value is supplied, the {@link #borrowObject borrowObject} will block for at |
| * most that many milliseconds, after which a {@link NoSuchElementException} |
| * will be thrown. If {@link #setMaxWait maxWait} is non-positive, |
| * the {@link #borrowObject borrowObject} method will block indefinitely. |
| * </li> |
| * </ul> |
| * The default <code>whenExhaustedAction</code> setting is |
| * {@link WhenExhaustedAction#BLOCK}. |
| * </li> |
| * <li> |
| * When {@link #setTestOnBorrow testOnBorrow} is set, the pool will |
| * attempt to validate each object before it is returned from the |
| * {@link #borrowObject borrowObject} method. (Using the provided factory's |
| * {@link KeyedPoolableObjectFactory#validateObject validateObject} method.) |
| * Objects that fail to validate will be dropped from the pool, and a |
| * different object will be borrowed. The default setting for this parameter |
| * is <code>false.</code> |
| * </li> |
| * <li> |
| * When {@link #setTestOnReturn testOnReturn} is set, the pool will |
| * attempt to validate each object before it is returned to the pool in the |
| * {@link #returnObject returnObject} method. (Using the provided factory's |
| * {@link KeyedPoolableObjectFactory#validateObject validateObject} |
| * method.) Objects that fail to validate will be dropped from the pool. |
| * The default setting for this parameter is <code>false.</code> |
| * </li> |
| * </ul> |
| * <p> |
| * Optionally, one may configure the pool to examine and possibly evict objects |
| * as they sit idle in the pool and to ensure that a minimum number of idle |
| * objects is maintained for each key. This is performed by an |
| * "idle object eviction" thread, which runs asynchronously. Caution should be |
| * used when configuring this optional feature. Eviction runs contend with client |
| * threads for access to objects in the pool, so if they run too frequently |
| * performance issues may result. The idle object eviction thread may be |
| * configured using the following attributes: |
| * <ul> |
| * <li> |
| * {@link #setTimeBetweenEvictionRunsMillis timeBetweenEvictionRunsMillis} |
| * indicates how long the eviction thread should sleep before "runs" of examining |
| * idle objects. When non-positive, no eviction thread will be launched. The |
| * default setting for this parameter is -1 (i.e., by default, idle object |
| * eviction is disabled). |
| * </li> |
| * <li> |
| * {@link #setMinEvictableIdleTimeMillis minEvictableIdleTimeMillis} |
| * specifies the minimum amount of time that an object may sit idle in the |
| * pool before it is eligible for eviction due to idle time. When |
| * non-positive, no object will be dropped from the pool due to idle time |
| * alone. This setting has no effect unless |
| * <code>timeBetweenEvictionRunsMillis > 0.</code> The default setting |
| * for this parameter is 30 minutes. |
| * </li> |
| * <li> |
| * {@link #setTestWhileIdle testWhileIdle} indicates whether or not idle |
| * objects should be validated using the factory's |
| * {@link KeyedPoolableObjectFactory#validateObject validateObject} method |
| * during idle object eviction runs. Objects that fail to validate will be |
| * dropped from the pool. This setting has no effect unless |
| * <code>timeBetweenEvictionRunsMillis > 0.</code> The default setting |
| * for this parameter is <code>false.</code> |
| * </li> |
| * <li> |
| * {@link #setMinIdle minIdle} sets a target value for the minimum number of |
| * idle objects (per key) that should always be available. If this parameter |
| * is set to a positive number and |
| * <code>timeBetweenEvictionRunsMillis > 0,</code> each time the idle object |
| * eviction thread runs, it will try to create enough idle instances so that |
| * there will be <code>minIdle</code> idle instances available under each |
| * key. This parameter is also used by {@link #preparePool preparePool} |
| * if <code>true</code> is provided as that method's |
| * <code>populateImmediately</code> parameter. The default setting for this |
| * parameter is 0. |
| * </li> |
| * </ul> |
| * <p> |
| * The pools can be configured to behave as LIFO queues with respect to idle |
| * objects - always returning the most recently used object from the pool, |
| * or as FIFO queues, where borrowObject always returns the oldest object |
| * in the idle object pool. |
| * <ul> |
| * <li> |
| * {@link #setLifo <i>Lifo</i>} |
| * determines whether or not the pools return idle objects in |
| * last-in-first-out order. The default setting for this parameter is |
| * <code>true.</code> |
| * </li> |
| * </ul> |
| * <p> |
| * GenericKeyedObjectPool is not usable without a {@link KeyedPoolableObjectFactory}. A |
| * non-<code>null</code> factory must be provided as a constructor argument |
| * </p> |
| * <p> |
| * Implementation note: To prevent possible deadlocks, care has been taken to |
| * ensure that no call to a factory method will occur within a synchronization |
| * block. See POOL-125 and DBCP-44 for more information. |
| * </p> |
| * @see GenericObjectPool |
| * @author Rodney Waldhoff |
| * @author Dirk Verbeeck |
| * @author Sandy McArthur |
| * @version $Revision$ $Date$ |
| * @since Pool 1.0 |
| */ |
| public class GenericKeyedObjectPool<K,V> extends BaseKeyedObjectPool<K,V> implements GenericKeyedObjectPoolMBean<K> { |
| |
| //--- constructors ----------------------------------------------- |
| |
| /** |
| * Create a new <code>GenericKeyedObjectPool</code> using the specified values. |
| * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy |
| * objects if not <code>null</code> |
| */ |
| public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory) { |
| this(factory, new GenericKeyedObjectPoolConfig.Builder().createConfig()); |
| } |
| |
| /** |
| * Create a new <code>GenericKeyedObjectPool</code> using the specified values. |
| * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects |
| * if not <code>null</code> |
| * @param config a non-<code>null</code> {@link GenericKeyedObjectPoolConfig} describing the configuration |
| */ |
| public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K,V> factory, GenericKeyedObjectPoolConfig config) { |
| if (factory == null) { |
| throw new IllegalArgumentException("factory must not be null"); |
| } |
| if (config == null) { |
| throw new IllegalArgumentException("config must not be null"); |
| } |
| _factory = factory; |
| this.maxIdlePerKey = config.getMaxIdlePerKey(); |
| this.minIdlePerKey = config.getMinIdlePerKey(); |
| this.maxTotalPerKey = config.getMaxTotalPerKey(); |
| this.maxTotal = config.getMaxTotal(); |
| this.maxWait = config.getMaxWait(); |
| this.whenExhaustedAction = config.getWhenExhaustedAction(); |
| this.testOnBorrow = config.getTestOnBorrow(); |
| this.testOnReturn = config.getTestOnReturn(); |
| this.testWhileIdle = config.getTestWhileIdle(); |
| this.timeBetweenEvictionRunsMillis = config.getTimeBetweenEvictionRunsMillis(); |
| this.numTestsPerEvictionRun = config.getNumTestsPerEvictionRun(); |
| this.minEvictableIdleTimeMillis = config.getMinEvictableIdleTimeMillis(); |
| this.lifo = config.getLifo(); |
| |
| _poolMap = new HashMap<K,ObjectQueue>(); |
| _poolList = new CursorableLinkedList<K>(); |
| |
| startEvictor(this.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 #setMaxTotalPerKey |
| * @since 2.0 |
| */ |
| public synchronized int getMaxTotalPerKey() { |
| return this.maxTotalPerKey; |
| } |
| |
| /** |
| * Sets the cap on the total number of instances from all pools combined. |
| * When <code>maxTotal</code> is set to a |
| * positive value and {@link #borrowObject borrowObject} is invoked |
| * when at the limit with no idle instances available, an attempt is made to |
| * create room by clearing the oldest 15% of the elements from the keyed |
| * pools. |
| * |
| * @param maxTotal The cap on the total number of instances across pools. |
| * Use a negative value for no limit. |
| * @see #getMaxTotal |
| */ |
| public void setMaxTotalPerKey(int maxTotalPerKey) { |
| synchronized(this) { |
| this.maxTotalPerKey = maxTotalPerKey; |
| } |
| 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 this.maxTotal; |
| } |
| |
| /** |
| * Sets the cap on the total number of instances from all pools combined. |
| * When <code>maxTotal</code> is set to a |
| * positive value and {@link #borrowObject borrowObject} is invoked |
| * when at the limit with no idle instances available, an attempt is made to |
| * create room by clearing the oldest 15% of the elements from the keyed |
| * pools. |
| * |
| * @param maxTotal The cap on the total number of instances across pools. |
| * Use a negative value for no limit. |
| * @see #getMaxTotal |
| */ |
| public void setMaxTotal(int maxTotal) { |
| synchronized(this) { |
| 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 WhenExhaustedAction#BLOCK}, |
| * {@link WhenExhaustedAction#FAIL} or {@link WhenExhaustedAction#GROW} |
| * @see #setWhenExhaustedAction |
| */ |
| public synchronized WhenExhaustedAction getWhenExhaustedAction() { |
| return this.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 |
| * @see #getWhenExhaustedAction |
| */ |
| public void setWhenExhaustedAction(WhenExhaustedAction whenExhaustedAction) { |
| synchronized(this) { |
| this.whenExhaustedAction = whenExhaustedAction; |
| } |
| 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 WhenExhaustedAction#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 WhenExhaustedAction#BLOCK |
| */ |
| public synchronized long getMaxWait() { |
| return this.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 WhenExhaustedAction#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 WhenExhaustedAction#BLOCK |
| */ |
| public void setMaxWait(long maxWait) { |
| synchronized(this) { |
| 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 |
| * |
| * @since 2.0 |
| */ |
| public synchronized int getMaxIdlePerKey() { |
| return this.maxIdlePerKey; |
| } |
| |
| /** |
| * 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 maxIdlePerKey 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_PER_KEY |
| * |
| * @since 2.0 |
| */ |
| public void setMaxIdlePerKey(int maxIdlePerKey) { |
| synchronized(this) { |
| this.maxIdlePerKey = maxIdlePerKey; |
| } |
| allocate(); |
| } |
| |
| /** |
| * Sets the minimum number of idle objects to maintain in each of the keyed |
| * pools. This setting has no effect unless |
| * <code>timeBetweenEvictionRunsMillis > 0</code> and attempts to ensure |
| * that each pool has the required minimum number of instances are only |
| * made during idle object eviction runs. |
| * @param minIdlePerKey - The minimum size of the each keyed pool |
| * @since Pool 1.3 |
| * @see #getMinIdle |
| * @see #setTimeBetweenEvictionRunsMillis |
| * |
| * @since 2.0 |
| */ |
| public synchronized void setMinIdle(int minIdlePerKey) { |
| this.minIdlePerKey = minIdlePerKey; |
| } |
| |
| /** |
| * Returns the minimum number of idle objects to maintain in each of the keyed |
| * pools. This setting has no effect unless |
| * <code>timeBetweenEvictionRunsMillis > 0</code> and attempts to ensure |
| * that each pool has the required minimum number of instances are only |
| * made during idle object eviction runs. |
| * @return minimum size of the each keyed pool |
| * @since Pool 1.3 |
| * @see #setTimeBetweenEvictionRunsMillis |
| * |
| * @since 2.0 |
| */ |
| public synchronized int getMinIdlePerKey() { |
| return this.minIdlePerKey; |
| } |
| |
| /** |
| * When <code>true</code>, objects will be |
| * {@link org.apache.commons.pool2.PoolableObjectFactory#validateObject validated} |
| * before being returned by the {@link #borrowObject} |
| * method. If the object fails to validate, |
| * it will be dropped from the pool, and we will attempt |
| * to borrow another. |
| * |
| * @return <code>true</code> if objects are validated before being borrowed. |
| * @see #setTestOnBorrow |
| */ |
| public synchronized boolean getTestOnBorrow() { |
| return this.testOnBorrow; |
| } |
| |
| /** |
| * When <code>true</code>, objects will be |
| * {@link org.apache.commons.pool2.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 synchronized void setTestOnBorrow(boolean testOnBorrow) { |
| this.testOnBorrow = testOnBorrow; |
| } |
| |
| /** |
| * When <code>true</code>, objects will be |
| * {@link org.apache.commons.pool2.PoolableObjectFactory#validateObject validated} |
| * before being returned to the pool within the |
| * {@link #returnObject}. |
| * |
| * @return <code>true</code> when objects will be validated before being returned. |
| * @see #setTestOnReturn |
| */ |
| public synchronized boolean getTestOnReturn() { |
| return this.testOnReturn; |
| } |
| |
| /** |
| * When <code>true</code>, objects will be |
| * {@link org.apache.commons.pool2.PoolableObjectFactory#validateObject validated} |
| * before being returned to the pool within the |
| * {@link #returnObject}. |
| * |
| * @param testOnReturn <code>true</code> so objects will be validated before being returned. |
| * @see #getTestOnReturn |
| */ |
| public synchronized void setTestOnReturn(boolean testOnReturn) { |
| this.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 this.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) { |
| this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; |
| startEvictor(this.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 this.numTestsPerEvictionRun; |
| } |
| |
| /** |
| * Sets the max number of objects to examine during each run of the |
| * idle object evictor thread (if any). |
| * <p> |
| * When a negative value is supplied, |
| * <code>ceil({@link #getNumIdle()})/abs({@link #getNumTestsPerEvictionRun})</code> |
| * tests will be run. I.e., when the value is <code>-n</code>, roughly one <code>n</code>th of the |
| * idle objects will be tested per run. When the value is positive, the number of tests |
| * actually performed in each run will be the minimum of this value and the number of instances |
| * idle in the pools. |
| * |
| * @param numTestsPerEvictionRun number of objects to examine each eviction run. |
| * @see #setNumTestsPerEvictionRun |
| * @see #setTimeBetweenEvictionRunsMillis |
| */ |
| public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { |
| this.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 this.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) { |
| this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; |
| } |
| |
| /** |
| * When <code>true</code>, objects will be |
| * {@link org.apache.commons.pool2.PoolableObjectFactory#validateObject validated} |
| * by the idle object evictor (if any). If an object |
| * fails to validate, it will be dropped from the pool. |
| * |
| * @return <code>true</code> when objects are validated when borrowed. |
| * @see #setTestWhileIdle |
| * @see #setTimeBetweenEvictionRunsMillis |
| */ |
| public synchronized boolean getTestWhileIdle() { |
| return this.testWhileIdle; |
| } |
| |
| /** |
| * When <code>true</code>, objects will be |
| * {@link org.apache.commons.pool2.PoolableObjectFactory#validateObject validated} |
| * by the idle object evictor (if any). If an object |
| * fails to validate, it will be dropped from the pool. |
| * |
| * @param testWhileIdle <code>true</code> so objects are validated when borrowed. |
| * @see #getTestWhileIdle |
| * @see #setTimeBetweenEvictionRunsMillis |
| */ |
| public synchronized void setTestWhileIdle(boolean testWhileIdle) { |
| this.testWhileIdle = testWhileIdle; |
| } |
| |
| /** |
| * Whether or not the idle object pools act as LIFO queues. True means |
| * that borrowObject returns the most recently used ("last in") idle object |
| * in a pool (if there are idle instances available). False means that |
| * the pools behave as FIFO queues - objects are taken from idle object |
| * pools in the order that they are returned. |
| * |
| * @return <code>true</code> if the pools are configured to act as LIFO queues |
| * @since 1.4 |
| */ |
| public synchronized boolean getLifo() { |
| return this.lifo; |
| } |
| |
| /** |
| * Sets the LIFO property of the pools. True means that borrowObject returns |
| * the most recently used ("last in") idle object in a pool (if there are |
| * idle instances available). False means that the pools behave as FIFO |
| * queues - objects are taken from idle object pools in the order that |
| * they are returned. |
| * |
| * @param lifo the new value for the lifo property |
| * @since 1.4 |
| */ |
| public synchronized void setLifo(boolean lifo) { |
| this.lifo = lifo; |
| } |
| |
| //-- ObjectPool methods ------------------------------------------ |
| |
| /** |
| * <p>Borrows an object from the keyed pool associated with the given key.</p> |
| * |
| * <p>If there is an idle instance available in the pool associated with the given key, then |
| * either the most-recently returned (if {@link #getLifo() lifo} == true) or "oldest" (lifo == false) |
| * instance sitting idle in the pool will be activated and returned. If activation fails, or |
| * {@link #getTestOnBorrow() testOnBorrow} is set to true and validation fails, the instance is destroyed and the |
| * next available instance is examined. This continues until either a valid instance is returned or there |
| * are no more idle instances available.</p> |
| * |
| * <p>If there are no idle instances available in the pool associated with the given key, behavior |
| * depends on the {@link #getMaxActive() maxActive}, {@link #getMaxTotal() maxTotal}, and (if applicable) |
| * {@link #getWhenExhaustedAction() whenExhaustedAction} and {@link #getMaxWait() maxWait} properties. If the |
| * number of instances checked out from the pool under the given key is less than <code>maxActive</code> and |
| * the total number of instances in circulation (under all keys) is less than <code>maxTotal</code>, a new instance |
| * is created, activated and (if applicable) validated and returned to the caller.</p> |
| * |
| * <p>If the associated keyed pool is exhausted (no available idle instances and no capacity to create new ones), |
| * this method will either block ({@link WhenExhaustedAction#BLOCK}), throw a <code>NoSuchElementException</code> |
| * ({@link WhenExhaustedAction#FAIL}), or grow ({@link WhenExhaustedAction#GROW} - ignoring maxActive, maxTotal properties). |
| * The length of time that this method will block when <code>whenExhaustedAction == WHEN_EXHAUSTED_BLOCK</code> |
| * is determined by the {@link #getMaxWait() maxWait} property.</p> |
| * |
| * <p>When the pool is exhausted, multiple calling threads may be simultaneously blocked waiting for instances |
| * to become available. As of pool 1.5, a "fairness" algorithm has been implemented to ensure that threads receive |
| * available instances in request arrival order.</p> |
| * |
| * @param key pool key |
| * @return object instance from the keyed pool |
| * @throws NoSuchElementException if a keyed object instance cannot be returned. |
| */ |
| @Override |
| public V borrowObject(K key) throws Exception { |
| long starttime = System.currentTimeMillis(); |
| Latch latch = new Latch(key); |
| WhenExhaustedAction 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 = this.whenExhaustedAction; |
| maxWait = this.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 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 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 BLOCK: |
| try { |
| synchronized (latch) { |
| // Before we wait, make sure another thread didn't allocate us an object |
| // or permit a new object to be created |
| if (latch.getPair() == null && !latch.mayCreate()) { |
| if (maxWait <= 0) { |
| latch.wait(); |
| } else { |
| // this code may be executed again after a notify then continue cycle |
| // so, need to calculate the amount of time to wait |
| final long elapsed = (System.currentTimeMillis() - starttime); |
| final long waitTime = maxWait - elapsed; |
| if (waitTime > 0) |
| { |
| latch.wait(waitTime); |
| } |
| } |
| } else { |
| break; |
| } |
| } |
| } catch(InterruptedException e) { |
| 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<V>(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().getValue()); |
| if (this.testOnBorrow && !_factory.validateObject(key, latch.getPair().getValue())) { |
| throw new Exception("ValidateObject failed"); |
| } |
| synchronized (this) { |
| latch.getPool().decrementInternalProcessingCount(); |
| latch.getPool().incrementActiveCount(); |
| } |
| return latch.getPair().getValue(); |
| } catch (Throwable e) { |
| PoolUtils.checkRethrow(e); |
| // object cannot be activated or is invalid |
| try { |
| _factory.destroyObject(key, latch.getPair().getValue()); |
| } 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 <b>MUST NOT</b> be called |
| * from inside a sync block. |
| */ |
| private void allocate() { |
| boolean clearOldest = false; |
| |
| synchronized (this) { |
| if (isClosed()) return; |
| |
| Iterator<Latch> 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 ((this.maxTotal > 0) && |
| (_totalActive + _totalIdle + _totalInternalProcessing >= this.maxTotal)) { |
| clearOldest = true; |
| break; |
| } |
| |
| // Second utilise any spare capacity to create new objects |
| if ((this.maxTotalPerKey < 0 || pool.activeCount + pool.internalProcessingCount < this.maxTotalPerKey) && |
| (this.maxTotal < 0 || _totalActive + _totalIdle + _totalInternalProcessing < this.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 (this.maxTotalPerKey < 0) { |
| break; |
| } |
| } |
| } |
| |
| if (clearOldest) { |
| /* Clear oldest calls factory methods so it must be called from |
| * outside the sync block. |
| * It also needs to be outside the sync block as it calls |
| * allocate(). If called inside the sync block, the call to |
| * allocate() would be able to enter the sync block (since the |
| * thread already has the lock) which may have unexpected, |
| * unpleasant results. |
| */ |
| clearOldest(); |
| } |
| } |
| |
| /** |
| * Clears any objects sitting idle in the pool by removing them from the |
| * idle instance pool and then invoking the configured PoolableObjectFactory's |
| * {@link KeyedPoolableObjectFactory#destroyObject(Object, Object)} method on |
| * each idle instance. |
| * |
| * <p> Implementation notes: |
| * <ul><li>This method does not destroy or effect in any way instances that are |
| * checked out when it is invoked.</li> |
| * <li>Invoking this method does not prevent objects being |
| * returned to the idle instance pool, even during its execution. It locks |
| * the pool only during instance removal. Additional instances may be returned |
| * while removed items are being destroyed.</li> |
| * <li>Exceptions encountered destroying idle instances are swallowed.</li></ul></p> |
| */ |
| @Override |
| public void clear() { |
| Map<K,List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K,List<ObjectTimestampPair<V>>>(); |
| synchronized (this) { |
| for (Iterator<K> 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<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>(); |
| 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<K, List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K, List<ObjectTimestampPair<V>>>(); |
| |
| // build sorted map of idle objects |
| final Map<ObjectTimestampPair<V>, K> map = new TreeMap<ObjectTimestampPair<V>, K>(); |
| synchronized (this) { |
| for (K key : _poolMap.keySet()) { |
| final CursorableLinkedList<ObjectTimestampPair<V>> list = _poolMap.get(key).queue; |
| for (ObjectTimestampPair<V> pair : list) { |
| // 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(pair, key); |
| } |
| } |
| |
| // Now iterate created map and kill the first 15% plus one to account for zero |
| Set<Map.Entry<ObjectTimestampPair<V>, K>> setPairKeys = map.entrySet(); |
| int itemsToRemove = ((int) (map.size() * 0.15)) + 1; |
| |
| Iterator<Map.Entry<ObjectTimestampPair<V>, K>> iter = setPairKeys.iterator(); |
| while (iter.hasNext() && itemsToRemove > 0) { |
| Map.Entry<ObjectTimestampPair<V>, 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<V> pairTimeStamp = entry.getKey(); |
| ObjectQueue objectQueue = _poolMap.get(key); |
| final CursorableLinkedList<ObjectTimestampPair<V>> list = |
| objectQueue.queue; |
| list.remove(pairTimeStamp); |
| |
| if (toDestroy.containsKey(key)) { |
| toDestroy.get(key).add(pairTimeStamp); |
| } else { |
| List<ObjectTimestampPair<V>> listForKey = new ArrayList<ObjectTimestampPair<V>>(); |
| 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 <code>key</code>. |
| * |
| * @param key the key to clear |
| */ |
| @Override |
| public void clear(K key) { |
| Map<K, List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K, List<ObjectTimestampPair<V>>>(); |
| |
| 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<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>(); |
| objects.addAll(pool.queue); |
| toDestroy.put(key, objects); |
| _totalIdle = _totalIdle - pool.queue.size(); |
| _totalInternalProcessing = |
| _totalInternalProcessing + pool.queue.size(); |
| pool.queue.clear(); |
| } |
| destroy(toDestroy, _factory); |
| } |
| |
| /** |
| * Assuming Map<Object,Collection<ObjectTimestampPair>>, destroy all |
| * ObjectTimestampPair.value using the supplied factory. |
| * |
| * @param m Map containing keyed pools to clear |
| * @param factory KeyedPoolableObjectFactory used to destroy the objects |
| */ |
| private void destroy(Map<K, List<ObjectTimestampPair<V>>> m, KeyedPoolableObjectFactory<K, V> factory) { |
| for (Map.Entry<K, List<ObjectTimestampPair<V>>> entry : m.entrySet()) { |
| K key = entry.getKey(); |
| Collection<ObjectTimestampPair<V>> c = entry.getValue(); |
| for (ObjectTimestampPair<V> pair : c) { |
| try { |
| factory.destroyObject(key, pair.getValue()); |
| } 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); |
| } |
| } |
| } |
| 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 <code>key</code>. |
| * |
| * @param key the key to query |
| * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool |
| */ |
| @Override |
| public synchronized int getNumActive(K key) { |
| final ObjectQueue pool = (_poolMap.get(key)); |
| return pool != null ? pool.activeCount : 0; |
| } |
| |
| /** |
| * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool. |
| * |
| * @param key the key to query |
| * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool |
| */ |
| @Override |
| public synchronized int getNumIdle(K key) { |
| final ObjectQueue pool = (_poolMap.get(key)); |
| return pool != null ? pool.queue.size() : 0; |
| } |
| |
| /** |
| * <p>Returns an object to a keyed pool.</p> |
| * |
| * <p>For the pool to function correctly, the object instance <strong>must</strong> have been borrowed |
| * from the pool (under the same key) and not yet returned. Repeated <code>returnObject</code> calls on |
| * the same object/key pair (with no <code>borrowObject</code> calls in between) will result in multiple |
| * references to the object in the idle instance pool.</p> |
| * |
| * <p>If {@link #getMaxIdle() maxIdle} is set to a positive value and the number of idle instances under the given |
| * key has reached this value, the returning instance is destroyed.</p> |
| * |
| * <p>If {@link #getTestOnReturn() testOnReturn} == true, the returning instance is validated before being returned |
| * to the idle instance pool under the given key. In this case, if validation fails, the instance is destroyed.</p> |
| * |
| * @param key pool key |
| * @param obj instance to return to the keyed pool |
| * @throws Exception |
| */ |
| @Override |
| public void returnObject(K key, V obj) throws Exception { |
| try { |
| addObjectToPool(key, obj, true); |
| } catch (Exception e) { |
| 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(); |
| } |
| } |
| } |
| |
| /** |
| * <p>Adds an object to the keyed pool.</p> |
| * |
| * <p>Validates the object if testOnReturn == true and passivates it before returning it to the pool. |
| * if validation or passivation fails, or maxIdle is set and there is no room in the pool, the instance |
| * is destroyed.</p> |
| * |
| * <p>Calls {@link #allocate()} on successful completion</p> |
| * |
| * @param key pool key |
| * @param obj instance to add to the keyed pool |
| * @param decrementNumActive whether or not to decrement the active count associated with the keyed pool |
| * @throws Exception |
| */ |
| private void addObjectToPool(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 (this.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 (this.maxIdlePerKey >= 0 && (pool.queue.size() >= this.maxIdlePerKey)) { |
| 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 (this.lifo) { |
| pool.queue.addFirst(new ObjectTimestampPair<V>(obj)); |
| } else { |
| pool.queue.addLast(new ObjectTimestampPair<V>(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} |
| * <p>Activation of this method decrements the active count associated with the given keyed pool |
| * and attempts to destroy <code>obj.</code></p> |
| * |
| * @param key pool key |
| * @param obj instance to invalidate |
| * @throws Exception if an exception occurs destroying the object |
| */ |
| @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. |
| * <code>addObject</code> is useful for "pre-loading" a pool with idle objects. |
| * |
| * @param key the key a new instance should be added to |
| * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails. |
| * called on this pool. |
| */ |
| @Override |
| public void addObject(K key) throws Exception { |
| assertOpen(); |
| 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 <code>populateImmediately</code> is <code>true</code> and |
| * <code>minIdle > 0,</code> the pool under the given key will be |
| * populated immediately with <code>minIdle</code> idle instances. |
| * |
| * @param key - The key to register for pool control. |
| * @param populateImmediately - If this is <code>true</code>, the pool |
| * will be populated immediately. |
| * @since Pool 1.3 |
| */ |
| public synchronized void preparePool(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 |
| } |
| } |
| } |
| |
| /** |
| * <p>Closes the keyed object pool. Once the pool is closed, {@link #borrowObject(Object)} |
| * will fail with IllegalStateException, but {@link #returnObject(Object, Object)} and |
| * {@link #invalidateObject(Object, Object)} will continue to work, with returned objects |
| * destroyed on return.</p> |
| * |
| * <p>Destroys idle instances in the pool by invoking {@link #clear()}.</p> |
| * |
| * @throws Exception |
| */ |
| @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); |
| } |
| } |
| |
| /** |
| * <p>Perform <code>numTests</code> idle object eviction tests, evicting |
| * examined objects that meet the criteria for eviction. If |
| * <code>testWhileIdle</code> is true, examined objects are validated |
| * when visited (and removed if invalid); otherwise only objects that |
| * have been idle for more than <code>minEvicableIdletimeMillis</code> |
| * are removed.</p> |
| * |
| * <p>Successive activations of this method examine objects in keyed pools |
| * in sequence, cycling through the keys and examining objects in |
| * oldest-to-youngest order within the keyed pools.</p> |
| * |
| * @throws Exception when there is a problem evicting idle objects. |
| */ |
| public void evict() throws Exception { |
| 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 = this.testWhileIdle; |
| minEvictableIdleTimeMillis = this.minEvictableIdleTimeMillis; |
| |
| // Initialize key to last key value |
| if (_evictionKeyCursor != null && |
| _evictionKeyCursor._lastReturned != null) { |
| key = _evictionKeyCursor._lastReturned.value(); |
| } |
| } |
| |
| for (int i=0, m=getNumTests(); i<m; i++) { |
| final ObjectTimestampPair<V> 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 ((this.lifo && !_evictionCursor.hasPrevious()) || |
| (!this.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 ((this.lifo && !_evictionCursor.hasPrevious()) || |
| (!this.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 = this.lifo ? |
| _evictionCursor.previous() : |
| _evictionCursor.next(); |
| _evictionCursor.remove(); |
| ObjectQueue objectQueue = _poolMap.get(key); |
| objectQueue.incrementInternalProcessingCount(); |
| _totalIdle--; |
| } |
| |
| boolean removeObject=false; |
| if ((minEvictableIdleTimeMillis > 0) && |
| (System.currentTimeMillis() - pair.getTstamp() > |
| minEvictableIdleTimeMillis)) { |
| removeObject=true; |
| } |
| if (testWhileIdle && removeObject == false) { |
| boolean active = false; |
| try { |
| _factory.activateObject(key,pair.getValue()); |
| active = true; |
| } catch(Exception e) { |
| removeObject=true; |
| } |
| if (active) { |
| if (!_factory.validateObject(key,pair.getValue())) { |
| removeObject=true; |
| } else { |
| try { |
| _factory.passivateObject(key,pair.getValue()); |
| } catch(Exception e) { |
| removeObject=true; |
| } |
| } |
| } |
| } |
| |
| if (removeObject) { |
| try { |
| _factory.destroyObject(key, pair.getValue()); |
| } 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 (this.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(K key) { |
| if (_evictionCursor != null) { |
| _evictionCursor.close(); |
| } |
| if (_poolMap == null) { |
| return; |
| } |
| ObjectQueue pool = _poolMap.get(key); |
| if (pool != null) { |
| CursorableLinkedList<ObjectTimestampPair<V>> queue = pool.queue; |
| _evictionCursor = queue.cursor(this.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") // OK, see (1) |
| private void ensureMinIdle() throws Exception { |
| //Check if should sustain the pool |
| if (this.minIdlePerKey > 0) { |
| K[] keysCopy; |
| synchronized(this) { |
| // Get the current set of keys |
| keysCopy = (K[]) _poolMap.keySet().toArray(); // (1) keySet() is of type Set<T> |
| } |
| |
| // Loop through all elements in _poolList |
| // Find out the total number of max active and max idle for that class |
| // If the number is less than the minIdle, do creation loop to boost numbers |
| for (int i=0; i < keysCopy.length; i++) { |
| //Get the next key to process |
| ensureMinIdle(keysCopy[i]); |
| } |
| } |
| } |
| |
| /** |
| * Re-creates any needed objects to maintain the minimum levels of |
| * pooled objects for the specified key. |
| * |
| * This method uses {@link #calculateDeficit} to calculate the number |
| * of objects to be created. {@link #calculateDeficit} can be overridden to |
| * provide a different method of calculating the number of objects to be |
| * created. |
| * @param key The key to process |
| * @throws Exception If there was an error whilst creating the pooled objects |
| */ |
| private void ensureMinIdle(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 |
| * <code>delay</code> is non-positive, stop it |
| * if it is already running. |
| * |
| * @param delay milliseconds between evictor runs. |
| */ |
| protected synchronized void startEvictor(long delay) { |
| if (null != _evictor) { |
| EvictionTimer.cancel(_evictor); |
| _evictor = null; |
| } |
| if (delay > 0) { |
| _evictor = new Evictor(); |
| EvictionTimer.schedule(_evictor, delay, delay); |
| } |
| } |
| |
| /** |
| * Returns pool info including {@link #getNumActive()}, {@link #getNumIdle()} |
| * and currently defined keys. |
| * |
| * @return string containing debug information |
| */ |
| synchronized String debugInfo() { |
| StringBuffer buf = new StringBuffer(); |
| buf.append("Active: ").append(getNumActive()).append("\n"); |
| buf.append("Idle: ").append(getNumIdle()).append("\n"); |
| for (K key : _poolMap.keySet()) { |
| buf.append("\t").append(key).append(" ").append(_poolMap.get(key)).append("\n"); |
| } |
| return buf.toString(); |
| } |
| |
| /** |
| * Returns the number of tests to be performed in an Evictor run, |
| * based on the current values of <code>_numTestsPerEvictionRun</code> |
| * and <code>_totalIdle</code>. |
| * |
| * @see #setNumTestsPerEvictionRun |
| * @return the number of tests for the Evictor to run |
| */ |
| private synchronized int getNumTests() { |
| if (this.numTestsPerEvictionRun >= 0) { |
| return Math.min(this.numTestsPerEvictionRun, _totalIdle); |
| } else { |
| return(int)(Math.ceil(_totalIdle/Math.abs((double)this.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 = this.minIdlePerKey - pool.queue.size(); |
| if (this.maxTotalPerKey > 0) { |
| int growLimit = Math.max(0, this.maxTotalPerKey - pool.activeCount - pool.queue.size() - pool.internalProcessingCount); |
| objectDefecit = Math.min(objectDefecit, growLimit); |
| } |
| |
| // Take the maxTotal limit into account |
| if (this.maxTotal > 0) { |
| int growLimit = Math.max(0, this.maxTotal - 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<ObjectTimestampPair<V>> queue = new CursorableLinkedList<ObjectTimestampPair<V>>(); |
| |
| /** 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<V> implements Comparable<ObjectTimestampPair<V>> { |
| //CHECKSTYLE: stop VisibilityModifier |
| /** |
| * Object instance. |
| */ |
| private final V value; |
| |
| /** |
| * timestamp |
| */ |
| private final long tstamp; |
| //CHECKSTYLE: resume VisibilityModifier |
| |
| /** |
| * Create a new ObjectTimestampPair using the given object and the current system time. |
| * @param val object instance |
| */ |
| ObjectTimestampPair(V 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(V 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 other object to compare |
| * @return result of comparison |
| */ |
| public int compareTo(ObjectTimestampPair<V> 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 V 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 |
| } |
| } |
| } |
| |
| /** |
| * 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 K _key; |
| |
| /** keyed pool associated with this latch */ |
| private ObjectQueue _pool; |
| |
| /** holds an ObjectTimestampPair when this latch has been allocated an instance */ |
| private ObjectTimestampPair<V> _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(K key) { |
| _key = key; |
| } |
| |
| /** |
| * Retuns the key of the associated pool |
| * @return associated pool key |
| */ |
| private synchronized K 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<V> getPair() { |
| return _pair; |
| } |
| |
| /** |
| * Allocate an ObjectTimestampPair to this latch. |
| * @param pair ObjectTimestampPair on this latch |
| */ |
| private synchronized void setPair(ObjectTimestampPair<V> 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 --------------------------------------- |
| |
| /* Pool configuration */ |
| |
| /** |
| * The cap on the number of idle instances in the pool. |
| */ |
| private int maxIdlePerKey; // @GuardedBy("this") |
| |
| /** |
| * The cap on the minimum number of idle instances in the pool. |
| * |
| * @see #setMinIdle |
| */ |
| private int minIdlePerKey; // @GuardedBy("this") |
| |
| /** |
| * The cap on the total number of active instances from the pool per key. |
| * |
| * @see #setMaxTotal(int) |
| */ |
| private int maxTotalPerKey; // @GuardedBy("this") |
| |
| /** |
| * The cap on the total number of active instances from the pool. |
| * |
| * @see #setMaxTotal(int) |
| */ |
| private int maxTotal; // @GuardedBy("this") |
| |
| /** |
| * The maximum amount of time (in millis) the |
| * {@link org.apache.commons.pool2.ObjectPool#borrowObject} method should block before throwing |
| * an exception when the pool is exhausted and the |
| * {@link #getWhenExhaustedAction "when exhausted" action} is |
| * {@link WhenExhaustedAction#BLOCK}. |
| * |
| * When less than or equal to 0, the {@link org.apache.commons.pool2.ObjectPool#borrowObject} method |
| * may block indefinitely. |
| * |
| * @see #setMaxWait |
| * @see WhenExhaustedAction#BLOCK |
| * @see #setWhenExhaustedAction |
| */ |
| private long maxWait; // @GuardedBy("this") |
| |
| /** |
| * The action to take when the {@link org.apache.commons.pool2.ObjectPool#borrowObject} method |
| * is invoked when the pool is exhausted (the maximum number |
| * of "active" objects has been reached). |
| * |
| * @see WHEN_EXHAUSTED_ACTION#BLOCK |
| * @see WHEN_EXHAUSTED_ACTION#FAIL |
| * @see WHEN_EXHAUSTED_ACTION#GROW |
| * @see DEFAULT_WHEN_EXHAUSTED_ACTION |
| * @see #setWhenExhaustedAction |
| */ |
| private WhenExhaustedAction whenExhaustedAction; // @GuardedBy("this") |
| |
| /** |
| * When <tt>true</tt>, objects will be |
| * {@link org.apache.commons.pool2.PoolableObjectFactory#validateObject validated} |
| * before being returned by the {@link org.apache.commons.pool2.ObjectPool#borrowObject} |
| * method. If the object fails to validate, |
| * it will be dropped from the pool, and we will attempt |
| * to borrow another. |
| * |
| * @see #setTestOnBorrow |
| */ |
| private boolean testOnBorrow; // @GuardedBy("this") |
| |
| /** |
| * When <tt>true</tt>, objects will be |
| * {@link org.apache.commons.pool2.ObjectPool#validateObject validated} |
| * before being returned to the pool within the |
| * {@link #returnObject}. |
| * |
| * @see #setTestOnReturn |
| */ |
| private boolean testOnReturn; // @GuardedBy("this") |
| |
| /** |
| * When <tt>true</tt>, objects will be |
| * {@link org.apache.commons.pool2.ObjectPool#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 |
| */ |
| private boolean testWhileIdle; // @GuardedBy("this") |
| |
| /** |
| * 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 |
| */ |
| private long timeBetweenEvictionRunsMillis; // @GuardedBy("this") |
| |
| /** |
| * The max number of objects to examine during each run of the |
| * idle object evictor thread (if any). |
| * <p> |
| * When a negative value is supplied, <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 #setTimeBetweenEvictionRunsMillis |
| */ |
| private int numTestsPerEvictionRun; // @GuardedBy("this") |
| |
| /** |
| * 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 #setTimeBetweenEvictionRunsMillis |
| */ |
| private long minEvictableIdleTimeMillis; // @GuardedBy("this") |
| |
| /** |
| * Whether or not the pool behaves as a LIFO queue (last in first out) |
| * |
| * @see #setLifo |
| */ |
| private boolean lifo; // @GuardedBy("this") |
| |
| /** My hash of pools (ObjectQueue). */ |
| private final Map<K,ObjectQueue> _poolMap; |
| |
| /** 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 final KeyedPoolableObjectFactory<K,V> _factory; |
| |
| /** |
| * My idle object eviction {@link TimerTask}, if any. |
| */ |
| private Evictor _evictor = null; |
| |
| /** |
| * A cursorable list of my pools. |
| * @see GenericKeyedObjectPool.Evictor#run |
| */ |
| private final CursorableLinkedList<K> _poolList; |
| |
| /** Eviction cursor (over instances within-key) */ |
| private CursorableLinkedList<ObjectTimestampPair<V>>.Cursor _evictionCursor = null; |
| |
| /** Eviction cursor (over keys) */ |
| private CursorableLinkedList<K>.Cursor _evictionKeyCursor = null; |
| |
| /** |
| * 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 final LinkedList<Latch> _allocationQueue = new LinkedList<Latch>(); |
| |
| } |