package org.apache.commons.pool.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.TimerTask; import org.apache.commons.pool.BaseObjectPool; import org.apache.commons.pool.ObjectPool; import org.apache.commons.pool.PoolUtils; import org.apache.commons.pool.PoolableObjectFactory; public class GenericObjectPool extends BaseObjectPool implements ObjectPool { public static final byte WHEN_EXHAUSTED_FAIL = 0; public static final byte WHEN_EXHAUSTED_BLOCK = 1; public static final byte WHEN_EXHAUSTED_GROW = 2; public static final int DEFAULT_MAX_IDLE = 8; public static final int DEFAULT_MIN_IDLE = 0; public static final int DEFAULT_MAX_ACTIVE = 8; public static final byte DEFAULT_WHEN_EXHAUSTED_ACTION = 1; public static final boolean DEFAULT_LIFO = true; public static final long DEFAULT_MAX_WAIT = -1L; public static final boolean DEFAULT_TEST_ON_BORROW = false; public static final boolean DEFAULT_TEST_ON_RETURN = false; public static final boolean DEFAULT_TEST_WHILE_IDLE = false; public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L; public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3; public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1800000L; public static final long DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1L; private int _maxIdle = 8; private int _minIdle = 0; private int _maxActive = 8; private long _maxWait = -1L; private byte _whenExhaustedAction = 1; private volatile boolean _testOnBorrow = false; private volatile boolean _testOnReturn = false; private boolean _testWhileIdle = false; private long _timeBetweenEvictionRunsMillis = -1L; private int _numTestsPerEvictionRun = 3; private long _minEvictableIdleTimeMillis = 1800000L; private long _softMinEvictableIdleTimeMillis = -1L; private boolean _lifo = true; private CursorableLinkedList _pool = null; private CursorableLinkedList.Cursor _evictionCursor = null; private PoolableObjectFactory _factory = null; private int _numActive = 0; private Evictor _evictor = null; private int _numInternalProcessing = 0; private final LinkedList _allocationQueue = new LinkedList(); public GenericObjectPool() { this(null, 8, (byte)1, -1L, 8, 0, false, false, -1L, 3, 1800000L, false); } public GenericObjectPool(PoolableObjectFactory factory) { this(factory, 8, (byte)1, -1L, 8, 0, false, false, -1L, 3, 1800000L, false); } public GenericObjectPool(PoolableObjectFactory factory, Config config) { this(factory, config.maxActive, config.whenExhaustedAction, config.maxWait, config.maxIdle, config.minIdle, config.testOnBorrow, config.testOnReturn, config.timeBetweenEvictionRunsMillis, config.numTestsPerEvictionRun, config.minEvictableIdleTimeMillis, config.testWhileIdle, config.softMinEvictableIdleTimeMillis, config.lifo); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive) { this(factory, maxActive, (byte)1, -1L, 8, 0, false, false, -1L, 3, 1800000L, false); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait) { this(factory, maxActive, whenExhaustedAction, maxWait, 8, 0, false, false, -1L, 3, 1800000L, false); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) { this(factory, maxActive, whenExhaustedAction, maxWait, 8, 0, testOnBorrow, testOnReturn, -1L, 3, 1800000L, false); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) { this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, 0, false, false, -1L, 3, 1800000L, false); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) { this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, 0, testOnBorrow, testOnReturn, -1L, 3, 1800000L, false); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) { this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, 0, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) { this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, -1L); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis) { this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, softMinEvictableIdleTimeMillis, true); } public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis, boolean lifo) { this._factory = factory; this._maxActive = maxActive; this._lifo = lifo; switch (whenExhaustedAction) { case 0: case 1: case 2: this._whenExhaustedAction = whenExhaustedAction; break; default: throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized."); } this._maxWait = maxWait; this._maxIdle = maxIdle; this._minIdle = minIdle; this._testOnBorrow = testOnBorrow; this._testOnReturn = testOnReturn; this._timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; this._numTestsPerEvictionRun = numTestsPerEvictionRun; this._minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; this._softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; this._testWhileIdle = testWhileIdle; this._pool = new CursorableLinkedList(); startEvictor(this._timeBetweenEvictionRunsMillis); } public synchronized int getMaxActive() { return this._maxActive; } public synchronized void setMaxActive(int maxActive) { this._maxActive = maxActive; allocate(); } public synchronized byte getWhenExhaustedAction() { return this._whenExhaustedAction; } public synchronized void setWhenExhaustedAction(byte whenExhaustedAction) { switch (whenExhaustedAction) { case 0: case 1: case 2: this._whenExhaustedAction = whenExhaustedAction; allocate(); break; default: throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized."); } } public synchronized long getMaxWait() { return this._maxWait; } public synchronized void setMaxWait(long maxWait) { this._maxWait = maxWait; allocate(); } public synchronized int getMaxIdle() { return this._maxIdle; } public synchronized void setMaxIdle(int maxIdle) { this._maxIdle = maxIdle; allocate(); } public synchronized void setMinIdle(int minIdle) { this._minIdle = minIdle; allocate(); } public synchronized int getMinIdle() { return this._minIdle; } public boolean getTestOnBorrow() { return this._testOnBorrow; } public void setTestOnBorrow(boolean testOnBorrow) { this._testOnBorrow = testOnBorrow; } public boolean getTestOnReturn() { return this._testOnReturn; } public void setTestOnReturn(boolean testOnReturn) { this._testOnReturn = testOnReturn; } public synchronized long getTimeBetweenEvictionRunsMillis() { return this._timeBetweenEvictionRunsMillis; } public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { this._timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; startEvictor(this._timeBetweenEvictionRunsMillis); } public synchronized int getNumTestsPerEvictionRun() { return this._numTestsPerEvictionRun; } public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { this._numTestsPerEvictionRun = numTestsPerEvictionRun; } public synchronized long getMinEvictableIdleTimeMillis() { return this._minEvictableIdleTimeMillis; } public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { this._minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; } public synchronized long getSoftMinEvictableIdleTimeMillis() { return this._softMinEvictableIdleTimeMillis; } public synchronized void setSoftMinEvictableIdleTimeMillis(long softMinEvictableIdleTimeMillis) { this._softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; } public synchronized boolean getTestWhileIdle() { return this._testWhileIdle; } public synchronized void setTestWhileIdle(boolean testWhileIdle) { this._testWhileIdle = testWhileIdle; } public synchronized boolean getLifo() { return this._lifo; } public synchronized void setLifo(boolean lifo) { this._lifo = lifo; } public synchronized void setConfig(Config conf) { setMaxIdle(conf.maxIdle); setMinIdle(conf.minIdle); setMaxActive(conf.maxActive); setMaxWait(conf.maxWait); setWhenExhaustedAction(conf.whenExhaustedAction); setTestOnBorrow(conf.testOnBorrow); setTestOnReturn(conf.testOnReturn); setTestWhileIdle(conf.testWhileIdle); setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun); setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis); setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis); setSoftMinEvictableIdleTimeMillis(conf.softMinEvictableIdleTimeMillis); setLifo(conf.lifo); allocate(); } public Object borrowObject() throws Exception { long starttime = System.currentTimeMillis(); Latch latch = new Latch(null); byte whenExhaustedAction; long maxWait; synchronized (this) { whenExhaustedAction = this._whenExhaustedAction; maxWait = this._maxWait; this._allocationQueue.add(latch); allocate(); } while (true) { synchronized (this) { assertOpen(); } if (latch.getPair() == null) { if (!latch.mayCreate()) { switch (whenExhaustedAction) { case 2: synchronized (this) { if ((latch.getPair() == null) && (!latch.mayCreate())) { this._allocationQueue.remove(latch); this._numInternalProcessing += 1; } } break; case 0: synchronized (this) { if ((latch.getPair() == null) && (latch.mayCreate())) { break label497; } this._allocationQueue.remove(latch); } throw new NoSuchElementException("Pool exhausted"); case 1: try { synchronized (latch) { if ((latch.getPair() == null) && (!latch.mayCreate())) { if (maxWait <= 0L) { latch.wait(); } else { long elapsed = System.currentTimeMillis() - starttime; long waitTime = maxWait - elapsed; if (waitTime > 0L) { latch.wait(waitTime); } } } else break label497; } } catch (InterruptedException e) { synchronized (this) { if ((latch.getPair() == null) && (!latch.mayCreate())) { this._allocationQueue.remove(latch); } else break label497; } Thread.currentThread().interrupt(); throw e; } if ((maxWait <= 0L) || (System.currentTimeMillis() - starttime < maxWait)) continue; synchronized (this) { if ((latch.getPair() == null) && (!latch.mayCreate())) { this._allocationQueue.remove(latch); } else break label497; } throw new NoSuchElementException("Timeout waiting for idle object"); default: throw new IllegalArgumentException("WhenExhaustedAction property " + whenExhaustedAction + " not recognized."); } } } else { label497: boolean newlyCreated = false; if (null == latch.getPair()) { try { Object obj = this._factory.makeObject(); latch.setPair(new GenericKeyedObjectPool.ObjectTimestampPair(obj)); newlyCreated = true; } finally { if (!newlyCreated) { synchronized (this) { this._numInternalProcessing -= 1; allocate(); } } } } try { this._factory.activateObject(latch.getPair().value); if ((this._testOnBorrow) && (!this._factory.validateObject(latch.getPair().value))) { throw new Exception("ValidateObject failed"); } synchronized (this) { this._numInternalProcessing -= 1; this._numActive += 1; } return latch.getPair().value; } catch (Throwable e) { PoolUtils.checkRethrow(e); try { this._factory.destroyObject(latch.getPair().value); } catch (Throwable e2) { PoolUtils.checkRethrow(e2); } synchronized (this) { this._numInternalProcessing -= 1; if (!newlyCreated) { latch.reset(); this._allocationQueue.add(0, latch); } allocate(); } if (newlyCreated) throw new NoSuchElementException("Could not create a validated object, cause: " + e.getMessage()); } } } } private synchronized void allocate() { if (isClosed()) return; while ((!this._pool.isEmpty()) && (!this._allocationQueue.isEmpty())) { Latch latch = (Latch)this._allocationQueue.removeFirst(); latch.setPair((GenericKeyedObjectPool.ObjectTimestampPair)this._pool.removeFirst()); this._numInternalProcessing += 1; synchronized (latch) { latch.notify(); } } while ((!this._allocationQueue.isEmpty()) && ((this._maxActive < 0) || (this._numActive + this._numInternalProcessing < this._maxActive))) { Latch latch = (Latch)this._allocationQueue.removeFirst(); latch.setMayCreate(true); this._numInternalProcessing += 1; synchronized (latch) { latch.notify(); } } } public void invalidateObject(Object obj) throws Exception { try { if (this._factory != null) this._factory.destroyObject(obj); } finally { synchronized (this) { this._numActive -= 1; allocate(); } } } public void clear() { List toDestroy = new ArrayList(); synchronized (this) { toDestroy.addAll(this._pool); this._numInternalProcessing += this._pool._size; this._pool.clear(); } destroy(toDestroy, this._factory); } private void destroy(Collection c, PoolableObjectFactory factory) { for (Iterator it = c.iterator(); it.hasNext(); ) try { factory.destroyObject(((GenericKeyedObjectPool.ObjectTimestampPair)it.next()).value); } catch (Exception e) { } finally { synchronized (this) { this._numInternalProcessing -= 1; allocate(); } } } public synchronized int getNumActive() { return this._numActive; } public synchronized int getNumIdle() { return this._pool.size(); } public void returnObject(Object obj) throws Exception { try { addObjectToPool(obj, true); } catch (Exception e) { if (this._factory != null) { try { this._factory.destroyObject(obj); } catch (Exception e2) { } synchronized (this) { this._numActive -= 1; allocate(); } } } } private void addObjectToPool(Object obj, boolean decrementNumActive) throws Exception { boolean success = true; if ((this._testOnReturn) && (!this._factory.validateObject(obj))) success = false; else { this._factory.passivateObject(obj); } boolean shouldDestroy = !success; synchronized (this) { if (isClosed()) { shouldDestroy = true; } else if ((this._maxIdle >= 0) && (this._pool.size() >= this._maxIdle)) { shouldDestroy = true; } else if (success) { if (this._lifo) this._pool.addFirst(new GenericKeyedObjectPool.ObjectTimestampPair(obj)); else { this._pool.addLast(new GenericKeyedObjectPool.ObjectTimestampPair(obj)); } if (decrementNumActive) { this._numActive -= 1; } allocate(); } } if (shouldDestroy) { try { this._factory.destroyObject(obj); } catch (Exception e) { } if (decrementNumActive) synchronized (this) { this._numActive -= 1; allocate(); } } } public void close() throws Exception { super.close(); synchronized (this) { clear(); startEvictor(-1L); } } /** @deprecated */ public void setFactory(PoolableObjectFactory factory) throws IllegalStateException { List toDestroy = new ArrayList(); PoolableObjectFactory oldFactory = this._factory; synchronized (this) { assertOpen(); if (0 < getNumActive()) { throw new IllegalStateException("Objects are already active"); } toDestroy.addAll(this._pool); this._numInternalProcessing += this._pool._size; this._pool.clear(); this._factory = factory; } destroy(toDestroy, oldFactory); } public void evict() throws Exception { assertOpen(); synchronized (this) { if (this._pool.isEmpty()) { return; } if (null == this._evictionCursor) { this._evictionCursor = this._pool.cursor(this._lifo ? this._pool.size() : 0); } } int i = 0; for (int m = getNumTests(); i < m; i++) { GenericKeyedObjectPool.ObjectTimestampPair pair; synchronized (this) { if (((this._lifo) && (!this._evictionCursor.hasPrevious())) || ((!this._lifo) && (!this._evictionCursor.hasNext()))) { this._evictionCursor.close(); this._evictionCursor = this._pool.cursor(this._lifo ? this._pool.size() : 0); } pair = this._lifo ? (GenericKeyedObjectPool.ObjectTimestampPair)this._evictionCursor.previous() : (GenericKeyedObjectPool.ObjectTimestampPair)this._evictionCursor.next(); this._evictionCursor.remove(); this._numInternalProcessing += 1; } boolean removeObject = false; long idleTimeMilis = System.currentTimeMillis() - pair.tstamp; if ((getMinEvictableIdleTimeMillis() > 0L) && (idleTimeMilis > getMinEvictableIdleTimeMillis())) { removeObject = true; } else if ((getSoftMinEvictableIdleTimeMillis() > 0L) && (idleTimeMilis > getSoftMinEvictableIdleTimeMillis()) && (getNumIdle() + 1 > getMinIdle())) { removeObject = true; } if ((getTestWhileIdle()) && (!removeObject)) { boolean active = false; try { this._factory.activateObject(pair.value); active = true; } catch (Exception e) { removeObject = true; } if (active) { if (!this._factory.validateObject(pair.value)) removeObject = true; else { try { this._factory.passivateObject(pair.value); } catch (Exception e) { removeObject = true; } } } } if (removeObject) try { this._factory.destroyObject(pair.value); } catch (Exception e) { } synchronized (this) { if (!removeObject) { this._evictionCursor.add(pair); if (this._lifo) { this._evictionCursor.previous(); } } this._numInternalProcessing -= 1; } } } private void ensureMinIdle() throws Exception { int objectDeficit = calculateDeficit(false); for (int j = 0; (j < objectDeficit) && (calculateDeficit(true) > 0); j++) try { addObject(); } finally { synchronized (this) { this._numInternalProcessing -= 1; allocate(); } } } private synchronized int calculateDeficit(boolean incrementInternal) { int objectDeficit = getMinIdle() - getNumIdle(); if (this._maxActive > 0) { int growLimit = Math.max(0, getMaxActive() - getNumActive() - getNumIdle() - this._numInternalProcessing); objectDeficit = Math.min(objectDeficit, growLimit); } if ((incrementInternal) && (objectDeficit > 0)) { this._numInternalProcessing += 1; } return objectDeficit; } public void addObject() throws Exception { assertOpen(); if (this._factory == null) { throw new IllegalStateException("Cannot add objects without a factory."); } Object obj = this._factory.makeObject(); try { assertOpen(); addObjectToPool(obj, false); } catch (IllegalStateException ex) { try { this._factory.destroyObject(obj); } catch (Exception ex2) { } throw ex; } } protected synchronized void startEvictor(long delay) { if (null != this._evictor) { EvictionTimer.cancel(this._evictor); this._evictor = null; } if (delay > 0L) { this._evictor = new Evictor(null); EvictionTimer.schedule(this._evictor, delay, delay); } } synchronized String debugInfo() { StringBuffer buf = new StringBuffer(); buf.append("Active: ").append(getNumActive()).append("\n"); buf.append("Idle: ").append(getNumIdle()).append("\n"); buf.append("Idle Objects:\n"); Iterator it = this._pool.iterator(); long time = System.currentTimeMillis(); while (it.hasNext()) { GenericKeyedObjectPool.ObjectTimestampPair pair = (GenericKeyedObjectPool.ObjectTimestampPair)it.next(); buf.append("\t").append(pair.value).append("\t").append(time - pair.tstamp).append("\n"); } return buf.toString(); } private int getNumTests() { if (this._numTestsPerEvictionRun >= 0) { return Math.min(this._numTestsPerEvictionRun, this._pool.size()); } return (int)Math.ceil(this._pool.size() / Math.abs(this._numTestsPerEvictionRun)); } private static final class Latch { private GenericKeyedObjectPool.ObjectTimestampPair _pair; private boolean _mayCreate = false; private Latch() { } private synchronized GenericKeyedObjectPool.ObjectTimestampPair getPair() { return this._pair; } private synchronized void setPair(GenericKeyedObjectPool.ObjectTimestampPair pair) { this._pair = pair; } private synchronized boolean mayCreate() { return this._mayCreate; } private synchronized void setMayCreate(boolean mayCreate) { this._mayCreate = mayCreate; } private synchronized void reset() { this._pair = null; this._mayCreate = false; } Latch(GenericObjectPool.1 x0) { this(); } } public static class Config { public int maxIdle = 8; public int minIdle = 0; public int maxActive = 8; public long maxWait = -1L; public byte whenExhaustedAction = 1; public boolean testOnBorrow = false; public boolean testOnReturn = false; public boolean testWhileIdle = false; public long timeBetweenEvictionRunsMillis = -1L; public int numTestsPerEvictionRun = 3; public long minEvictableIdleTimeMillis = 1800000L; public long softMinEvictableIdleTimeMillis = -1L; public boolean lifo = true; } private class Evictor extends TimerTask { private Evictor() { } public void run() { try { GenericObjectPool.this.evict(); } catch (Exception e) { } catch (OutOfMemoryError oome) { oome.printStackTrace(System.err); } try { GenericObjectPool.this.ensureMinIdle(); } catch (Exception e) { } } Evictor(GenericObjectPool.1 x1) { this(); } } }