package edu.brown.pools;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.pool.BaseObjectPool;
import org.apache.commons.pool.PoolUtils;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.log4j.Logger;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* Based on org.apache.commons.pool.impl.StackObjectPool
* @author pavlo
* @param <T>
*/
public class FastObjectPool<T> extends BaseObjectPool {
private static final Logger LOG = Logger.getLogger(FastObjectPool.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
/**
* The cap on the number of "sleeping" instances in the pool.
*/
private static final int DEFAULT_MAX_SLEEPING = 8;
/**
* The default initial size of the pool
* (this specifies the size of the container, it does not
* cause the pool to be pre-populated.)
*/
private static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
/**
* My pool.
*/
private Queue<T> pool = null;
/**
* My {@link PoolableObjectFactory}.
*/
private PoolableObjectFactory factory = null;
/**
* The cap on the number of "sleeping" instances in the pool.
*/
private int maxSleeping = DEFAULT_MAX_SLEEPING;
/**
* Number of objects borrowed but not yet returned to the pool.
*/
private final AtomicInteger numActive = new AtomicInteger(0);
/**
* Number of objects sitting in the pool
*/
private final AtomicInteger numInactive = new AtomicInteger(0);
// ----------------------------------------------------------------------------
// CONSTRUCTORS
// ----------------------------------------------------------------------------
public FastObjectPool(PoolableObjectFactory factory) {
this(factory, DEFAULT_MAX_SLEEPING, DEFAULT_INIT_SLEEPING_CAPACITY);
}
public FastObjectPool(PoolableObjectFactory factory, int idle) {
this(factory, idle, DEFAULT_INIT_SLEEPING_CAPACITY);
}
public FastObjectPool(PoolableObjectFactory factory, int maxIdle, int initIdleCapacity) {
this.factory = factory;
maxSleeping = (maxIdle < 0 ? DEFAULT_MAX_SLEEPING : maxIdle);
pool = new ConcurrentLinkedQueue<T>();
}
@SuppressWarnings("unchecked")
@Override
public T borrowObject() throws Exception {
assertOpen();
boolean newlyCreated = false;
T obj = this.pool.poll();
if (obj == null) {
if (null == this.factory) {
throw new NoSuchElementException();
} else {
obj = (T)this.factory.makeObject();
if (obj == null) {
throw new NoSuchElementException("PoolableObjectFactory.makeObject() returned null.");
}
newlyCreated = true;
}
}
else {
this.numInactive.decrementAndGet();
}
assert(obj != null);
try {
this.factory.activateObject(obj);
if (!this.factory.validateObject(obj)) {
throw new Exception("ValidateObject failed");
}
} catch (Throwable ex) {
PoolUtils.checkRethrow(ex);
try {
this.factory.destroyObject(obj);
} catch (Throwable t2) {
PoolUtils.checkRethrow(t2);
// swallowed
} finally {
obj = null;
}
if (newlyCreated) {
throw new NoSuchElementException(
"Could not create a validated object, cause: " +
ex.getMessage());
}
}
this.numActive.incrementAndGet();
if (debug.val)
LOG.debug(String.format("Retrieved %s from ObjectPool [hashCode=%d]",
obj.getClass().getSimpleName(), obj.hashCode()));
return obj;
}
@Override
public void returnObject(Object obj) throws Exception {
@SuppressWarnings("unchecked")
T t = (T)obj;
if (isClosed() || this.factory == null) return;
boolean success = true;
try {
this.factory.passivateObject(obj);
} catch(Exception e) {
success = false;
}
boolean shouldDestroy = !success;
this.numActive.decrementAndGet();
int poolSize = this.numInactive.incrementAndGet();
if (success) {
Object toBeDestroyed = null;
if (poolSize >= maxSleeping) {
shouldDestroy = true;
toBeDestroyed = this.pool.poll(); // remove the stalest object
}
if (debug.val)
LOG.debug(String.format("Returning %s back to ObjectPool [hashCode=%d]",
t.getClass().getSimpleName(), t.hashCode()));
this.pool.offer(t);
// swap returned obj with the stalest one so it can be destroyed
if (toBeDestroyed != null) obj = toBeDestroyed;
}
if (shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null
try {
this.factory.destroyObject(obj);
} catch(Exception e) {
// ignored
}
}
}
@Override
public void invalidateObject(Object obj) throws Exception {
this.numActive.decrementAndGet();
if (null != factory) {
this.factory.destroyObject(obj);
}
}
/**
* Return the number of instances
* currently idle in this pool.
*
* @return the number of instances currently idle in this pool
*/
public int getNumIdle() {
return this.numInactive.get();
}
/**
* Return the number of instances currently borrowed from this pool.
*
* @return the number of instances currently borrowed from this pool
*/
public int getNumActive() {
return this.numActive.get();
}
/**
* Clears any objects sitting idle in the pool. Silently swallows any
* exceptions thrown by {@link PoolableObjectFactory#destroyObject(Object)}.
*/
public void clear() {
if (null != factory) {
T t = null;
while ((t = this.pool.poll()) != null) {
try {
this.factory.destroyObject(t);
} catch(Exception e) {
// ignore error, keep destroying the rest
}
} // WHILE
this.numInactive.lazySet(0);
}
}
/**
* Returns the {@link PoolableObjectFactory} used by this pool to create and manage object instances.
*
* @return the factory
* @since 1.5.5
*/
public PoolableObjectFactory getFactory() {
return this.factory;
}
}