blob: 4fb0abab76b6ca8c79fc65b083293c3a6a7f11e0 [file] [log] [blame]
/*
* 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;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
/**
* This class consists exclusively of static methods that operate on or return
* ObjectPool or KeyedObjectPool related interfaces.
*
* @since 2.0
*/
public final class PoolUtils {
/**
* Timer used to periodically check pools idle object count. Because a
* {@link Timer} creates a {@link Thread}, an IODH is used.
*/
static class TimerHolder {
static final Timer MIN_IDLE_TIMER = new Timer(true);
}
/**
* PoolUtils instances should NOT be constructed in standard programming.
* Instead, the class should be used procedurally: PoolUtils.adapt(aPool);.
* This constructor is public to permit tools that require a JavaBean
* instance to operate.
*/
public PoolUtils() {
}
/**
* Should the supplied Throwable be re-thrown (eg if it is an instance of
* one of the Throwables that should never be swallowed). Used by the pool
* error handling for operations that throw exceptions that normally need to
* be ignored.
*
* @param t
* The Throwable to check
* @throws ThreadDeath
* if that is passed in
* @throws VirtualMachineError
* if that is passed in
*/
public static void checkRethrow(final Throwable t) {
if (t instanceof ThreadDeath) {
throw (ThreadDeath) t;
}
if (t instanceof VirtualMachineError) {
throw (VirtualMachineError) t;
}
// All other instances of Throwable will be silently swallowed
}
/**
* Periodically check the idle object count for the pool. At most one idle
* object will be added per period. If there is an exception when calling
* {@link ObjectPool#addObject()} then no more checks will be performed.
*
* @param pool
* the pool to check periodically.
* @param minIdle
* if the {@link ObjectPool#getNumIdle()} is less than this then
* add an idle object.
* @param period
* the frequency to check the number of idle objects in a pool,
* see {@link Timer#schedule(TimerTask, long, long)}.
* @param <T> the type of objects in the pool
* @return the {@link TimerTask} that will periodically check the pools idle
* object count.
* @throws IllegalArgumentException
* when <code>pool</code> is <code>null</code> or when
* <code>minIdle</code> is negative or when <code>period</code>
* isn't valid for {@link Timer#schedule(TimerTask, long, long)}
*/
public static <T> TimerTask checkMinIdle(final ObjectPool<T> pool,
final int minIdle, final long period)
throws IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException("keyedPool must not be null.");
}
if (minIdle < 0) {
throw new IllegalArgumentException("minIdle must be non-negative.");
}
final TimerTask task = new ObjectPoolMinIdleTimerTask<>(pool, minIdle);
getMinIdleTimer().schedule(task, 0L, period);
return task;
}
/**
* Periodically check the idle object count for the key in the keyedPool. At
* most one idle object will be added per period. If there is an exception
* when calling {@link KeyedObjectPool#addObject(Object)} then no more
* checks for that key will be performed.
*
* @param keyedPool
* the keyedPool to check periodically.
* @param key
* the key to check the idle count of.
* @param minIdle
* if the {@link KeyedObjectPool#getNumIdle(Object)} is less than
* this then add an idle object.
* @param period
* the frequency to check the number of idle objects in a
* keyedPool, see {@link Timer#schedule(TimerTask, long, long)}.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return the {@link TimerTask} that will periodically check the pools idle
* object count.
* @throws IllegalArgumentException
* when <code>keyedPool</code>, <code>key</code> is
* <code>null</code> or when <code>minIdle</code> is negative or
* when <code>period</code> isn't valid for
* {@link Timer#schedule(TimerTask, long, long)}.
*/
public static <K, V> TimerTask checkMinIdle(
final KeyedObjectPool<K, V> keyedPool, final K key,
final int minIdle, final long period)
throws IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException("keyedPool must not be null.");
}
if (key == null) {
throw new IllegalArgumentException("key must not be null.");
}
if (minIdle < 0) {
throw new IllegalArgumentException("minIdle must be non-negative.");
}
final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<>(
keyedPool, key, minIdle);
getMinIdleTimer().schedule(task, 0L, period);
return task;
}
/**
* Periodically check the idle object count for each key in the
* <code>Collection</code> <code>keys</code> in the keyedPool. At most one
* idle object will be added per period.
*
* @param keyedPool
* the keyedPool to check periodically.
* @param keys
* a collection of keys to check the idle object count.
* @param minIdle
* if the {@link KeyedObjectPool#getNumIdle(Object)} is less than
* this then add an idle object.
* @param period
* the frequency to check the number of idle objects in a
* keyedPool, see {@link Timer#schedule(TimerTask, long, long)}.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a {@link Map} of key and {@link TimerTask} pairs that will
* periodically check the pools idle object count.
* @throws IllegalArgumentException
* when <code>keyedPool</code>, <code>keys</code>, or any of the
* values in the collection is <code>null</code> or when
* <code>minIdle</code> is negative or when <code>period</code>
* isn't valid for {@link Timer#schedule(TimerTask, long, long)}
* .
* @see #checkMinIdle(KeyedObjectPool, Object, int, long)
*/
public static <K, V> Map<K, TimerTask> checkMinIdle(
final KeyedObjectPool<K, V> keyedPool, final Collection<K> keys,
final int minIdle, final long period)
throws IllegalArgumentException {
if (keys == null) {
throw new IllegalArgumentException("keys must not be null.");
}
final Map<K, TimerTask> tasks = new HashMap<>(keys.size());
final Iterator<K> iter = keys.iterator();
while (iter.hasNext()) {
final K key = iter.next();
final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
tasks.put(key, task);
}
return tasks;
}
/**
* Calls {@link ObjectPool#addObject()} on <code>pool</code> <code>count</code>
* number of times.
*
* @param pool
* the pool to prefill.
* @param count
* the number of idle objects to add.
* @param <T> the type of objects in the pool
* @throws Exception
* when {@link ObjectPool#addObject()} fails.
* @throws IllegalArgumentException
* when <code>pool</code> is <code>null</code>.
*/
public static <T> void prefill(final ObjectPool<T> pool, final int count)
throws Exception, IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException("pool must not be null.");
}
for (int i = 0; i < count; i++) {
pool.addObject();
}
}
/**
* Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with
* <code>key</code> <code>count</code> number of times.
*
* @param keyedPool
* the keyedPool to prefill.
* @param key
* the key to add objects for.
* @param count
* the number of idle objects to add for <code>key</code>.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @throws Exception
* when {@link KeyedObjectPool#addObject(Object)} fails.
* @throws IllegalArgumentException
* when <code>keyedPool</code> or <code>key</code> is
* <code>null</code>.
*/
public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool,
final K key, final int count) throws Exception,
IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException("keyedPool must not be null.");
}
if (key == null) {
throw new IllegalArgumentException("key must not be null.");
}
for (int i = 0; i < count; i++) {
keyedPool.addObject(key);
}
}
/**
* Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with each
* key in <code>keys</code> for <code>count</code> number of times. This has
* the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)}
* for each key in the <code>keys</code> collection.
*
* @param keyedPool
* the keyedPool to prefill.
* @param keys
* {@link Collection} of keys to add objects for.
* @param count
* the number of idle objects to add for each <code>key</code>.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @throws Exception
* when {@link KeyedObjectPool#addObject(Object)} fails.
* @throws IllegalArgumentException
* when <code>keyedPool</code>, <code>keys</code>, or any value
* in <code>keys</code> is <code>null</code>.
* @see #prefill(KeyedObjectPool, Object, int)
*/
public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool,
final Collection<K> keys, final int count) throws Exception,
IllegalArgumentException {
if (keys == null) {
throw new IllegalArgumentException("keys must not be null.");
}
final Iterator<K> iter = keys.iterator();
while (iter.hasNext()) {
prefill(keyedPool, iter.next(), count);
}
}
/**
* Returns a synchronized (thread-safe) ObjectPool backed by the specified
* ObjectPool.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library. Wrapping a pool that {@link #wait() waits} for poolable
* objects to be returned before allowing another one to be borrowed with
* another layer of synchronization will cause liveliness issues or a
* deadlock.
* </p>
*
* @param pool
* the ObjectPool to be "wrapped" in a synchronized ObjectPool.
* @param <T> the type of objects in the pool
* @return a synchronized view of the specified ObjectPool.
*/
public static <T> ObjectPool<T> synchronizedPool(final ObjectPool<T> pool) {
if (pool == null) {
throw new IllegalArgumentException("pool must not be null.");
}
/*
* assert !(pool instanceof GenericObjectPool) :
* "GenericObjectPool is already thread-safe"; assert !(pool instanceof
* SoftReferenceObjectPool) :
* "SoftReferenceObjectPool is already thread-safe"; assert !(pool
* instanceof StackObjectPool) :
* "StackObjectPool is already thread-safe"; assert
* !"org.apache.commons.pool.composite.CompositeObjectPool"
* .equals(pool.getClass().getName()) :
* "CompositeObjectPools are already thread-safe";
*/
return new SynchronizedObjectPool<>(pool);
}
/**
* Returns a synchronized (thread-safe) KeyedObjectPool backed by the
* specified KeyedObjectPool.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library. Wrapping a pool that {@link #wait() waits} for poolable
* objects to be returned before allowing another one to be borrowed with
* another layer of synchronization will cause liveliness issues or a
* deadlock.
* </p>
*
* @param keyedPool
* the KeyedObjectPool to be "wrapped" in a synchronized
* KeyedObjectPool.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a synchronized view of the specified KeyedObjectPool.
*/
public static <K, V> KeyedObjectPool<K, V> synchronizedPool(
final KeyedObjectPool<K, V> keyedPool) {
/*
* assert !(keyedPool instanceof GenericKeyedObjectPool) :
* "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool
* instanceof StackKeyedObjectPool) :
* "StackKeyedObjectPool is already thread-safe"; assert
* !"org.apache.commons.pool.composite.CompositeKeyedObjectPool"
* .equals(keyedPool.getClass().getName()) :
* "CompositeKeyedObjectPools are already thread-safe";
*/
return new SynchronizedKeyedObjectPool<>(keyedPool);
}
/**
* Returns a synchronized (thread-safe) PooledObjectFactory backed by the
* specified PooledObjectFactory.
*
* @param factory
* the PooledObjectFactory to be "wrapped" in a synchronized
* PooledObjectFactory.
* @param <T> the type of objects in the pool
* @return a synchronized view of the specified PooledObjectFactory.
*/
public static <T> PooledObjectFactory<T> synchronizedPooledFactory(
final PooledObjectFactory<T> factory) {
return new SynchronizedPooledObjectFactory<>(factory);
}
/**
* Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by
* the specified KeyedPoolableObjectFactory.
*
* @param keyedFactory
* the KeyedPooledObjectFactory to be "wrapped" in a
* synchronized KeyedPooledObjectFactory.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a synchronized view of the specified KeyedPooledObjectFactory.
*/
public static <K, V> KeyedPooledObjectFactory<K, V> synchronizedKeyedPooledFactory(
final KeyedPooledObjectFactory<K, V> keyedFactory) {
return new SynchronizedKeyedPooledObjectFactory<>(keyedFactory);
}
/**
* Returns a pool that adaptively decreases its size when idle objects are
* no longer needed. This is intended as an always thread-safe alternative
* to using an idle object evictor provided by many pool implementations.
* This is also an effective way to shrink FIFO ordered pools that
* experience load spikes.
*
* @param pool
* the ObjectPool to be decorated so it shrinks its idle count
* when possible.
* @param <T> the type of objects in the pool
* @return a pool that adaptively decreases its size when idle objects are
* no longer needed.
* @see #erodingPool(ObjectPool, float)
*/
public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool) {
return erodingPool(pool, 1f);
}
/**
* Returns a pool that adaptively decreases its size when idle objects are
* no longer needed. This is intended as an always thread-safe alternative
* to using an idle object evictor provided by many pool implementations.
* This is also an effective way to shrink FIFO ordered pools that
* experience load spikes.
* <p>
* The factor parameter provides a mechanism to tweak the rate at which the
* pool tries to shrink its size. Values between 0 and 1 cause the pool to
* try to shrink its size more often. Values greater than 1 cause the pool
* to less frequently try to shrink its size.
* </p>
*
* @param pool
* the ObjectPool to be decorated so it shrinks its idle count
* when possible.
* @param factor
* a positive value to scale the rate at which the pool tries to
* reduce its size. If 0 &lt; factor &lt; 1 then the pool
* shrinks more aggressively. If 1 &lt; factor then the pool
* shrinks less aggressively.
* @param <T> the type of objects in the pool
* @return a pool that adaptively decreases its size when idle objects are
* no longer needed.
* @see #erodingPool(ObjectPool)
*/
public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool,
final float factor) {
if (pool == null) {
throw new IllegalArgumentException("pool must not be null.");
}
if (factor <= 0f) {
throw new IllegalArgumentException("factor must be positive.");
}
return new ErodingObjectPool<>(pool, factor);
}
/**
* Returns a pool that adaptively decreases its size when idle objects are
* no longer needed. This is intended as an always thread-safe alternative
* to using an idle object evictor provided by many pool implementations.
* This is also an effective way to shrink FIFO ordered pools that
* experience load spikes.
*
* @param keyedPool
* the KeyedObjectPool to be decorated so it shrinks its idle
* count when possible.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a pool that adaptively decreases its size when idle objects are
* no longer needed.
* @see #erodingPool(KeyedObjectPool, float)
* @see #erodingPool(KeyedObjectPool, float, boolean)
*/
public static <K, V> KeyedObjectPool<K, V> erodingPool(
final KeyedObjectPool<K, V> keyedPool) {
return erodingPool(keyedPool, 1f);
}
/**
* Returns a pool that adaptively decreases its size when idle objects are
* no longer needed. This is intended as an always thread-safe alternative
* to using an idle object evictor provided by many pool implementations.
* This is also an effective way to shrink FIFO ordered pools that
* experience load spikes.
* <p>
* The factor parameter provides a mechanism to tweak the rate at which the
* pool tries to shrink its size. Values between 0 and 1 cause the pool to
* try to shrink its size more often. Values greater than 1 cause the pool
* to less frequently try to shrink its size.
* </p>
*
* @param keyedPool
* the KeyedObjectPool to be decorated so it shrinks its idle
* count when possible.
* @param factor
* a positive value to scale the rate at which the pool tries to
* reduce its size. If 0 &lt; factor &lt; 1 then the pool
* shrinks more aggressively. If 1 &lt; factor then the pool
* shrinks less aggressively.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a pool that adaptively decreases its size when idle objects are
* no longer needed.
* @see #erodingPool(KeyedObjectPool, float, boolean)
*/
public static <K, V> KeyedObjectPool<K, V> erodingPool(
final KeyedObjectPool<K, V> keyedPool, final float factor) {
return erodingPool(keyedPool, factor, false);
}
/**
* Returns a pool that adaptively decreases its size when idle objects are
* no longer needed. This is intended as an always thread-safe alternative
* to using an idle object evictor provided by many pool implementations.
* This is also an effective way to shrink FIFO ordered pools that
* experience load spikes.
* <p>
* The factor parameter provides a mechanism to tweak the rate at which the
* pool tries to shrink its size. Values between 0 and 1 cause the pool to
* try to shrink its size more often. Values greater than 1 cause the pool
* to less frequently try to shrink its size.
* </p>
* <p>
* The perKey parameter determines if the pool shrinks on a whole pool basis
* or a per key basis. When perKey is false, the keys do not have an effect
* on the rate at which the pool tries to shrink its size. When perKey is
* true, each key is shrunk independently.
* </p>
*
* @param keyedPool
* the KeyedObjectPool to be decorated so it shrinks its idle
* count when possible.
* @param factor
* a positive value to scale the rate at which the pool tries to
* reduce its size. If 0 &lt; factor &lt; 1 then the pool
* shrinks more aggressively. If 1 &lt; factor then the pool
* shrinks less aggressively.
* @param perKey
* when true, each key is treated independently.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a pool that adaptively decreases its size when idle objects are
* no longer needed.
* @see #erodingPool(KeyedObjectPool)
* @see #erodingPool(KeyedObjectPool, float)
*/
public static <K, V> KeyedObjectPool<K, V> erodingPool(
final KeyedObjectPool<K, V> keyedPool, final float factor,
final boolean perKey) {
if (keyedPool == null) {
throw new IllegalArgumentException("keyedPool must not be null.");
}
if (factor <= 0f) {
throw new IllegalArgumentException("factor must be positive.");
}
if (perKey) {
return new ErodingPerKeyKeyedObjectPool<>(keyedPool, factor);
}
return new ErodingKeyedObjectPool<>(keyedPool, factor);
}
/**
* Gets the <code>Timer</code> for checking keyedPool's idle count.
*
* @return the {@link Timer} for checking keyedPool's idle count.
*/
private static Timer getMinIdleTimer() {
return TimerHolder.MIN_IDLE_TIMER;
}
/**
* Timer task that adds objects to the pool until the number of idle
* instances reaches the configured minIdle. Note that this is not the same
* as the pool's minIdle setting.
*
* @param <T> type of objects in the pool
*/
private static final class ObjectPoolMinIdleTimerTask<T> extends TimerTask {
/** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
private final int minIdle;
/** Object pool */
private final ObjectPool<T> pool;
/**
* Create a new ObjectPoolMinIdleTimerTask for the given pool with the
* given minIdle setting.
*
* @param pool
* object pool
* @param minIdle
* number of idle instances to maintain
* @throws IllegalArgumentException
* if the pool is null
*/
ObjectPoolMinIdleTimerTask(final ObjectPool<T> pool, final int minIdle)
throws IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException("pool must not be null.");
}
this.pool = pool;
this.minIdle = minIdle;
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
boolean success = false;
try {
if (pool.getNumIdle() < minIdle) {
pool.addObject();
}
success = true;
} catch (final Exception e) {
cancel();
} finally {
// detect other types of Throwable and cancel this Timer
if (!success) {
cancel();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("ObjectPoolMinIdleTimerTask");
sb.append("{minIdle=").append(minIdle);
sb.append(", pool=").append(pool);
sb.append('}');
return sb.toString();
}
}
/**
* Timer task that adds objects to the pool until the number of idle
* instances for the given key reaches the configured minIdle. Note that
* this is not the same as the pool's minIdle setting.
*
* @param <K> object pool key type
* @param <V> object pool value type
*/
private static final class KeyedObjectPoolMinIdleTimerTask<K, V> extends
TimerTask {
/** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
private final int minIdle;
/** Key to ensure minIdle for */
private final K key;
/** Keyed object pool */
private final KeyedObjectPool<K, V> keyedPool;
/**
* Creates a new KeyedObjecPoolMinIdleTimerTask.
*
* @param keyedPool
* keyed object pool
* @param key
* key to ensure minimum number of idle instances
* @param minIdle
* minimum number of idle instances
* @throws IllegalArgumentException
* if the key is null
*/
KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool<K, V> keyedPool,
final K key, final int minIdle) throws IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException(
"keyedPool must not be null.");
}
this.keyedPool = keyedPool;
this.key = key;
this.minIdle = minIdle;
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
boolean success = false;
try {
if (keyedPool.getNumIdle(key) < minIdle) {
keyedPool.addObject(key);
}
success = true;
} catch (final Exception e) {
cancel();
} finally {
// detect other types of Throwable and cancel this Timer
if (!success) {
cancel();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("KeyedObjectPoolMinIdleTimerTask");
sb.append("{minIdle=").append(minIdle);
sb.append(", key=").append(key);
sb.append(", keyedPool=").append(keyedPool);
sb.append('}');
return sb.toString();
}
}
/**
* A synchronized (thread-safe) ObjectPool backed by the specified
* ObjectPool.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library. Wrapping a pool that {@link #wait() waits} for poolable
* objects to be returned before allowing another one to be borrowed with
* another layer of synchronization will cause liveliness issues or a
* deadlock.
* </p>
*
* @param <T> type of objects in the pool
*/
private static final class SynchronizedObjectPool<T> implements ObjectPool<T> {
/**
* Object whose monitor is used to synchronize methods on the wrapped
* pool.
*/
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/** the underlying object pool */
private final ObjectPool<T> pool;
/**
* Creates a new SynchronizedObjectPool wrapping the given pool.
*
* @param pool
* the ObjectPool to be "wrapped" in a synchronized
* ObjectPool.
* @throws IllegalArgumentException
* if the pool is null
*/
SynchronizedObjectPool(final ObjectPool<T> pool)
throws IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException("pool must not be null.");
}
this.pool = pool;
}
/**
* {@inheritDoc}
*/
@Override
public T borrowObject() throws Exception, NoSuchElementException,
IllegalStateException {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
return pool.borrowObject();
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void returnObject(final T obj) {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
pool.returnObject(obj);
} catch (final Exception e) {
// swallowed as of Pool 2
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void invalidateObject(final T obj) {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
pool.invalidateObject(obj);
} catch (final Exception e) {
// swallowed as of Pool 2
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void addObject() throws Exception, IllegalStateException,
UnsupportedOperationException {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
pool.addObject();
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public int getNumIdle() {
final ReadLock readLock = readWriteLock.readLock();
readLock.lock();
try {
return pool.getNumIdle();
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public int getNumActive() {
final ReadLock readLock = readWriteLock.readLock();
readLock.lock();
try {
return pool.getNumActive();
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void clear() throws Exception, UnsupportedOperationException {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
pool.clear();
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
pool.close();
} catch (final Exception e) {
// swallowed as of Pool 2
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("SynchronizedObjectPool");
sb.append("{pool=").append(pool);
sb.append('}');
return sb.toString();
}
}
/**
* A synchronized (thread-safe) KeyedObjectPool backed by the specified
* KeyedObjectPool.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library. Wrapping a pool that {@link #wait() waits} for poolable
* objects to be returned before allowing another one to be borrowed with
* another layer of synchronization will cause liveliness issues or a
* deadlock.
* </p>
*
* @param <K> object pool key type
* @param <V> object pool value type
*/
private static final class SynchronizedKeyedObjectPool<K, V> implements
KeyedObjectPool<K, V> {
/**
* Object whose monitor is used to synchronize methods on the wrapped
* pool.
*/
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/** Underlying object pool */
private final KeyedObjectPool<K, V> keyedPool;
/**
* Creates a new SynchronizedKeyedObjectPool wrapping the given pool
*
* @param keyedPool
* KeyedObjectPool to wrap
* @throws IllegalArgumentException
* if keyedPool is null
*/
SynchronizedKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool)
throws IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException(
"keyedPool must not be null.");
}
this.keyedPool = keyedPool;
}
/**
* {@inheritDoc}
*/
@Override
public V borrowObject(final K key) throws Exception,
NoSuchElementException, IllegalStateException {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
return keyedPool.borrowObject(key);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void returnObject(final K key, final V obj) {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
keyedPool.returnObject(key, obj);
} catch (final Exception e) {
// swallowed
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void invalidateObject(final K key, final V obj) {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
keyedPool.invalidateObject(key, obj);
} catch (final Exception e) {
// swallowed as of Pool 2
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void addObject(final K key) throws Exception,
IllegalStateException, UnsupportedOperationException {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
keyedPool.addObject(key);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public int getNumIdle(final K key) {
final ReadLock readLock = readWriteLock.readLock();
readLock.lock();
try {
return keyedPool.getNumIdle(key);
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public int getNumActive(final K key) {
final ReadLock readLock = readWriteLock.readLock();
readLock.lock();
try {
return keyedPool.getNumActive(key);
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public int getNumIdle() {
final ReadLock readLock = readWriteLock.readLock();
readLock.lock();
try {
return keyedPool.getNumIdle();
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public int getNumActive() {
final ReadLock readLock = readWriteLock.readLock();
readLock.lock();
try {
return keyedPool.getNumActive();
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void clear() throws Exception, UnsupportedOperationException {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
keyedPool.clear();
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void clear(final K key) throws Exception,
UnsupportedOperationException {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
keyedPool.clear(key);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
final WriteLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
keyedPool.close();
} catch (final Exception e) {
// swallowed as of Pool 2
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("SynchronizedKeyedObjectPool");
sb.append("{keyedPool=").append(keyedPool);
sb.append('}');
return sb.toString();
}
}
/**
* A fully synchronized PooledObjectFactory that wraps a
* PooledObjectFactory and synchronizes access to the wrapped factory
* methods.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library.
* </p>
*
* @param <T> pooled object factory type
*/
private static final class SynchronizedPooledObjectFactory<T> implements
PooledObjectFactory<T> {
/** Synchronization lock */
private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
/** Wrapped factory */
private final PooledObjectFactory<T> factory;
/**
* Creates a SynchronizedPoolableObjectFactory wrapping the given
* factory.
*
* @param factory
* underlying factory to wrap
* @throws IllegalArgumentException
* if the factory is null
*/
SynchronizedPooledObjectFactory(final PooledObjectFactory<T> factory)
throws IllegalArgumentException {
if (factory == null) {
throw new IllegalArgumentException("factory must not be null.");
}
this.factory = factory;
}
/**
* {@inheritDoc}
*/
@Override
public PooledObject<T> makeObject() throws Exception {
writeLock.lock();
try {
return factory.makeObject();
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void destroyObject(final PooledObject<T> p) throws Exception {
writeLock.lock();
try {
factory.destroyObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean validateObject(final PooledObject<T> p) {
writeLock.lock();
try {
return factory.validateObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void activateObject(final PooledObject<T> p) throws Exception {
writeLock.lock();
try {
factory.activateObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void passivateObject(final PooledObject<T> p) throws Exception {
writeLock.lock();
try {
factory.passivateObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("SynchronizedPoolableObjectFactory");
sb.append("{factory=").append(factory);
sb.append('}');
return sb.toString();
}
}
/**
* A fully synchronized KeyedPooledObjectFactory that wraps a
* KeyedPooledObjectFactory and synchronizes access to the wrapped factory
* methods.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library.
* </p>
*
* @param <K> pooled object factory key type
* @param <V> pooled object factory key value
*/
private static final class SynchronizedKeyedPooledObjectFactory<K, V>
implements KeyedPooledObjectFactory<K, V> {
/** Synchronization lock */
private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
/** Wrapped factory */
private final KeyedPooledObjectFactory<K, V> keyedFactory;
/**
* Creates a SynchronizedKeyedPoolableObjectFactory wrapping the given
* factory.
*
* @param keyedFactory
* underlying factory to wrap
* @throws IllegalArgumentException
* if the factory is null
*/
SynchronizedKeyedPooledObjectFactory(
final KeyedPooledObjectFactory<K, V> keyedFactory)
throws IllegalArgumentException {
if (keyedFactory == null) {
throw new IllegalArgumentException(
"keyedFactory must not be null.");
}
this.keyedFactory = keyedFactory;
}
/**
* {@inheritDoc}
*/
@Override
public PooledObject<V> makeObject(final K key) throws Exception {
writeLock.lock();
try {
return keyedFactory.makeObject(key);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void destroyObject(final K key, final PooledObject<V> p) throws Exception {
writeLock.lock();
try {
keyedFactory.destroyObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean validateObject(final K key, final PooledObject<V> p) {
writeLock.lock();
try {
return keyedFactory.validateObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void activateObject(final K key, final PooledObject<V> p) throws Exception {
writeLock.lock();
try {
keyedFactory.activateObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void passivateObject(final K key, final PooledObject<V> p) throws Exception {
writeLock.lock();
try {
keyedFactory.passivateObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("SynchronizedKeyedPoolableObjectFactory");
sb.append("{keyedFactory=").append(keyedFactory);
sb.append('}');
return sb.toString();
}
}
/**
* Encapsulate the logic for when the next poolable object should be
* discarded. Each time update is called, the next time to shrink is
* recomputed, based on the float factor, number of idle instances in the
* pool and high water mark. Float factor is assumed to be between 0 and 1.
* Values closer to 1 cause less frequent erosion events. Erosion event
* timing also depends on numIdle. When this value is relatively high (close
* to previously established high water mark), erosion occurs more
* frequently.
*/
private static final class ErodingFactor {
/** Determines frequency of "erosion" events */
private final float factor;
/** Time of next shrink event */
private transient volatile long nextShrink;
/** High water mark - largest numIdle encountered */
private transient volatile int idleHighWaterMark;
/**
* Creates a new ErodingFactor with the given erosion factor.
*
* @param factor
* erosion factor
*/
public ErodingFactor(final float factor) {
this.factor = factor;
nextShrink = System.currentTimeMillis() + (long) (900000 * factor); // now
// +
// 15
// min
// *
// factor
idleHighWaterMark = 1;
}
/**
* Updates internal state using the supplied time and numIdle.
*
* @param now
* current time
* @param numIdle
* number of idle elements in the pool
*/
public void update(final long now, final int numIdle) {
final int idle = Math.max(0, numIdle);
idleHighWaterMark = Math.max(idle, idleHighWaterMark);
final float maxInterval = 15f;
final float minutes = maxInterval +
((1f - maxInterval) / idleHighWaterMark) * idle;
nextShrink = now + (long) (minutes * 60000f * factor);
}
/**
* Returns the time of the next erosion event.
*
* @return next shrink time
*/
public long getNextShrink() {
return nextShrink;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "ErodingFactor{" + "factor=" + factor +
", idleHighWaterMark=" + idleHighWaterMark + '}';
}
}
/**
* Decorates an object pool, adding "eroding" behavior. Based on the
* configured {@link #factor erosion factor}, objects returning to the pool
* may be invalidated instead of being added to idle capacity.
*
* @param <T> type of objects in the pool
*/
private static class ErodingObjectPool<T> implements ObjectPool<T> {
/** Underlying object pool */
private final ObjectPool<T> pool;
/** Erosion factor */
private final ErodingFactor factor;
/**
* Creates an ErodingObjectPool wrapping the given pool using the
* specified erosion factor.
*
* @param pool
* underlying pool
* @param factor
* erosion factor - determines the frequency of erosion
* events
* @see #factor
*/
public ErodingObjectPool(final ObjectPool<T> pool, final float factor) {
this.pool = pool;
this.factor = new ErodingFactor(factor);
}
/**
* {@inheritDoc}
*/
@Override
public T borrowObject() throws Exception, NoSuchElementException,
IllegalStateException {
return pool.borrowObject();
}
/**
* Returns obj to the pool, unless erosion is triggered, in which case
* obj is invalidated. Erosion is triggered when there are idle
* instances in the pool and more than the {@link #factor erosion
* factor}-determined time has elapsed since the last returnObject
* activation.
*
* @param obj
* object to return or invalidate
* @see #factor
*/
@Override
public void returnObject(final T obj) {
boolean discard = false;
final long now = System.currentTimeMillis();
synchronized (pool) {
if (factor.getNextShrink() < now) { // XXX: Pool 3: move test
// out of sync block
final int numIdle = pool.getNumIdle();
if (numIdle > 0) {
discard = true;
}
factor.update(now, numIdle);
}
}
try {
if (discard) {
pool.invalidateObject(obj);
} else {
pool.returnObject(obj);
}
} catch (final Exception e) {
// swallowed
}
}
/**
* {@inheritDoc}
*/
@Override
public void invalidateObject(final T obj) {
try {
pool.invalidateObject(obj);
} catch (final Exception e) {
// swallowed
}
}
/**
* {@inheritDoc}
*/
@Override
public void addObject() throws Exception, IllegalStateException,
UnsupportedOperationException {
pool.addObject();
}
/**
* {@inheritDoc}
*/
@Override
public int getNumIdle() {
return pool.getNumIdle();
}
/**
* {@inheritDoc}
*/
@Override
public int getNumActive() {
return pool.getNumActive();
}
/**
* {@inheritDoc}
*/
@Override
public void clear() throws Exception, UnsupportedOperationException {
pool.clear();
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
try {
pool.close();
} catch (final Exception e) {
// swallowed
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "ErodingObjectPool{" + "factor=" + factor + ", pool=" +
pool + '}';
}
}
/**
* Decorates a keyed object pool, adding "eroding" behavior. Based on the
* configured erosion factor, objects returning to the pool
* may be invalidated instead of being added to idle capacity.
*
* @param <K> object pool key type
* @param <V> object pool value type
*/
private static class ErodingKeyedObjectPool<K, V> implements
KeyedObjectPool<K, V> {
/** Underlying pool */
private final KeyedObjectPool<K, V> keyedPool;
/** Erosion factor */
private final ErodingFactor erodingFactor;
/**
* Creates an ErodingObjectPool wrapping the given pool using the
* specified erosion factor.
*
* @param keyedPool
* underlying pool
* @param factor
* erosion factor - determines the frequency of erosion
* events
* @see #erodingFactor
*/
public ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
final float factor) {
this(keyedPool, new ErodingFactor(factor));
}
/**
* Creates an ErodingObjectPool wrapping the given pool using the
* specified erosion factor.
*
* @param keyedPool
* underlying pool - must not be null
* @param erodingFactor
* erosion factor - determines the frequency of erosion
* events
* @see #erodingFactor
*/
protected ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
final ErodingFactor erodingFactor) {
if (keyedPool == null) {
throw new IllegalArgumentException(
"keyedPool must not be null.");
}
this.keyedPool = keyedPool;
this.erodingFactor = erodingFactor;
}
/**
* {@inheritDoc}
*/
@Override
public V borrowObject(final K key) throws Exception,
NoSuchElementException, IllegalStateException {
return keyedPool.borrowObject(key);
}
/**
* Returns obj to the pool, unless erosion is triggered, in which case
* obj is invalidated. Erosion is triggered when there are idle
* instances in the pool associated with the given key and more than the
* configured {@link #erodingFactor erosion factor} time has elapsed
* since the last returnObject activation.
*
* @param obj
* object to return or invalidate
* @param key
* key
* @see #erodingFactor
*/
@Override
public void returnObject(final K key, final V obj) throws Exception {
boolean discard = false;
final long now = System.currentTimeMillis();
final ErodingFactor factor = getErodingFactor(key);
synchronized (keyedPool) {
if (factor.getNextShrink() < now) {
final int numIdle = getNumIdle(key);
if (numIdle > 0) {
discard = true;
}
factor.update(now, numIdle);
}
}
try {
if (discard) {
keyedPool.invalidateObject(key, obj);
} else {
keyedPool.returnObject(key, obj);
}
} catch (final Exception e) {
// swallowed
}
}
/**
* Returns the eroding factor for the given key
*
* @param key
* key
* @return eroding factor for the given keyed pool
*/
protected ErodingFactor getErodingFactor(final K key) {
return erodingFactor;
}
/**
* {@inheritDoc}
*/
@Override
public void invalidateObject(final K key, final V obj) {
try {
keyedPool.invalidateObject(key, obj);
} catch (final Exception e) {
// swallowed
}
}
/**
* {@inheritDoc}
*/
@Override
public void addObject(final K key) throws Exception,
IllegalStateException, UnsupportedOperationException {
keyedPool.addObject(key);
}
/**
* {@inheritDoc}
*/
@Override
public int getNumIdle() {
return keyedPool.getNumIdle();
}
/**
* {@inheritDoc}
*/
@Override
public int getNumIdle(final K key) {
return keyedPool.getNumIdle(key);
}
/**
* {@inheritDoc}
*/
@Override
public int getNumActive() {
return keyedPool.getNumActive();
}
/**
* {@inheritDoc}
*/
@Override
public int getNumActive(final K key) {
return keyedPool.getNumActive(key);
}
/**
* {@inheritDoc}
*/
@Override
public void clear() throws Exception, UnsupportedOperationException {
keyedPool.clear();
}
/**
* {@inheritDoc}
*/
@Override
public void clear(final K key) throws Exception,
UnsupportedOperationException {
keyedPool.clear(key);
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
try {
keyedPool.close();
} catch (final Exception e) {
// swallowed
}
}
/**
* Returns the underlying pool
*
* @return the keyed pool that this ErodingKeyedObjectPool wraps
*/
protected KeyedObjectPool<K, V> getKeyedPool() {
return keyedPool;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "ErodingKeyedObjectPool{" + "factor=" +
erodingFactor + ", keyedPool=" + keyedPool + '}';
}
}
/**
* Extends ErodingKeyedObjectPool to allow erosion to take place on a
* per-key basis. Timing of erosion events is tracked separately for
* separate keyed pools.
*
* @param <K> object pool key type
* @param <V> object pool value type
*/
private static final class ErodingPerKeyKeyedObjectPool<K, V> extends
ErodingKeyedObjectPool<K, V> {
/** Erosion factor - same for all pools */
private final float factor;
/** Map of ErodingFactor instances keyed on pool keys */
private final Map<K, ErodingFactor> factors = Collections.synchronizedMap(new HashMap<K, ErodingFactor>());
/**
* Creates a new ErordingPerKeyKeyedObjectPool decorating the given keyed
* pool with the specified erosion factor.
*
* @param keyedPool
* underlying keyed pool
* @param factor
* erosion factor
*/
public ErodingPerKeyKeyedObjectPool(
final KeyedObjectPool<K, V> keyedPool, final float factor) {
super(keyedPool, null);
this.factor = factor;
}
/**
* {@inheritDoc}
*/
@Override
protected ErodingFactor getErodingFactor(final K key) {
ErodingFactor eFactor = factors.get(key);
// this may result in two ErodingFactors being created for a key
// since they are small and cheap this is okay.
if (eFactor == null) {
eFactor = new ErodingFactor(this.factor);
factors.put(key, eFactor);
}
return eFactor;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor +
", keyedPool=" + getKeyedPool() + '}';
}
}
}