org.apache.commons.pool2.impl.BaseGenericObjectPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-pool2 Show documentation
Show all versions of commons-pool2 Show documentation
Apache Commons Object Pooling Library
/*
* 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.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TimerTask;
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.PooledObject;
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 Type of element pooled in this pool.
*
* This class is intended to be thread-safe.
*
* @version $Revision: $
*
* @since 2.0
*/
public abstract class BaseGenericObjectPool {
// 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;
// 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 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 evictionPolicy;
// Internal (primarily state) attributes
final Object closeLock = new Object();
volatile boolean closed = false;
final Object evictionLock = new Object();
private Evictor evictor = null; // @GuardedBy("evictionLock")
Iterator> evictionIterator = null; // @GuardedBy("evictionLock")
/*
* Class loader for evictor thread to use since in a J2EE or similar
* environment the context class loader for the evictor thread may have
* visibility of the correct factory. See POOL-161.
*/
private final ClassLoader factoryClassLoader;
// Monitoring (primarily JMX) attributes
private final ObjectName oname;
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 LinkedList activeTimes = new LinkedList(); // @GuardedBy("activeTimes") - except in initStats()
private final LinkedList idleTimes = new LinkedList(); // @GuardedBy("activeTimes") - except in initStats()
private final LinkedList waitTimes = new LinkedList(); // @GuardedBy("activeTimes") - except in initStats()
private final Object maxBorrowWaitTimeMillisLock = new Object();
private volatile long maxBorrowWaitTimeMillis = 0; // @GuardedBy("maxBorrowWaitTimeMillisLock")
private 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(BaseObjectPoolConfig config,
String jmxNameBase, String jmxNamePrefix) {
if (config.getJmxEnabled()) {
this.oname = jmxRegister(config, jmxNameBase, jmxNamePrefix);
} else {
this.oname = null;
}
// Populate the creation stack trace
this.creationStackTrace = getStackTrace(new Exception());
// save the current CCL to be used later by the evictor Thread
factoryClassLoader = Thread.currentThread().getContextClassLoader();
// Initialise the attributes used to record rolling averages
initStats();
}
/**
* 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(int maxTotal) {
this.maxTotal = maxTotal;
}
/**
* Returns whether to block when the borrowObject()
method is
* invoked when the pool is exhausted (the maximum number of "active"
* objects has been reached).
*
* @return true
if borrowObject()
should block
* when the pool is exhausted
*
* @see #setBlockWhenExhausted
*/
public final boolean getBlockWhenExhausted() {
return blockWhenExhausted;
}
/**
* Sets whether to block when the borrowObject()
method is
* invoked when the pool is exhausted (the maximum number of "active"
* objects has been reached).
*
* @param blockWhenExhausted true
if
* borrowObject()
should block
* when the pool is exhausted
*
* @see #getBlockWhenExhausted
*/
public final void setBlockWhenExhausted(boolean blockWhenExhausted) {
this.blockWhenExhausted = blockWhenExhausted;
}
/**
* Returns the maximum amount of time (in milliseconds) the
* borrowObject()
method should block before throwing an
* exception when the pool is exhausted and
* {@link #getBlockWhenExhausted} is true. When less than 0, the
* borrowObject()
method may block indefinitely.
*
* @return the maximum number of milliseconds borrowObject()
* will block.
*
* @see #setMaxWaitMillis
* @see #setBlockWhenExhausted
*/
public final long getMaxWaitMillis() {
return maxWaitMillis;
}
/**
* Sets the maximum amount of time (in milliseconds) the
* borrowObject()
method should block before throwing an
* exception when the pool is exhausted and
* {@link #getBlockWhenExhausted} is true. When less than 0, the
* borrowObject()
method may block indefinitely.
*
* @param maxWaitMillis the maximum number of milliseconds
* borrowObject()
will block or negative
* for indefinitely.
*
* @see #getMaxWaitMillis
* @see #setBlockWhenExhausted
*/
public final void setMaxWaitMillis(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 true
if the pool is configured with LIFO behaviour
* or false
if the pool is configured with FIFO
* behaviour
*
* @see #setLifo
*/
public final boolean getLifo() {
return lifo;
}
/**
* 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 true
if the pool is to be configured with LIFO
* behaviour or false
if the pool is to be
* configured with FIFO behaviour
*
* @see #getLifo()
*/
public final void setLifo(boolean lifo) {
this.lifo = lifo;
}
/**
* Returns whether objects borrowed from the pool will be validated before
* being returned from the borrowObject()
method. Validation is
* performed by the 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 true
if objects are validated before being returned
* from the 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 borrowObject()
method. Validation is
* performed by the 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 true
if objects should be validated
* before being returned from the
* borrowObject()
method
*
* @see #getTestOnBorrow
*/
public final void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
/**
* Returns whether objects borrowed from the pool will be validated when
* they are returned to the pool via the returnObject()
method.
* Validation is performed by the validateObject()
method of
* the factory associated with the pool. Returning objects that fail validation
* are destroyed rather then being returned the pool.
*
* @return true
if objects are validated on return to
* the pool via the 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 returnObject()
method.
* Validation is performed by the validateObject()
method of
* the factory associated with the pool. Returning objects that fail validation
* are destroyed rather then being returned the pool.
*
* @param testOnReturn true
if objects are validated on
* return to the pool via the
* returnObject()
method
*
* @see #getTestOnReturn
*/
public final void setTestOnReturn(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 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 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 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
* timeBetweenEvictionRunsMillis
to a positive value.
*
* @param testWhileIdle
* true
so objects will be validated by the evictor
*
* @see #getTestWhileIdle
* @see #setTimeBetweenEvictionRunsMillis
*/
public final void setTestWhileIdle(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. When non-positive, no idle object evictor thread
* will be run.
*
* @param timeBetweenEvictionRunsMillis
* number of milliseconds to sleep between evictor runs
*
* @see #getTimeBetweenEvictionRunsMillis
*/
public final void setTimeBetweenEvictionRunsMillis(
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 ceil({@link #getNumIdle}/
* abs({@link #getNumTestsPerEvictionRun})) which means that when the value
* is -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 ceil({@link #getNumIdle}/
* abs({@link #getNumTestsPerEvictionRun})) which means that when the value
* is -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(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(
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 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 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(
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 name of the {@link EvictionPolicy} implementation that is
* used by this pool.
*
* @param evictionPolicyClassName the fully qualified class name of the
* new eviction policy
*
* @see #getEvictionPolicyClassName()
*/
public final void setEvictionPolicyClassName(
String evictionPolicyClassName) {
try {
Class> clazz = Class.forName(evictionPolicyClassName);
Object policy = clazz.newInstance();
if (policy instanceof EvictionPolicy>) {
@SuppressWarnings("unchecked") // safe, because we just checked the class
EvictionPolicy evicPolicy = (EvictionPolicy) policy;
this.evictionPolicy = evicPolicy;
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(
"Unable to create EvictionPolicy instance of type " +
evictionPolicyClassName, e);
} catch (InstantiationException e) {
throw new IllegalArgumentException(
"Unable to create EvictionPolicy instance of type " +
evictionPolicyClassName, e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(
"Unable to create EvictionPolicy instance of type " +
evictionPolicyClassName, e);
}
}
/**
* 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 true
when this pool has been closed.
*/
public final boolean isClosed() {
return closed;
}
/**
* Perform numTests
idle object eviction tests, evicting
* examined objects that meet the criteria for eviction. If
* testWhileIdle
is true, examined objects are validated
* when visited (and removed if invalid); otherwise only objects that
* have been idle for more than minEvicableIdleTimeMillis
* are removed.
*
* @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
*/
final EvictionPolicy 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");
}
}
/**
* 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.
*
* This method needs to be final, since it is called from a constructor.
* See POOL-195.
*
* @param delay time in milliseconds before start and between eviction runs
*/
final void startEvictor(long delay) {
synchronized (evictionLock) {
if (null != evictor) {
EvictionTimer.cancel(evictor);
evictor = null;
evictionIterator = null;
}
if (delay > 0) {
evictor = new Evictor();
EvictionTimer.schedule(evictor, delay, delay);
}
}
}
/**
* 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 null
if the pool has not been
* registered.
* @return the JMX name
*/
public final ObjectName getJmxName() {
return oname;
}
/**
* 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 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 getMeanFromStatsCache(activeTimes);
}
/**
* 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 getMeanFromStatsCache(idleTimes);
}
/**
* 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 getMeanFromStatsCache(waitTimes);
}
/**
* 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;
}
/**
* 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 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 null
* for no listener
*/
public final void setSwallowedExceptionListener(
SwallowedExceptionListener swallowedExceptionListener) {
this.swallowedExceptionListener = swallowedExceptionListener;
}
/**
* Swallows an exception and notifies the configured listener for swallowed
* exceptions queue.
*
* @param e exception to be swallowed
*/
final void swallowException(Exception e) {
SwallowedExceptionListener listener = getSwallowedExceptionListener();
if (listener == null) {
return;
}
try {
listener.onSwallowException(e);
} catch (OutOfMemoryError oome) {
throw oome;
} catch (VirtualMachineError vme) {
throw vme;
} catch (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(PooledObject p, long waitTime) {
borrowedCount.incrementAndGet();
synchronized (idleTimes) {
idleTimes.add(Long.valueOf(p.getIdleTimeMillis()));
idleTimes.poll();
}
synchronized (waitTimes) {
waitTimes.add(Long.valueOf(waitTime));
waitTimes.poll();
}
synchronized (maxBorrowWaitTimeMillisLock) {
if (waitTime > maxBorrowWaitTimeMillis) {
maxBorrowWaitTimeMillis = 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(long activeTime) {
returnedCount.incrementAndGet();
synchronized (activeTimes) {
activeTimes.add(Long.valueOf(activeTime));
activeTimes.poll();
}
}
/**
* Unregisters this pool's MBean.
*/
final void jmxUnregister() {
if (oname != null) {
try {
ManagementFactory.getPlatformMBeanServer().unregisterMBean(
oname);
} catch (MBeanRegistrationException e) {
swallowException(e);
} catch (InstanceNotFoundException e) {
swallowException(e);
}
}
}
/**
* Registers the pool with the platform MBean server.
* The registered name will be
* 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(BaseObjectPoolConfig config,
String jmxNameBase, String jmxNamePrefix) {
ObjectName objectName = null;
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);
objectName = objName;
registered = true;
} catch (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 (InstanceAlreadyExistsException e) {
// Increment the index and try again
i++;
} catch (MBeanRegistrationException e) {
// Shouldn't happen. Skip registration if it does.
registered = true;
} catch (NotCompliantMBeanException e) {
// Shouldn't happen. Skip registration if it does.
registered = true;
}
}
return objectName;
}
/**
* 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(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.
Writer w = new StringWriter();
PrintWriter pw = new PrintWriter(w);
e.printStackTrace(pw);
return w.toString();
}
/**
* Returns the greatest integer less than ore equal to the arithmetic mean
* of the entries in cache,
acquiring and holding the argument's
* monitor while making a local copy.
* @param cache list containing entries to analyze
* @return truncated arithmetic mean
*/
private long getMeanFromStatsCache(LinkedList cache) {
List times = new ArrayList(MEAN_TIMING_STATS_CACHE_SIZE);
synchronized (cache) {
times.addAll(cache);
}
double result = 0;
int counter = 0;
Iterator iter = times.iterator();
while (iter.hasNext()) {
Long time = iter.next();
if (time != null) {
counter++;
result = result * ((counter - 1) / (double) counter) +
time.longValue()/(double) counter;
}
}
return (long) result;
}
/**
* Initializes pool statistics.
*/
private void initStats() {
for (int i = 0; i < MEAN_TIMING_STATS_CACHE_SIZE; i++) {
activeTimes.add(null);
idleTimes.add(null);
waitTimes.add(null);
}
}
// Inner classes
/**
* The idle object evictor {@link TimerTask}.
*
* @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
*/
class Evictor extends TimerTask {
/**
* 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() {
ClassLoader savedClassLoader =
Thread.currentThread().getContextClassLoader();
try {
// Set the class loader for the factory
Thread.currentThread().setContextClassLoader(
factoryClassLoader);
// Evict from the pool
try {
evict();
} catch(Exception e) {
swallowException(e);
} catch(OutOfMemoryError oome) {
// Log problem but give evictor thread a chance to continue
// in case error is recoverable
oome.printStackTrace(System.err);
}
// Re-create idle instances.
try {
ensureMinIdle();
} catch (Exception e) {
swallowException(e);
}
} finally {
// Restore the previous CCL
Thread.currentThread().setContextClassLoader(savedClassLoader);
}
}
}
}