1412 lines
53 KiB
Java
1412 lines
53 KiB
Java
/*
|
|
* 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.tomcat.dbcp.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.tomcat.dbcp.pool2.BaseObject;
|
|
import org.apache.tomcat.dbcp.pool2.PooledObject;
|
|
import org.apache.tomcat.dbcp.pool2.PooledObjectState;
|
|
import org.apache.tomcat.dbcp.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()</code> method is
|
|
* invoked when the pool is exhausted (the maximum number of "active"
|
|
* objects has been reached).
|
|
*
|
|
* @return <code>true</code> if <code>borrowObject()</code> should block
|
|
* when the pool is exhausted
|
|
*
|
|
* @see #setBlockWhenExhausted
|
|
*/
|
|
public final boolean getBlockWhenExhausted() {
|
|
return blockWhenExhausted;
|
|
}
|
|
|
|
/**
|
|
* Sets whether to block when the <code>borrowObject()</code> method is
|
|
* invoked when the pool is exhausted (the maximum number of "active"
|
|
* objects has been reached).
|
|
*
|
|
* @param blockWhenExhausted <code>true</code> if
|
|
* <code>borrowObject()</code> should block
|
|
* when the pool is exhausted
|
|
*
|
|
* @see #getBlockWhenExhausted
|
|
*/
|
|
public final void setBlockWhenExhausted(final boolean blockWhenExhausted) {
|
|
this.blockWhenExhausted = blockWhenExhausted;
|
|
}
|
|
|
|
protected void setConfig(final BaseObjectPoolConfig<T> conf) {
|
|
setLifo(conf.getLifo());
|
|
setMaxWaitMillis(conf.getMaxWaitMillis());
|
|
setBlockWhenExhausted(conf.getBlockWhenExhausted());
|
|
setTestOnCreate(conf.getTestOnCreate());
|
|
setTestOnBorrow(conf.getTestOnBorrow());
|
|
setTestOnReturn(conf.getTestOnReturn());
|
|
setTestWhileIdle(conf.getTestWhileIdle());
|
|
setNumTestsPerEvictionRun(conf.getNumTestsPerEvictionRun());
|
|
setMinEvictableIdleTimeMillis(conf.getMinEvictableIdleTimeMillis());
|
|
setTimeBetweenEvictionRunsMillis(conf.getTimeBetweenEvictionRunsMillis());
|
|
setSoftMinEvictableIdleTimeMillis(conf.getSoftMinEvictableIdleTimeMillis());
|
|
final EvictionPolicy<T> policy = conf.getEvictionPolicy();
|
|
if (policy == null) {
|
|
// Use the class name (pre-2.6.0 compatible)
|
|
setEvictionPolicyClassName(conf.getEvictionPolicyClassName());
|
|
} else {
|
|
// Otherwise, use the class (2.6.0 feature)
|
|
setEvictionPolicy(policy);
|
|
}
|
|
setEvictorShutdownTimeoutMillis(conf.getEvictorShutdownTimeoutMillis());
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum amount of time (in milliseconds) the
|
|
* <code>borrowObject()</code> method should block before throwing an
|
|
* exception when the pool is exhausted and
|
|
* {@link #getBlockWhenExhausted} is true. When less than 0, the
|
|
* <code>borrowObject()</code> method may block indefinitely.
|
|
*
|
|
* @return the maximum number of milliseconds <code>borrowObject()</code>
|
|
* will block.
|
|
*
|
|
* @see #setMaxWaitMillis
|
|
* @see #setBlockWhenExhausted
|
|
*/
|
|
public final long getMaxWaitMillis() {
|
|
return maxWaitMillis;
|
|
}
|
|
|
|
/**
|
|
* Sets the maximum amount of time (in milliseconds) the
|
|
* <code>borrowObject()</code> method should block before throwing an
|
|
* exception when the pool is exhausted and
|
|
* {@link #getBlockWhenExhausted} is true. When less than 0, the
|
|
* <code>borrowObject()</code> method may block indefinitely.
|
|
*
|
|
* @param maxWaitMillis the maximum number of milliseconds
|
|
* <code>borrowObject()</code> 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) behaviour 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</code> if the pool is configured with LIFO behaviour
|
|
* or <code>false</code> if the pool is configured with FIFO
|
|
* behaviour
|
|
*
|
|
* @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</code> 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) behaviour 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</code> if the pool is to be configured with LIFO
|
|
* behaviour or <code>false</code> if the pool is to be
|
|
* configured with FIFO behaviour
|
|
*
|
|
* @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()</code> method. Validation is
|
|
* performed by the <code>validateObject()</code> method of the factory
|
|
* associated with the pool. If the object fails to validate, then
|
|
* <code>borrowObject()</code> will fail.
|
|
*
|
|
* @return <code>true</code> if newly created objects are validated before
|
|
* being returned from the <code>borrowObject()</code> 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()</code> method. Validation is
|
|
* performed by the <code>validateObject()</code> method of the factory
|
|
* associated with the pool. If the object fails to validate, then
|
|
* <code>borrowObject()</code> will fail.
|
|
*
|
|
* @param testOnCreate <code>true</code> if newly created objects should be
|
|
* validated before being returned from the
|
|
* <code>borrowObject()</code> 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()</code> method. Validation is
|
|
* performed by the <code>validateObject()</code> 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</code> if objects are validated before being returned
|
|
* from the <code>borrowObject()</code> 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()</code> method. Validation is
|
|
* performed by the <code>validateObject()</code> 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</code> if objects should be validated
|
|
* before being returned from the
|
|
* <code>borrowObject()</code> 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()</code> method.
|
|
* Validation is performed by the <code>validateObject()</code> method of
|
|
* the factory associated with the pool. Returning objects that fail validation
|
|
* are destroyed rather then being returned the pool.
|
|
*
|
|
* @return <code>true</code> if objects are validated on return to
|
|
* the pool via the <code>returnObject()</code> 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()</code> method.
|
|
* Validation is performed by the <code>validateObject()</code> 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</code> if objects are validated on
|
|
* return to the pool via the
|
|
* <code>returnObject()</code> 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()</code> 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</code> 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()</code> 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</code> to a positive value.
|
|
*
|
|
* @param testWhileIdle
|
|
* <code>true</code> 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</code> 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</code> 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</code> 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</code> 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);
|
|
}
|
|
}
|
|
|
|
@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</code> when this pool has been closed.
|
|
*/
|
|
public final boolean isClosed() {
|
|
return closed;
|
|
}
|
|
|
|
/**
|
|
* <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>
|
|
*
|
|
* @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) {
|
|
EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS);
|
|
evictor = null;
|
|
evictionIterator = null;
|
|
if (delay > 0) {
|
|
evictor = new Evictor();
|
|
EvictionTimer.schedule(evictor, delay, delay);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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</code> 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()</code> 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</code> 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</code>
|
|
* 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</code> 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);
|
|
}
|
|
}
|
|
|
|
|
|
void setScheduledFuture(final ScheduledFuture<?> scheduledFuture) {
|
|
this.scheduledFuture = scheduledFuture;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
}
|