/* | |
* 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.io.PrintWriter; | |
import java.io.StringWriter; | |
import java.io.Writer; | |
import java.lang.management.ManagementFactory; | |
import java.lang.ref.WeakReference; | |
import java.lang.reflect.InvocationTargetException; | |
import java.util.Arrays; | |
import java.util.Deque; | |
import java.util.Iterator; | |
import java.util.TimerTask; | |
import java.util.concurrent.ScheduledFuture; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.atomic.AtomicLong; | |
import javax.management.InstanceAlreadyExistsException; | |
import javax.management.InstanceNotFoundException; | |
import javax.management.MBeanRegistrationException; | |
import javax.management.MBeanServer; | |
import javax.management.MalformedObjectNameException; | |
import javax.management.NotCompliantMBeanException; | |
import javax.management.ObjectName; | |
import org.apache.commons.pool2.BaseObject; | |
import org.apache.commons.pool2.PooledObject; | |
import org.apache.commons.pool2.PooledObjectState; | |
import org.apache.commons.pool2.SwallowedExceptionListener; | |
/** | |
* Base class that provides common functionality for {@link GenericObjectPool} | |
* and {@link GenericKeyedObjectPool}. The primary reason this class exists is | |
* reduce code duplication between the two pool implementations. | |
* | |
* @param <T> Type of element pooled in this pool. | |
* | |
* This class is intended to be thread-safe. | |
* | |
* @since 2.0 | |
*/ | |
public abstract class BaseGenericObjectPool<T> extends BaseObject { | |
// Constants | |
/** | |
* The size of the caches used to store historical data for some attributes | |
* so that rolling means may be calculated. | |
*/ | |
public static final int MEAN_TIMING_STATS_CACHE_SIZE = 100; | |
private static final String EVICTION_POLICY_TYPE_NAME = EvictionPolicy.class.getName(); | |
// Configuration attributes | |
private volatile int maxTotal = | |
GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; | |
private volatile boolean blockWhenExhausted = | |
BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; | |
private volatile long maxWaitMillis = | |
BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS; | |
private volatile boolean lifo = BaseObjectPoolConfig.DEFAULT_LIFO; | |
private final boolean fairness; | |
private volatile boolean testOnCreate = | |
BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE; | |
private volatile boolean testOnBorrow = | |
BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW; | |
private volatile boolean testOnReturn = | |
BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN; | |
private volatile boolean testWhileIdle = | |
BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE; | |
private volatile long timeBetweenEvictionRunsMillis = | |
BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; | |
private volatile int numTestsPerEvictionRun = | |
BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; | |
private volatile long minEvictableIdleTimeMillis = | |
BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; | |
private volatile long softMinEvictableIdleTimeMillis = | |
BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; | |
private volatile EvictionPolicy<T> evictionPolicy; | |
private volatile long evictorShutdownTimeoutMillis = | |
BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS; | |
// Internal (primarily state) attributes | |
final Object closeLock = new Object(); | |
volatile boolean closed = false; | |
final Object evictionLock = new Object(); | |
private Evictor evictor = null; // @GuardedBy("evictionLock") | |
EvictionIterator evictionIterator = null; // @GuardedBy("evictionLock") | |
/* | |
* Class loader for evictor thread to use since, in a JavaEE or similar | |
* environment, the context class loader for the evictor thread may not have | |
* visibility of the correct factory. See POOL-161. Uses a weak reference to | |
* avoid potential memory leaks if the Pool is discarded rather than closed. | |
*/ | |
private final WeakReference<ClassLoader> factoryClassLoader; | |
// Monitoring (primarily JMX) attributes | |
private final ObjectName objectName; | |
private final String creationStackTrace; | |
private final AtomicLong borrowedCount = new AtomicLong(0); | |
private final AtomicLong returnedCount = new AtomicLong(0); | |
final AtomicLong createdCount = new AtomicLong(0); | |
final AtomicLong destroyedCount = new AtomicLong(0); | |
final AtomicLong destroyedByEvictorCount = new AtomicLong(0); | |
final AtomicLong destroyedByBorrowValidationCount = new AtomicLong(0); | |
private final StatsStore activeTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); | |
private final StatsStore idleTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); | |
private final StatsStore waitTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); | |
private final AtomicLong maxBorrowWaitTimeMillis = new AtomicLong(0L); | |
private volatile SwallowedExceptionListener swallowedExceptionListener = null; | |
/** | |
* Handles JMX registration (if required) and the initialization required for | |
* monitoring. | |
* | |
* @param config Pool configuration | |
* @param jmxNameBase The default base JMX name for the new pool unless | |
* overridden by the config | |
* @param jmxNamePrefix Prefix to be used for JMX name for the new pool | |
*/ | |
public BaseGenericObjectPool(final BaseObjectPoolConfig<T> config, | |
final String jmxNameBase, final String jmxNamePrefix) { | |
if (config.getJmxEnabled()) { | |
this.objectName = jmxRegister(config, jmxNameBase, jmxNamePrefix); | |
} else { | |
this.objectName = null; | |
} | |
// Populate the creation stack trace | |
this.creationStackTrace = getStackTrace(new Exception()); | |
// save the current TCCL (if any) to be used later by the evictor Thread | |
final ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |
if (cl == null) { | |
factoryClassLoader = null; | |
} else { | |
factoryClassLoader = new WeakReference<>(cl); | |
} | |
fairness = config.getFairness(); | |
} | |
/** | |
* Returns the maximum number of objects that can be allocated by the pool | |
* (checked out to clients, or idle awaiting checkout) at a given time. When | |
* negative, there is no limit to the number of objects that can be | |
* managed by the pool at one time. | |
* | |
* @return the cap on the total number of object instances managed by the | |
* pool. | |
* | |
* @see #setMaxTotal | |
*/ | |
public final int getMaxTotal() { | |
return maxTotal; | |
} | |
/** | |
* Sets the cap on the number of objects that can be allocated by the pool | |
* (checked out to clients, or idle awaiting checkout) at a given time. Use | |
* a negative value for no limit. | |
* | |
* @param maxTotal The cap on the total number of object instances managed | |
* by the pool. Negative values mean that there is no limit | |
* to the number of objects allocated by the pool. | |
* | |
* @see #getMaxTotal | |
*/ | |
public final void setMaxTotal(final int maxTotal) { | |
this.maxTotal = maxTotal; | |
} | |
/** | |
* Returns whether to block when the {@code borrowObject()} method is | |
* invoked when the pool is exhausted (the maximum number of "active" | |
* objects has been reached). | |
* | |
* @return {@code true} if {@code borrowObject()} should block | |
* when the pool is exhausted | |
* | |
* @see #setBlockWhenExhausted | |
*/ | |
public final boolean getBlockWhenExhausted() { | |
return blockWhenExhausted; | |
} | |
/** | |
* Sets whether to block when the {@code borrowObject()} method is | |
* invoked when the pool is exhausted (the maximum number of "active" | |
* objects has been reached). | |
* | |
* @param blockWhenExhausted {@code true} if | |
* {@code borrowObject()} should block | |
* when the pool is exhausted | |
* | |
* @see #getBlockWhenExhausted | |
*/ | |
public final void setBlockWhenExhausted(final boolean blockWhenExhausted) { | |
this.blockWhenExhausted = blockWhenExhausted; | |
} | |
/** | |
* Initializes the receiver with the given configuration. | |
* | |
* @param config Initialization source. | |
*/ | |
protected void setConfig(final BaseObjectPoolConfig<T> config) { | |
setLifo(config.getLifo()); | |
setMaxWaitMillis(config.getMaxWaitMillis()); | |
setBlockWhenExhausted(config.getBlockWhenExhausted()); | |
setTestOnCreate(config.getTestOnCreate()); | |
setTestOnBorrow(config.getTestOnBorrow()); | |
setTestOnReturn(config.getTestOnReturn()); | |
setTestWhileIdle(config.getTestWhileIdle()); | |
setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun()); | |
setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis()); | |
setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis()); | |
setSoftMinEvictableIdleTimeMillis(config.getSoftMinEvictableIdleTimeMillis()); | |
final EvictionPolicy<T> policy = config.getEvictionPolicy(); | |
if (policy == null) { | |
// Use the class name (pre-2.6.0 compatible) | |
setEvictionPolicyClassName(config.getEvictionPolicyClassName()); | |
} else { | |
// Otherwise, use the class (2.6.0 feature) | |
setEvictionPolicy(policy); | |
} | |
setEvictorShutdownTimeoutMillis(config.getEvictorShutdownTimeoutMillis()); | |
} | |
/** | |
* Returns the maximum amount of time (in milliseconds) the | |
* {@code borrowObject()} method should block before throwing an | |
* exception when the pool is exhausted and | |
* {@link #getBlockWhenExhausted} is true. When less than 0, the | |
* {@code borrowObject()} method may block indefinitely. | |
* | |
* @return the maximum number of milliseconds {@code borrowObject()} | |
* will block. | |
* | |
* @see #setMaxWaitMillis | |
* @see #setBlockWhenExhausted | |
*/ | |
public final long getMaxWaitMillis() { | |
return maxWaitMillis; | |
} | |
/** | |
* Sets the maximum amount of time (in milliseconds) the | |
* {@code borrowObject()} method should block before throwing an | |
* exception when the pool is exhausted and | |
* {@link #getBlockWhenExhausted} is true. When less than 0, the | |
* {@code borrowObject()} method may block indefinitely. | |
* | |
* @param maxWaitMillis the maximum number of milliseconds | |
* {@code borrowObject()} will block or negative | |
* for indefinitely. | |
* | |
* @see #getMaxWaitMillis | |
* @see #setBlockWhenExhausted | |
*/ | |
public final void setMaxWaitMillis(final long maxWaitMillis) { | |
this.maxWaitMillis = maxWaitMillis; | |
} | |
/** | |
* Returns whether the pool has LIFO (last in, first out) behavior with | |
* respect to idle objects - always returning the most recently used object | |
* from the pool, or as a FIFO (first in, first out) queue, where the pool | |
* always returns the oldest object in the idle object pool. | |
* | |
* @return {@code true} if the pool is configured with LIFO behavior | |
* or {@code false} if the pool is configured with FIFO | |
* behavior | |
* | |
* @see #setLifo | |
*/ | |
public final boolean getLifo() { | |
return lifo; | |
} | |
/** | |
* Returns whether or not the pool serves threads waiting to borrow objects fairly. | |
* True means that waiting threads are served as if waiting in a FIFO queue. | |
* | |
* @return {@code true} if waiting threads are to be served | |
* by the pool in arrival order | |
*/ | |
public final boolean getFairness() { | |
return fairness; | |
} | |
/** | |
* Sets whether the pool has LIFO (last in, first out) behavior with | |
* respect to idle objects - always returning the most recently used object | |
* from the pool, or as a FIFO (first in, first out) queue, where the pool | |
* always returns the oldest object in the idle object pool. | |
* | |
* @param lifo {@code true} if the pool is to be configured with LIFO | |
* behavior or {@code false} if the pool is to be | |
* configured with FIFO behavior | |
* | |
* @see #getLifo() | |
*/ | |
public final void setLifo(final boolean lifo) { | |
this.lifo = lifo; | |
} | |
/** | |
* Returns whether objects created for the pool will be validated before | |
* being returned from the {@code borrowObject()} method. Validation is | |
* performed by the {@code validateObject()} method of the factory | |
* associated with the pool. If the object fails to validate, then | |
* {@code borrowObject()} will fail. | |
* | |
* @return {@code true} if newly created objects are validated before | |
* being returned from the {@code borrowObject()} method | |
* | |
* @see #setTestOnCreate | |
* | |
* @since 2.2 | |
*/ | |
public final boolean getTestOnCreate() { | |
return testOnCreate; | |
} | |
/** | |
* Sets whether objects created for the pool will be validated before | |
* being returned from the {@code borrowObject()} method. Validation is | |
* performed by the {@code validateObject()} method of the factory | |
* associated with the pool. If the object fails to validate, then | |
* {@code borrowObject()} will fail. | |
* | |
* @param testOnCreate {@code true} if newly created objects should be | |
* validated before being returned from the | |
* {@code borrowObject()} method | |
* | |
* @see #getTestOnCreate | |
* | |
* @since 2.2 | |
*/ | |
public final void setTestOnCreate(final boolean testOnCreate) { | |
this.testOnCreate = testOnCreate; | |
} | |
/** | |
* Returns whether objects borrowed from the pool will be validated before | |
* being returned from the {@code borrowObject()} method. Validation is | |
* performed by the {@code validateObject()} method of the factory | |
* associated with the pool. If the object fails to validate, it will be | |
* removed from the pool and destroyed, and a new attempt will be made to | |
* borrow an object from the pool. | |
* | |
* @return {@code true} if objects are validated before being returned | |
* from the {@code borrowObject()} method | |
* | |
* @see #setTestOnBorrow | |
*/ | |
public final boolean getTestOnBorrow() { | |
return testOnBorrow; | |
} | |
/** | |
* Sets whether objects borrowed from the pool will be validated before | |
* being returned from the {@code borrowObject()} method. Validation is | |
* performed by the {@code validateObject()} method of the factory | |
* associated with the pool. If the object fails to validate, it will be | |
* removed from the pool and destroyed, and a new attempt will be made to | |
* borrow an object from the pool. | |
* | |
* @param testOnBorrow {@code true} if objects should be validated | |
* before being returned from the | |
* {@code borrowObject()} method | |
* | |
* @see #getTestOnBorrow | |
*/ | |
public final void setTestOnBorrow(final boolean testOnBorrow) { | |
this.testOnBorrow = testOnBorrow; | |
} | |
/** | |
* Returns whether objects borrowed from the pool will be validated when | |
* they are returned to the pool via the {@code returnObject()} method. | |
* Validation is performed by the {@code validateObject()} method of | |
* the factory associated with the pool. Returning objects that fail validation | |
* are destroyed rather then being returned the pool. | |
* | |
* @return {@code true} if objects are validated on return to | |
* the pool via the {@code returnObject()} method | |
* | |
* @see #setTestOnReturn | |
*/ | |
public final boolean getTestOnReturn() { | |
return testOnReturn; | |
} | |
/** | |
* Sets whether objects borrowed from the pool will be validated when | |
* they are returned to the pool via the {@code returnObject()} method. | |
* Validation is performed by the {@code validateObject()} method of | |
* the factory associated with the pool. Returning objects that fail validation | |
* are destroyed rather then being returned the pool. | |
* | |
* @param testOnReturn {@code true} if objects are validated on | |
* return to the pool via the | |
* {@code returnObject()} method | |
* | |
* @see #getTestOnReturn | |
*/ | |
public final void setTestOnReturn(final boolean testOnReturn) { | |
this.testOnReturn = testOnReturn; | |
} | |
/** | |
* Returns whether objects sitting idle in the pool will be validated by the | |
* idle object evictor (if any - see | |
* {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed | |
* by the {@code validateObject()} method of the factory associated | |
* with the pool. If the object fails to validate, it will be removed from | |
* the pool and destroyed. | |
* | |
* @return {@code true} if objects will be validated by the evictor | |
* | |
* @see #setTestWhileIdle | |
* @see #setTimeBetweenEvictionRunsMillis | |
*/ | |
public final boolean getTestWhileIdle() { | |
return testWhileIdle; | |
} | |
/** | |
* Returns whether objects sitting idle in the pool will be validated by the | |
* idle object evictor (if any - see | |
* {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed | |
* by the {@code validateObject()} method of the factory associated | |
* with the pool. If the object fails to validate, it will be removed from | |
* the pool and destroyed. Note that setting this property has no effect | |
* unless the idle object evictor is enabled by setting | |
* {@code timeBetweenEvictionRunsMillis} to a positive value. | |
* | |
* @param testWhileIdle | |
* {@code true} so objects will be validated by the evictor | |
* | |
* @see #getTestWhileIdle | |
* @see #setTimeBetweenEvictionRunsMillis | |
*/ | |
public final void setTestWhileIdle(final boolean testWhileIdle) { | |
this.testWhileIdle = testWhileIdle; | |
} | |
/** | |
* 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 number of milliseconds to sleep between evictor runs | |
* | |
* @see #setTimeBetweenEvictionRunsMillis | |
*/ | |
public final long getTimeBetweenEvictionRunsMillis() { | |
return timeBetweenEvictionRunsMillis; | |
} | |
/** | |
* Sets the number of milliseconds to sleep between runs of the idle object evictor thread. | |
* <ul> | |
* <li>When positive, the idle object evictor thread starts.</li> | |
* <li>When non-positive, no idle object evictor thread runs.</li> | |
* </ul> | |
* | |
* @param timeBetweenEvictionRunsMillis | |
* number of milliseconds to sleep between evictor runs | |
* | |
* @see #getTimeBetweenEvictionRunsMillis | |
*/ | |
public final void setTimeBetweenEvictionRunsMillis( | |
final long timeBetweenEvictionRunsMillis) { | |
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; | |
startEvictor(timeBetweenEvictionRunsMillis); | |
} | |
/** | |
* Returns the maximum number of objects to examine during each run (if any) | |
* of the idle object evictor thread. When positive, the number of tests | |
* performed for a run will be the minimum of the configured value and the | |
* number of idle instances in the pool. When negative, the number of tests | |
* performed will be <code>ceil({@link #getNumIdle}/ | |
* abs({@link #getNumTestsPerEvictionRun}))</code> which means that when the | |
* value is {@code -n} roughly one nth of the idle objects will be | |
* tested per run. | |
* | |
* @return max number of objects to examine during each evictor run | |
* | |
* @see #setNumTestsPerEvictionRun | |
* @see #setTimeBetweenEvictionRunsMillis | |
*/ | |
public final int getNumTestsPerEvictionRun() { | |
return numTestsPerEvictionRun; | |
} | |
/** | |
* Sets the maximum number of objects to examine during each run (if any) | |
* of the idle object evictor thread. When positive, the number of tests | |
* performed for a run will be the minimum of the configured value and the | |
* number of idle instances in the pool. When negative, the number of tests | |
* performed will be <code>ceil({@link #getNumIdle}/ | |
* abs({@link #getNumTestsPerEvictionRun}))</code> which means that when the | |
* value is {@code -n} roughly one nth of the idle objects will be | |
* tested per run. | |
* | |
* @param numTestsPerEvictionRun | |
* max number of objects to examine during each evictor run | |
* | |
* @see #getNumTestsPerEvictionRun | |
* @see #setTimeBetweenEvictionRunsMillis | |
*/ | |
public final void setNumTestsPerEvictionRun(final 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 - | |
* see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive, | |
* no objects will be evicted from the pool due to idle time alone. | |
* | |
* @return minimum amount of time an object may sit idle in the pool before | |
* it is eligible for eviction | |
* | |
* @see #setMinEvictableIdleTimeMillis | |
* @see #setTimeBetweenEvictionRunsMillis | |
*/ | |
public final long getMinEvictableIdleTimeMillis() { | |
return minEvictableIdleTimeMillis; | |
} | |
/** | |
* Sets the minimum amount of time an object may sit idle in the pool | |
* before it is eligible for eviction by the idle object evictor (if any - | |
* see {@link #setTimeBetweenEvictionRunsMillis(long)}). 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 final void setMinEvictableIdleTimeMillis( | |
final long minEvictableIdleTimeMillis) { | |
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; | |
} | |
/** | |
* 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 - | |
* see {@link #setTimeBetweenEvictionRunsMillis(long)}), | |
* with the extra condition that at least {@code minIdle} object | |
* instances remain in the pool. This setting is overridden by | |
* {@link #getMinEvictableIdleTimeMillis} (that is, if | |
* {@link #getMinEvictableIdleTimeMillis} is positive, then | |
* {@link #getSoftMinEvictableIdleTimeMillis} is ignored). | |
* | |
* @return minimum amount of time an object may sit idle in the pool before | |
* it is eligible for eviction if minIdle instances are available | |
* | |
* @see #setSoftMinEvictableIdleTimeMillis | |
*/ | |
public final long getSoftMinEvictableIdleTimeMillis() { | |
return softMinEvictableIdleTimeMillis; | |
} | |
/** | |
* 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 - | |
* see {@link #setTimeBetweenEvictionRunsMillis(long)}), | |
* with the extra condition that at least {@code minIdle} object | |
* instances remain in the pool. This setting is overridden by | |
* {@link #getMinEvictableIdleTimeMillis} (that is, if | |
* {@link #getMinEvictableIdleTimeMillis} is positive, then | |
* {@link #getSoftMinEvictableIdleTimeMillis} is ignored). | |
* | |
* @param softMinEvictableIdleTimeMillis | |
* minimum amount of time an object may sit idle in the pool | |
* before it is eligible for eviction if minIdle instances are | |
* available | |
* | |
* @see #getSoftMinEvictableIdleTimeMillis | |
*/ | |
public final void setSoftMinEvictableIdleTimeMillis( | |
final long softMinEvictableIdleTimeMillis) { | |
this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; | |
} | |
/** | |
* Returns the name of the {@link EvictionPolicy} implementation that is | |
* used by this pool. | |
* | |
* @return The fully qualified class name of the {@link EvictionPolicy} | |
* | |
* @see #setEvictionPolicyClassName(String) | |
*/ | |
public final String getEvictionPolicyClassName() { | |
return evictionPolicy.getClass().getName(); | |
} | |
/** | |
* Sets the eviction policy for this pool. | |
* | |
* @param evictionPolicy | |
* the eviction policy for this pool. | |
* @since 2.6.0 | |
*/ | |
public void setEvictionPolicy(final EvictionPolicy<T> evictionPolicy) { | |
this.evictionPolicy = evictionPolicy; | |
} | |
/** | |
* Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to | |
* load the class using the given class loader. If that fails, use the class loader for the {@link EvictionPolicy} | |
* interface. | |
* | |
* @param evictionPolicyClassName | |
* the fully qualified class name of the new eviction policy | |
* @param classLoader | |
* the class loader to load the given {@code evictionPolicyClassName}. | |
* | |
* @see #getEvictionPolicyClassName() | |
* @since 2.6.0 If loading the class using the given class loader fails, use the class loader for the | |
* {@link EvictionPolicy} interface. | |
*/ | |
public final void setEvictionPolicyClassName(final String evictionPolicyClassName, final ClassLoader classLoader) { | |
// Getting epClass here and now best matches the caller's environment | |
final Class<?> epClass = EvictionPolicy.class; | |
final ClassLoader epClassLoader = epClass.getClassLoader(); | |
try { | |
try { | |
setEvictionPolicy(evictionPolicyClassName, classLoader); | |
} catch (final ClassCastException | ClassNotFoundException e) { | |
setEvictionPolicy(evictionPolicyClassName, epClassLoader); | |
} | |
} catch (final ClassCastException e) { | |
throw new IllegalArgumentException("Class " + evictionPolicyClassName + " from class loaders [" + | |
classLoader + ", " + epClassLoader + "] do not implement " + EVICTION_POLICY_TYPE_NAME); | |
} catch (final ClassNotFoundException | InstantiationException | IllegalAccessException | | |
InvocationTargetException | NoSuchMethodException e) { | |
final String exMessage = "Unable to create " + EVICTION_POLICY_TYPE_NAME + " instance of type " + | |
evictionPolicyClassName; | |
throw new IllegalArgumentException(exMessage, e); | |
} | |
} | |
/** | |
* Sets the eviction policy. | |
* | |
* @param className Eviction policy class name. | |
* @param classLoader Load the class from this class loader. | |
*/ | |
@SuppressWarnings("unchecked") | |
private void setEvictionPolicy(final String className, final ClassLoader classLoader) | |
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { | |
final Class<?> clazz = Class.forName(className, true, classLoader); | |
final Object policy = clazz.getConstructor().newInstance(); | |
this.evictionPolicy = (EvictionPolicy<T>) policy; | |
} | |
/** | |
* Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to | |
* load the class using the thread context class loader. If that fails, the use the class loader for the | |
* {@link EvictionPolicy} interface. | |
* | |
* @param evictionPolicyClassName | |
* the fully qualified class name of the new eviction policy | |
* | |
* @see #getEvictionPolicyClassName() | |
* @since 2.6.0 If loading the class using the thread context class loader fails, use the class loader for the | |
* {@link EvictionPolicy} interface. | |
*/ | |
public final void setEvictionPolicyClassName(final String evictionPolicyClassName) { | |
setEvictionPolicyClassName(evictionPolicyClassName, Thread.currentThread().getContextClassLoader()); | |
} | |
/** | |
* Gets the timeout that will be used when waiting for the Evictor to | |
* shutdown if this pool is closed and it is the only pool still using the | |
* the value for the Evictor. | |
* | |
* @return The timeout in milliseconds that will be used while waiting for | |
* the Evictor to shut down. | |
*/ | |
public final long getEvictorShutdownTimeoutMillis() { | |
return evictorShutdownTimeoutMillis; | |
} | |
/** | |
* Sets the timeout that will be used when waiting for the Evictor to | |
* shutdown if this pool is closed and it is the only pool still using the | |
* the value for the Evictor. | |
* | |
* @param evictorShutdownTimeoutMillis the timeout in milliseconds that | |
* will be used while waiting for the | |
* Evictor to shut down. | |
*/ | |
public final void setEvictorShutdownTimeoutMillis( | |
final long evictorShutdownTimeoutMillis) { | |
this.evictorShutdownTimeoutMillis = evictorShutdownTimeoutMillis; | |
} | |
/** | |
* Closes the pool, destroys the remaining idle objects and, if registered | |
* in JMX, deregisters it. | |
*/ | |
public abstract void close(); | |
/** | |
* Has this pool instance been closed. | |
* @return {@code true} when this pool has been closed. | |
*/ | |
public final boolean isClosed() { | |
return closed; | |
} | |
/** | |
* <p>Perform {@code numTests} idle object eviction tests, evicting | |
* examined objects that meet the criteria for eviction. If | |
* {@code testWhileIdle} is true, examined objects are validated | |
* when visited (and removed if invalid); otherwise only objects that | |
* have been idle for more than {@code minEvicableIdleTimeMillis} | |
* are removed.</p> | |
* | |
* @throws Exception when there is a problem evicting idle objects. | |
*/ | |
public abstract void evict() throws Exception; | |
/** | |
* Returns the {@link EvictionPolicy} defined for this pool. | |
* | |
* @return the eviction policy | |
* @since 2.4 | |
* @since 2.6.0 Changed access from protected to public. | |
*/ | |
public EvictionPolicy<T> getEvictionPolicy() { | |
return evictionPolicy; | |
} | |
/** | |
* Verifies that the pool is open. | |
* @throws IllegalStateException if the pool is closed. | |
*/ | |
final void assertOpen() throws IllegalStateException { | |
if (isClosed()) { | |
throw new IllegalStateException("Pool not open"); | |
} | |
} | |
/** | |
* <p>Starts the evictor with the given delay. If there is an evictor | |
* running when this method is called, it is stopped and replaced with a | |
* new evictor with the specified delay.</p> | |
* | |
* <p>This method needs to be final, since it is called from a constructor. | |
* See POOL-195.</p> | |
* | |
* @param delay time in milliseconds before start and between eviction runs | |
*/ | |
final void startEvictor(final long delay) { | |
synchronized (evictionLock) { | |
if (evictor == null) { // Starting evictor for the first time or after a cancel | |
if (delay > 0) { // Starting new evictor | |
evictor = new Evictor(); | |
EvictionTimer.schedule(evictor, delay, delay); | |
} | |
} else { // Stop or restart of existing evictor | |
if (delay > 0) { // Restart | |
synchronized (EvictionTimer.class) { // Ensure no cancel can happen between cancel / schedule calls | |
EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS, true); | |
evictor = null; | |
evictionIterator = null; | |
evictor = new Evictor(); | |
EvictionTimer.schedule(evictor, delay, delay); | |
} | |
} else { // Stopping evictor | |
EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS, false); | |
} | |
} | |
} | |
} | |
/** | |
* Stops the evictor. | |
*/ | |
void stopEvictor() { | |
startEvictor(-1L); | |
} | |
/** | |
* Tries to ensure that the configured minimum number of idle instances are | |
* available in the pool. | |
* @throws Exception if an error occurs creating idle instances | |
*/ | |
abstract void ensureMinIdle() throws Exception; | |
// Monitoring (primarily JMX) related methods | |
/** | |
* Provides the name under which the pool has been registered with the | |
* platform MBean server or {@code null} if the pool has not been | |
* registered. | |
* @return the JMX name | |
*/ | |
public final ObjectName getJmxName() { | |
return objectName; | |
} | |
/** | |
* Provides the stack trace for the call that created this pool. JMX | |
* registration may trigger a memory leak so it is important that pools are | |
* deregistered when no longer used by calling the {@link #close()} method. | |
* This method is provided to assist with identifying code that creates but | |
* does not close it thereby creating a memory leak. | |
* @return pool creation stack trace | |
*/ | |
public final String getCreationStackTrace() { | |
return creationStackTrace; | |
} | |
/** | |
* The total number of objects successfully borrowed from this pool over the | |
* lifetime of the pool. | |
* @return the borrowed object count | |
*/ | |
public final long getBorrowedCount() { | |
return borrowedCount.get(); | |
} | |
/** | |
* The total number of objects returned to this pool over the lifetime of | |
* the pool. This excludes attempts to return the same object multiple | |
* times. | |
* @return the returned object count | |
*/ | |
public final long getReturnedCount() { | |
return returnedCount.get(); | |
} | |
/** | |
* The total number of objects created for this pool over the lifetime of | |
* the pool. | |
* @return the created object count | |
*/ | |
public final long getCreatedCount() { | |
return createdCount.get(); | |
} | |
/** | |
* The total number of objects destroyed by this pool over the lifetime of | |
* the pool. | |
* @return the destroyed object count | |
*/ | |
public final long getDestroyedCount() { | |
return destroyedCount.get(); | |
} | |
/** | |
* The total number of objects destroyed by the evictor associated with this | |
* pool over the lifetime of the pool. | |
* @return the evictor destroyed object count | |
*/ | |
public final long getDestroyedByEvictorCount() { | |
return destroyedByEvictorCount.get(); | |
} | |
/** | |
* The total number of objects destroyed by this pool as a result of failing | |
* validation during {@code borrowObject()} over the lifetime of the | |
* pool. | |
* @return validation destroyed object count | |
*/ | |
public final long getDestroyedByBorrowValidationCount() { | |
return destroyedByBorrowValidationCount.get(); | |
} | |
/** | |
* The mean time objects are active for based on the last {@link | |
* #MEAN_TIMING_STATS_CACHE_SIZE} objects returned to the pool. | |
* @return mean time an object has been checked out from the pool among | |
* recently returned objects | |
*/ | |
public final long getMeanActiveTimeMillis() { | |
return activeTimes.getMean(); | |
} | |
/** | |
* The mean time objects are idle for based on the last {@link | |
* #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. | |
* @return mean time an object has been idle in the pool among recently | |
* borrowed objects | |
*/ | |
public final long getMeanIdleTimeMillis() { | |
return idleTimes.getMean(); | |
} | |
/** | |
* The mean time threads wait to borrow an object based on the last {@link | |
* #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. | |
* @return mean time in milliseconds that a recently served thread has had | |
* to wait to borrow an object from the pool | |
*/ | |
public final long getMeanBorrowWaitTimeMillis() { | |
return waitTimes.getMean(); | |
} | |
/** | |
* The maximum time a thread has waited to borrow objects from the pool. | |
* @return maximum wait time in milliseconds since the pool was created | |
*/ | |
public final long getMaxBorrowWaitTimeMillis() { | |
return maxBorrowWaitTimeMillis.get(); | |
} | |
/** | |
* The number of instances currently idle in this pool. | |
* @return count of instances available for checkout from the pool | |
*/ | |
public abstract int getNumIdle(); | |
/** | |
* The listener used (if any) to receive notifications of exceptions | |
* unavoidably swallowed by the pool. | |
* | |
* @return The listener or {@code null} for no listener | |
*/ | |
public final SwallowedExceptionListener getSwallowedExceptionListener() { | |
return swallowedExceptionListener; | |
} | |
/** | |
* The listener used (if any) to receive notifications of exceptions | |
* unavoidably swallowed by the pool. | |
* | |
* @param swallowedExceptionListener The listener or {@code null} | |
* for no listener | |
*/ | |
public final void setSwallowedExceptionListener( | |
final SwallowedExceptionListener swallowedExceptionListener) { | |
this.swallowedExceptionListener = swallowedExceptionListener; | |
} | |
/** | |
* Swallows an exception and notifies the configured listener for swallowed | |
* exceptions queue. | |
* | |
* @param swallowException exception to be swallowed | |
*/ | |
final void swallowException(final Exception swallowException) { | |
final SwallowedExceptionListener listener = getSwallowedExceptionListener(); | |
if (listener == null) { | |
return; | |
} | |
try { | |
listener.onSwallowException(swallowException); | |
} catch (final VirtualMachineError e) { | |
throw e; | |
} catch (final Throwable t) { | |
// Ignore. Enjoy the irony. | |
} | |
} | |
/** | |
* Updates statistics after an object is borrowed from the pool. | |
* @param p object borrowed from the pool | |
* @param waitTime time (in milliseconds) that the borrowing thread had to wait | |
*/ | |
final void updateStatsBorrow(final PooledObject<T> p, final long waitTime) { | |
borrowedCount.incrementAndGet(); | |
idleTimes.add(p.getIdleTimeMillis()); | |
waitTimes.add(waitTime); | |
// lock-free optimistic-locking maximum | |
long currentMax; | |
do { | |
currentMax = maxBorrowWaitTimeMillis.get(); | |
if (currentMax >= waitTime) { | |
break; | |
} | |
} while (!maxBorrowWaitTimeMillis.compareAndSet(currentMax, waitTime)); | |
} | |
/** | |
* Updates statistics after an object is returned to the pool. | |
* @param activeTime the amount of time (in milliseconds) that the returning | |
* object was checked out | |
*/ | |
final void updateStatsReturn(final long activeTime) { | |
returnedCount.incrementAndGet(); | |
activeTimes.add(activeTime); | |
} | |
/** | |
* Marks the object as returning to the pool. | |
* @param pooledObject instance to return to the keyed pool | |
*/ | |
protected void markReturningState(final PooledObject<T> pooledObject) { | |
synchronized(pooledObject) { | |
final PooledObjectState state = pooledObject.getState(); | |
if (state != PooledObjectState.ALLOCATED) { | |
throw new IllegalStateException( | |
"Object has already been returned to this pool or is invalid"); | |
} | |
pooledObject.markReturning(); // Keep from being marked abandoned | |
} | |
} | |
/** | |
* Unregisters this pool's MBean. | |
*/ | |
final void jmxUnregister() { | |
if (objectName != null) { | |
try { | |
ManagementFactory.getPlatformMBeanServer().unregisterMBean( | |
objectName); | |
} catch (final MBeanRegistrationException | InstanceNotFoundException e) { | |
swallowException(e); | |
} | |
} | |
} | |
/** | |
* Registers the pool with the platform MBean server. | |
* The registered name will be | |
* {@code jmxNameBase + jmxNamePrefix + i} where i is the least | |
* integer greater than or equal to 1 such that the name is not already | |
* registered. Swallows MBeanRegistrationException, NotCompliantMBeanException | |
* returning null. | |
* | |
* @param config Pool configuration | |
* @param jmxNameBase default base JMX name for this pool | |
* @param jmxNamePrefix name prefix | |
* @return registered ObjectName, null if registration fails | |
*/ | |
private ObjectName jmxRegister(final BaseObjectPoolConfig<T> config, | |
final String jmxNameBase, String jmxNamePrefix) { | |
ObjectName newObjectName = null; | |
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); | |
int i = 1; | |
boolean registered = false; | |
String base = config.getJmxNameBase(); | |
if (base == null) { | |
base = jmxNameBase; | |
} | |
while (!registered) { | |
try { | |
ObjectName objName; | |
// Skip the numeric suffix for the first pool in case there is | |
// only one so the names are cleaner. | |
if (i == 1) { | |
objName = new ObjectName(base + jmxNamePrefix); | |
} else { | |
objName = new ObjectName(base + jmxNamePrefix + i); | |
} | |
mbs.registerMBean(this, objName); | |
newObjectName = objName; | |
registered = true; | |
} catch (final MalformedObjectNameException e) { | |
if (BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX.equals( | |
jmxNamePrefix) && jmxNameBase.equals(base)) { | |
// Shouldn't happen. Skip registration if it does. | |
registered = true; | |
} else { | |
// Must be an invalid name. Use the defaults instead. | |
jmxNamePrefix = | |
BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX; | |
base = jmxNameBase; | |
} | |
} catch (final InstanceAlreadyExistsException e) { | |
// Increment the index and try again | |
i++; | |
} catch (final MBeanRegistrationException | NotCompliantMBeanException e) { | |
// Shouldn't happen. Skip registration if it does. | |
registered = true; | |
} | |
} | |
return newObjectName; | |
} | |
/** | |
* Gets the stack trace of an exception as a string. | |
* @param e exception to trace | |
* @return exception stack trace as a string | |
*/ | |
private String getStackTrace(final Exception e) { | |
// Need the exception in string form to prevent the retention of | |
// references to classes in the stack trace that could trigger a memory | |
// leak in a container environment. | |
final Writer w = new StringWriter(); | |
final PrintWriter pw = new PrintWriter(w); | |
e.printStackTrace(pw); | |
return w.toString(); | |
} | |
// Inner classes | |
/** | |
* The idle object evictor {@link TimerTask}. | |
* | |
* @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis | |
*/ | |
class Evictor implements Runnable { | |
private ScheduledFuture<?> scheduledFuture; | |
/** | |
* Run pool maintenance. Evict objects qualifying for eviction and then | |
* ensure that the minimum number of idle instances are available. | |
* Since the Timer that invokes Evictors is shared for all Pools but | |
* pools may exist in different class loaders, the Evictor ensures that | |
* any actions taken are under the class loader of the factory | |
* associated with the pool. | |
*/ | |
@Override | |
public void run() { | |
final ClassLoader savedClassLoader = | |
Thread.currentThread().getContextClassLoader(); | |
try { | |
if (factoryClassLoader != null) { | |
// Set the class loader for the factory | |
final ClassLoader cl = factoryClassLoader.get(); | |
if (cl == null) { | |
// The pool has been dereferenced and the class loader | |
// GC'd. Cancel this timer so the pool can be GC'd as | |
// well. | |
cancel(); | |
return; | |
} | |
Thread.currentThread().setContextClassLoader(cl); | |
} | |
// Evict from the pool | |
try { | |
evict(); | |
} catch(final Exception e) { | |
swallowException(e); | |
} catch(final 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 (final Exception e) { | |
swallowException(e); | |
} | |
} finally { | |
// Restore the previous CCL | |
Thread.currentThread().setContextClassLoader(savedClassLoader); | |
} | |
} | |
/** | |
* Sets the scheduled future. | |
* | |
* @param scheduledFuture the scheduled future. | |
*/ | |
void setScheduledFuture(final ScheduledFuture<?> scheduledFuture) { | |
this.scheduledFuture = scheduledFuture; | |
} | |
/** | |
* Cancels the scheduled future. | |
*/ | |
void cancel() { | |
scheduledFuture.cancel(false); | |
} | |
} | |
/** | |
* Maintains a cache of values for a single metric and reports | |
* statistics on the cached values. | |
*/ | |
private class StatsStore { | |
private final AtomicLong values[]; | |
private final int size; | |
private int index; | |
/** | |
* Create a StatsStore with the given cache size. | |
* | |
* @param size number of values to maintain in the cache. | |
*/ | |
public StatsStore(final int size) { | |
this.size = size; | |
values = new AtomicLong[size]; | |
for (int i = 0; i < size; i++) { | |
values[i] = new AtomicLong(-1); | |
} | |
} | |
/** | |
* Adds a value to the cache. If the cache is full, one of the | |
* existing values is replaced by the new value. | |
* | |
* @param value new value to add to the cache. | |
*/ | |
public synchronized void add(final long value) { | |
values[index].set(value); | |
index++; | |
if (index == size) { | |
index = 0; | |
} | |
} | |
/** | |
* Returns the mean of the cached values. | |
* | |
* @return the mean of the cache, truncated to long | |
*/ | |
public long getMean() { | |
double result = 0; | |
int counter = 0; | |
for (int i = 0; i < size; i++) { | |
final long value = values[i].get(); | |
if (value != -1) { | |
counter++; | |
result = result * ((counter - 1) / (double) counter) + | |
value/(double) counter; | |
} | |
} | |
return (long) result; | |
} | |
@Override | |
public String toString() { | |
final StringBuilder builder = new StringBuilder(); | |
builder.append("StatsStore [values="); | |
builder.append(Arrays.toString(values)); | |
builder.append(", size="); | |
builder.append(size); | |
builder.append(", index="); | |
builder.append(index); | |
builder.append("]"); | |
return builder.toString(); | |
} | |
} | |
/** | |
* The idle object eviction iterator. Holds a reference to the idle objects. | |
*/ | |
class EvictionIterator implements Iterator<PooledObject<T>> { | |
private final Deque<PooledObject<T>> idleObjects; | |
private final Iterator<PooledObject<T>> idleObjectIterator; | |
/** | |
* Create an EvictionIterator for the provided idle instance deque. | |
* @param idleObjects underlying deque | |
*/ | |
EvictionIterator(final Deque<PooledObject<T>> idleObjects) { | |
this.idleObjects = idleObjects; | |
if (getLifo()) { | |
idleObjectIterator = idleObjects.descendingIterator(); | |
} else { | |
idleObjectIterator = idleObjects.iterator(); | |
} | |
} | |
/** | |
* Returns the idle object deque referenced by this iterator. | |
* @return the idle object deque | |
*/ | |
public Deque<PooledObject<T>> getIdleObjects() { | |
return idleObjects; | |
} | |
/** {@inheritDoc} */ | |
@Override | |
public boolean hasNext() { | |
return idleObjectIterator.hasNext(); | |
} | |
/** {@inheritDoc} */ | |
@Override | |
public PooledObject<T> next() { | |
return idleObjectIterator.next(); | |
} | |
/** {@inheritDoc} */ | |
@Override | |
public void remove() { | |
idleObjectIterator.remove(); | |
} | |
} | |
/** | |
* Wrapper for objects under management by the pool. | |
* | |
* GenericObjectPool and GenericKeyedObjectPool maintain references to all | |
* objects under management using maps keyed on the objects. This wrapper | |
* class ensures that objects can work as hash keys. | |
* | |
* @param <T> type of objects in the pool | |
*/ | |
static class IdentityWrapper<T> { | |
/** Wrapped object */ | |
private final T instance; | |
/** | |
* Create a wrapper for an instance. | |
* | |
* @param instance object to wrap | |
*/ | |
public IdentityWrapper(final T instance) { | |
this.instance = instance; | |
} | |
@Override | |
public int hashCode() { | |
return System.identityHashCode(instance); | |
} | |
@Override | |
@SuppressWarnings("rawtypes") | |
public boolean equals(final Object other) { | |
return other instanceof IdentityWrapper && | |
((IdentityWrapper) other).instance == instance; | |
} | |
/** | |
* @return the wrapped object | |
*/ | |
public T getObject() { | |
return instance; | |
} | |
@Override | |
public String toString() { | |
final StringBuilder builder = new StringBuilder(); | |
builder.append("IdentityWrapper [instance="); | |
builder.append(instance); | |
builder.append("]"); | |
return builder.toString(); | |
} | |
} | |
@Override | |
protected void toStringAppendFields(final StringBuilder builder) { | |
builder.append("maxTotal="); | |
builder.append(maxTotal); | |
builder.append(", blockWhenExhausted="); | |
builder.append(blockWhenExhausted); | |
builder.append(", maxWaitMillis="); | |
builder.append(maxWaitMillis); | |
builder.append(", lifo="); | |
builder.append(lifo); | |
builder.append(", fairness="); | |
builder.append(fairness); | |
builder.append(", testOnCreate="); | |
builder.append(testOnCreate); | |
builder.append(", testOnBorrow="); | |
builder.append(testOnBorrow); | |
builder.append(", testOnReturn="); | |
builder.append(testOnReturn); | |
builder.append(", testWhileIdle="); | |
builder.append(testWhileIdle); | |
builder.append(", timeBetweenEvictionRunsMillis="); | |
builder.append(timeBetweenEvictionRunsMillis); | |
builder.append(", numTestsPerEvictionRun="); | |
builder.append(numTestsPerEvictionRun); | |
builder.append(", minEvictableIdleTimeMillis="); | |
builder.append(minEvictableIdleTimeMillis); | |
builder.append(", softMinEvictableIdleTimeMillis="); | |
builder.append(softMinEvictableIdleTimeMillis); | |
builder.append(", evictionPolicy="); | |
builder.append(evictionPolicy); | |
builder.append(", closeLock="); | |
builder.append(closeLock); | |
builder.append(", closed="); | |
builder.append(closed); | |
builder.append(", evictionLock="); | |
builder.append(evictionLock); | |
builder.append(", evictor="); | |
builder.append(evictor); | |
builder.append(", evictionIterator="); | |
builder.append(evictionIterator); | |
builder.append(", factoryClassLoader="); | |
builder.append(factoryClassLoader); | |
builder.append(", oname="); | |
builder.append(objectName); | |
builder.append(", creationStackTrace="); | |
builder.append(creationStackTrace); | |
builder.append(", borrowedCount="); | |
builder.append(borrowedCount); | |
builder.append(", returnedCount="); | |
builder.append(returnedCount); | |
builder.append(", createdCount="); | |
builder.append(createdCount); | |
builder.append(", destroyedCount="); | |
builder.append(destroyedCount); | |
builder.append(", destroyedByEvictorCount="); | |
builder.append(destroyedByEvictorCount); | |
builder.append(", destroyedByBorrowValidationCount="); | |
builder.append(destroyedByBorrowValidationCount); | |
builder.append(", activeTimes="); | |
builder.append(activeTimes); | |
builder.append(", idleTimes="); | |
builder.append(idleTimes); | |
builder.append(", waitTimes="); | |
builder.append(waitTimes); | |
builder.append(", maxBorrowWaitTimeMillis="); | |
builder.append(maxBorrowWaitTimeMillis); | |
builder.append(", swallowedExceptionListener="); | |
builder.append(swallowedExceptionListener); | |
} | |
} |