Author: sandymac
Date: Sun Nov 12 17:01:22 2006
New Revision: 474109
URL: http://svn.apache.org/viewvc?view=rev&rev=474109
Log:
Added ErodingPool decorators designed to shrink the size of a pool without the
use of an evictor.
Modified:
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/PoolUtils.java
jakarta/commons/proper/pool/trunk/src/test/org/apache/commons/pool/TestPoolUtils.java
Modified:
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/PoolUtils.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/PoolUtils.java?view=diff&rev=474109&r1=474108&r2=474109
==============================================================================
---
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/PoolUtils.java
(original)
+++
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/PoolUtils.java
Sun Nov 12 17:01:22 2006
@@ -29,6 +29,7 @@
import java.util.NoSuchElementException;
import java.util.Timer;
import java.util.TimerTask;
+import java.util.Collections;
/**
* This class consists exclusively of static methods that operate on or return
ObjectPool
@@ -408,6 +409,136 @@
}
/**
+ * Returns a pool that adaptively decreases it's 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 it's idle
count when possible.
+ * @return a pool that adaptively decreases it's size when idle objects
are no longer needed.
+ * @see #erodingPool(ObjectPool, float)
+ * @since Pool 2.0
+ */
+ public static ObjectPool erodingPool(final ObjectPool pool) {
+ return erodingPool(pool, 1f);
+ }
+
+ /**
+ * Returns a pool that adaptively decreases it's 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
+ * it's size. Values between 0 and 1 cause the pool to try to shrink it's
size more often.
+ * Values greater than 1 cause the pool to less frequently try to shrink
it's size.
+ * </p>
+ *
+ * @param pool the ObjectPool to be decorated so it shrinks it's idle
count when possible.
+ * @param factor a positive value to scale the rate at which the pool
tries to reduce it's size.
+ * If 0 < factor < 1 then the pool shrinks more aggressively.
+ * If 1 < factor then the pool shrinks less aggressively.
+ * @return a pool that adaptively decreases it's size when idle objects
are no longer needed.
+ * @see #erodingPool(ObjectPool)
+ * @since Pool 2.0
+ */
+ public static ObjectPool erodingPool(final ObjectPool 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 it's 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 it's
idle count when
+ * possible.
+ * @return a pool that adaptively decreases it's size when idle objects
are no longer needed.
+ * @see #erodingPool(KeyedObjectPool, float)
+ * @see #erodingPool(KeyedObjectPool, float, boolean)
+ * @since Pool 2.0
+ */
+ public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool)
{
+ return erodingPool(keyedPool, 1f);
+ }
+
+ /**
+ * Returns a pool that adaptively decreases it's 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
+ * it's size. Values between 0 and 1 cause the pool to try to shrink it's
size more often.
+ * Values greater than 1 cause the pool to less frequently try to shrink
it's size.
+ * </p>
+ *
+ * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's
idle count when
+ * possible.
+ * @param factor a positive value to scale the rate at which the pool
tries to reduce it's size.
+ * If 0 < factor < 1 then the pool shrinks more aggressively.
+ * If 1 < factor then the pool shrinks less aggressively.
+ * @return a pool that adaptively decreases it's size when idle objects
are no longer needed.
+ * @see #erodingPool(KeyedObjectPool, float, boolean)
+ * @since Pool 2.0
+ */
+ public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool,
final float factor) {
+ return erodingPool(keyedPool, factor, false);
+ }
+
+ /**
+ * Returns a pool that adaptively decreases it's 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
+ * it's size. Values between 0 and 1 cause the pool to try to shrink it's
size more often.
+ * Values greater than 1 cause the pool to less frequently try to shrink
it's 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 it's size. When perKey is true, each key is shrunk independently.
+ * </p>
+ *
+ * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's
idle count when
+ * possible.
+ * @param factor a positive value to scale the rate at which the pool
tries to reduce it's size.
+ * If 0 < factor < 1 then the pool shrinks more aggressively.
+ * If 1 < factor then the pool shrinks less aggressively.
+ * @param perKey when true, each key is treated independently.
+ * @return a pool that adaptively decreases it's size when idle objects
are no longer needed.
+ * @see #erodingPool(KeyedObjectPool)
+ * @see #erodingPool(KeyedObjectPool, float)
+ * @since Pool 2.0
+ */
+ public static KeyedObjectPool erodingPool(final KeyedObjectPool 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);
+ } else {
+ return new ErodingKeyedObjectPool(keyedPool, factor);
+ }
+ }
+
+ /**
* Get the <code>Timer</code> for checking keyedPool's idle count. Lazily
create the [EMAIL PROTECTED] Timer} as needed.
*
* @return the [EMAIL PROTECTED] Timer} for checking keyedPool's idle
count.
@@ -555,8 +686,12 @@
keyedPool.clear();
}
- public void close() throws Exception {
- keyedPool.close();
+ public void close() {
+ try {
+ keyedPool.close();
+ } catch (Exception e) {
+ // swallowed as of Pool 2
+ }
}
public void setFactory(final PoolableObjectFactory factory) throws
IllegalStateException, UnsupportedOperationException {
@@ -631,8 +766,12 @@
pool.clear();
}
- public void close() throws Exception {
- pool.close();
+ public void close() {
+ try {
+ pool.close();
+ } catch (Exception e) {
+ // swallowed as of Pool 2
+ }
}
public void setFactory(final KeyedPoolableObjectFactory factory)
throws IllegalStateException, UnsupportedOperationException {
@@ -712,8 +851,12 @@
pool.clear();
}
- public void close() throws Exception {
- pool.close();
+ public void close() {
+ try {
+ pool.close();
+ } catch (Exception e) {
+ // swallowed as of Pool 2
+ }
}
public void setFactory(final PoolableObjectFactory factory) throws
IllegalStateException, UnsupportedOperationException {
@@ -806,8 +949,12 @@
keyedPool.clear(key);
}
- public void close() throws Exception {
- keyedPool.close();
+ public void close() {
+ try {
+ keyedPool.close();
+ } catch (Exception e) {
+ // swallowed as of Pool 2
+ }
}
public void setFactory(final KeyedPoolableObjectFactory factory)
throws IllegalStateException, UnsupportedOperationException {
@@ -971,9 +1118,13 @@
}
}
- public void close() throws Exception {
- synchronized (lock) {
- pool.close();
+ public void close() {
+ try {
+ synchronized (lock) {
+ pool.close();
+ }
+ } catch (Exception e) {
+ // swallowed as of Pool 2
}
}
@@ -1072,9 +1223,13 @@
}
}
- public void close() throws Exception {
- synchronized (lock) {
- keyedPool.close();
+ public void close() {
+ try {
+ synchronized (lock) {
+ keyedPool.close();
+ }
+ } catch (Exception e) {
+ // swallowed as of Pool 2
}
}
@@ -1192,6 +1347,270 @@
sb.append("{keyedFactory=").append(keyedFactory);
sb.append('}');
return sb.toString();
+ }
+ }
+
+ /**
+ * Encapsulate the logic for when the next poolable object should be
discarded.
+ */
+ private static class ErodingFactor {
+ private final float factor;
+ private transient volatile long nextShrink;
+ private transient volatile int idleHighWaterMark;
+
+ public ErodingFactor(final float factor) {
+ this.factor = factor;
+ nextShrink = System.currentTimeMillis() + (long)(900000 * factor);
// now + 15 min * factor
+ idleHighWaterMark = 1;
+ }
+
+ public void update(final int numIdle) {
+ update(System.currentTimeMillis(), numIdle);
+ }
+
+ 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);
+ }
+
+ public long getNextShrink() {
+ return nextShrink;
+ }
+
+ public String toString() {
+ return "ErodingFactor{" +
+ "factor=" + factor +
+ ", idleHighWaterMark=" + idleHighWaterMark +
+ '}';
+ }
+ }
+
+ private static class ErodingObjectPool implements ObjectPool {
+ private final ObjectPool pool;
+ private final ErodingFactor factor;
+
+ public ErodingObjectPool(final ObjectPool pool, final float factor) {
+ this.pool = pool;
+ this.factor = new ErodingFactor(factor);
+ }
+
+ public Object borrowObject() throws Exception, NoSuchElementException,
IllegalStateException {
+ return pool.borrowObject();
+ }
+
+ public void returnObject(final Object 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 (Exception e) {
+ // swallowed
+ }
+ }
+
+ public void invalidateObject(final Object obj) {
+ try {
+ pool.invalidateObject(obj);
+ } catch (Exception e) {
+ // swallowed
+ }
+ }
+
+ public void addObject() throws Exception, IllegalStateException,
UnsupportedOperationException {
+ pool.addObject();
+ }
+
+ public int getNumIdle() throws UnsupportedOperationException {
+ return pool.getNumIdle();
+ }
+
+ public int getNumActive() throws UnsupportedOperationException {
+ return pool.getNumActive();
+ }
+
+ public void clear() throws Exception, UnsupportedOperationException {
+ pool.clear();
+ }
+
+ public void close() {
+ try {
+ pool.close();
+ } catch (Exception e) {
+ // swallowed
+ }
+ }
+
+ public void setFactory(final PoolableObjectFactory factory) throws
IllegalStateException, UnsupportedOperationException {
+ pool.setFactory(factory);
+ }
+
+ public String toString() {
+ return "ErodingObjectPool{" +
+ "factor=" + factor +
+ ", pool=" + pool +
+ '}';
+ }
+ }
+
+ private static class ErodingKeyedObjectPool implements KeyedObjectPool {
+ private final KeyedObjectPool keyedPool;
+ private final ErodingFactor erodingFactor;
+
+ public ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final
float factor) {
+ this(keyedPool, new ErodingFactor(factor));
+ }
+
+ protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool,
final ErodingFactor erodingFactor) {
+ if (keyedPool == null) {
+ throw new IllegalArgumentException("keyedPool must not be
null.");
+ }
+ this.keyedPool = keyedPool;
+ this.erodingFactor = erodingFactor;
+ }
+
+ public Object borrowObject(final Object key) throws Exception,
NoSuchElementException, IllegalStateException {
+ return keyedPool.borrowObject(key);
+ }
+
+ public void returnObject(final Object key, final Object 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 = numIdle(key);
+ if (numIdle > 0) {
+ discard = true;
+ }
+
+ factor.update(now, numIdle);
+ }
+ }
+ try {
+ if (discard) {
+ keyedPool.invalidateObject(key, obj);
+ } else {
+ keyedPool.returnObject(key, obj);
+ }
+ } catch (Exception e) {
+ // swallowed
+ }
+ }
+
+ protected int numIdle(final Object key) {
+ return getKeyedPool().getNumIdle();
+ }
+
+ protected ErodingFactor getErodingFactor(final Object key) {
+ return erodingFactor;
+ }
+
+ public void invalidateObject(final Object key, final Object obj) {
+ try {
+ keyedPool.invalidateObject(key, obj);
+ } catch (Exception e) {
+ // swallowed
+ }
+ }
+
+ public void addObject(final Object key) throws Exception,
IllegalStateException, UnsupportedOperationException {
+ keyedPool.addObject(key);
+ }
+
+ public int getNumIdle() throws UnsupportedOperationException {
+ return keyedPool.getNumIdle();
+ }
+
+ public int getNumIdle(final Object key) throws
UnsupportedOperationException {
+ return keyedPool.getNumIdle(key);
+ }
+
+ public int getNumActive() throws UnsupportedOperationException {
+ return keyedPool.getNumActive();
+ }
+
+ public int getNumActive(final Object key) throws
UnsupportedOperationException {
+ return keyedPool.getNumActive(key);
+ }
+
+ public void clear() throws Exception, UnsupportedOperationException {
+ keyedPool.clear();
+ }
+
+ public void clear(final Object key) throws Exception,
UnsupportedOperationException {
+ keyedPool.clear(key);
+ }
+
+ public void close() {
+ try {
+ keyedPool.close();
+ } catch (Exception e) {
+ // swallowed
+ }
+ }
+
+ public void setFactory(final KeyedPoolableObjectFactory factory)
throws IllegalStateException, UnsupportedOperationException {
+ keyedPool.setFactory(factory);
+ }
+
+ protected KeyedObjectPool getKeyedPool() {
+ return keyedPool;
+ }
+
+ public String toString() {
+ return "ErodingKeyedObjectPool{" +
+ "erodingFactor=" + erodingFactor +
+ ", keyedPool=" + keyedPool +
+ '}';
+ }
+ }
+
+ private static class ErodingPerKeyKeyedObjectPool extends
ErodingKeyedObjectPool {
+ private final float factor;
+ private final Map factors = Collections.synchronizedMap(new HashMap());
+
+ public ErodingPerKeyKeyedObjectPool(final KeyedObjectPool keyedPool,
final float factor) {
+ super(keyedPool, null);
+ this.factor = factor;
+ }
+
+ protected int numIdle(final Object key) {
+ return getKeyedPool().getNumIdle(key);
+ }
+
+ protected ErodingFactor getErodingFactor(final Object key) {
+ ErodingFactor factor = (ErodingFactor)factors.get(key);
+ // this may result in two ErodingFactors being created for a key
+ // since they are small and cheap this is okay.
+ if (factor == null) {
+ factor = new ErodingFactor(this.factor);
+ factors.put(key, factor);
+ }
+ return factor;
+ }
+
+ public String toString() {
+ return "ErodingPerKeyKeyedObjectPool{" +
+ "factor=" + factor +
+ ", keyedPool=" + getKeyedPool() +
+ '}';
}
}
}
Modified:
jakarta/commons/proper/pool/trunk/src/test/org/apache/commons/pool/TestPoolUtils.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/pool/trunk/src/test/org/apache/commons/pool/TestPoolUtils.java?view=diff&rev=474109&r1=474108&r2=474109
==============================================================================
---
jakarta/commons/proper/pool/trunk/src/test/org/apache/commons/pool/TestPoolUtils.java
(original)
+++
jakarta/commons/proper/pool/trunk/src/test/org/apache/commons/pool/TestPoolUtils.java
Sun Nov 12 17:01:22 2006
@@ -79,7 +79,7 @@
// expected
}
try {
-
PoolUtils.adapt((KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class,
null), null);
+
PoolUtils.adapt((KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class,
(List)null), null);
fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not
allow null key.");
} catch (IllegalArgumentException iae) {
// expected
@@ -128,7 +128,7 @@
// expected
}
try {
-
PoolUtils.adapt((KeyedObjectPool)createProxy(KeyedObjectPool.class, null),
null);
+
PoolUtils.adapt((KeyedObjectPool)createProxy(KeyedObjectPool.class,
(List)null), null);
fail("PoolUtils.adapt(KeyedObjectPool, key) must not allow a null
key.");
} catch(IllegalArgumentException iae) {
// expected
@@ -166,7 +166,7 @@
// expected
}
try {
- PoolUtils.checkedPool((ObjectPool)createProxy(ObjectPool.class,
null), null);
+ PoolUtils.checkedPool((ObjectPool)createProxy(ObjectPool.class,
(List)null), null);
fail("PoolUtils.checkedPool(ObjectPool, Class) must not allow a
null type.");
} catch(IllegalArgumentException iae) {
// expected
@@ -216,7 +216,7 @@
// expected
}
try {
-
PoolUtils.checkedPool((KeyedObjectPool)createProxy(KeyedObjectPool.class,
null), null);
+
PoolUtils.checkedPool((KeyedObjectPool)createProxy(KeyedObjectPool.class,
(List)null), null);
fail("PoolUtils.checkedPool(KeyedObjectPool, Class) must not allow
a null type.");
} catch(IllegalArgumentException iae) {
// expected
@@ -269,7 +269,7 @@
// expected
}
try {
- final ObjectPool pool = (ObjectPool)createProxy(ObjectPool.class,
null);
+ final ObjectPool pool = (ObjectPool)createProxy(ObjectPool.class,
(List)null);
PoolUtils.checkMinIdle(pool, -1, 1);
fail("PoolUtils.checkMinIdle(ObjectPool,,) must not accept
negative min idle values.");
} catch (IllegalArgumentException iae) {
@@ -333,14 +333,14 @@
// expected
}
try {
- final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, null);
+ final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
PoolUtils.checkMinIdle(pool, (Object)null, 1, 1);
fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must
not accept null keys.");
} catch (IllegalArgumentException iae) {
// expected
}
try {
- final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, null);
+ final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
PoolUtils.checkMinIdle(pool, new Object(), -1, 1);
fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must
not accept negative min idle values.");
} catch (IllegalArgumentException iae) {
@@ -400,7 +400,7 @@
public void testCheckMinIdleKeyedObjectPoolKeys() throws Exception {
try {
- final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, null);
+ final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
PoolUtils.checkMinIdle(pool, null, 1, 1);
fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long)
must not accept null keys.");
} catch (IllegalArgumentException iae) {
@@ -473,7 +473,7 @@
// expected
}
try {
- final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, null);
+ final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
PoolUtils.prefill(pool, (Object)null, 1);
fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not
accept null key.");
} catch (IllegalArgumentException iae) {
@@ -497,7 +497,7 @@
public void testPrefillKeyedObjectPoolCollection() throws Exception {
try {
- final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, null);
+ final KeyedObjectPool pool =
(KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
PoolUtils.prefill(pool, null, 1);
fail("PoolUtils.prefill(KeyedObjectPool,Collection,int) must not
accept null keys.");
} catch (IllegalArgumentException iae) {
@@ -597,6 +597,174 @@
// TODO: Anyone feel motivated to construct a test that verifies
proper synchronization?
}
+ public void testErodingPoolObjectPool() throws Exception {
+ try {
+ PoolUtils.erodingPool((ObjectPool)null);
+ fail("PoolUtils.erodingPool(ObjectPool) must not allow a null
pool.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ PoolUtils.erodingPool((ObjectPool)null, 1f);
+ fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a
null pool.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ PoolUtils.erodingPool((ObjectPool)null, 0);
+ fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a
non-positive factor.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ final List calledMethods = new ArrayList();
+ final InvocationHandler handler = new MethodCallLogger(calledMethods) {
+ public Object invoke(final Object proxy, final Method method,
final Object[] args) throws Throwable {
+ Object o = super.invoke(proxy, method, args);
+ if (o instanceof Integer) {
+ // so getNumActive/getNumIdle are not zero.
+ o = new Integer(1);
+ }
+ return o;
+ }
+ };
+
+ // If the logic behind PoolUtils.erodingPool changes then this will
need to be tweaked.
+ float factor = 0.01f; // about ~9 seconds until first discard
+ final ObjectPool pool =
PoolUtils.erodingPool((ObjectPool)createProxy(ObjectPool.class, handler),
factor);
+
+ final List expectedMethods = new ArrayList();
+ assertEquals(expectedMethods, calledMethods);
+
+ Object o = pool.borrowObject();
+ expectedMethods.add("borrowObject");
+
+ assertEquals(expectedMethods, calledMethods);
+
+ pool.returnObject(o);
+ expectedMethods.add("returnObject");
+ assertEquals(expectedMethods, calledMethods);
+
+ for (int i=0; i < 5; i ++) {
+ o = pool.borrowObject();
+ expectedMethods.add("borrowObject");
+
+ Thread.sleep(50);
+
+ pool.returnObject(o);
+ expectedMethods.add("returnObject");
+
+ assertEquals(expectedMethods, calledMethods);
+
+ expectedMethods.clear();
+ calledMethods.clear();
+ }
+
+ Thread.sleep(10000); // 10 seconds
+
+
+ o = pool.borrowObject();
+ expectedMethods.add("borrowObject");
+ pool.returnObject(o);
+ expectedMethods.add("getNumIdle");
+ expectedMethods.add("invalidateObject");
+ assertEquals(expectedMethods, calledMethods);
+ }
+
+ public void testErodingPoolKeyedObjectPool() throws Exception {
+ try {
+ PoolUtils.erodingPool((KeyedObjectPool)null);
+ fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null
pool.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ PoolUtils.erodingPool((KeyedObjectPool)null, 1f);
+ fail("PoolUtils.erodingPool(KeyedObjectPool, float) must not allow
a null pool.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ PoolUtils.erodingPool((KeyedObjectPool)null, 0);
+ fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a
non-positive factor.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ PoolUtils.erodingPool((KeyedObjectPool)null, 1f, true);
+ fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must
not allow a null pool.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ PoolUtils.erodingPool((KeyedObjectPool)null, 0, false);
+ fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not
allow a non-positive factor.");
+ } catch(IllegalArgumentException iae) {
+ // expected
+ }
+
+ final List calledMethods = new ArrayList();
+ final InvocationHandler handler = new MethodCallLogger(calledMethods) {
+ public Object invoke(final Object proxy, final Method method,
final Object[] args) throws Throwable {
+ Object o = super.invoke(proxy, method, args);
+ if (o instanceof Integer) {
+ // so getNumActive/getNumIdle are not zero.
+ o = new Integer(1);
+ }
+ return o;
+ }
+ };
+
+ // If the logic behind PoolUtils.erodingPool changes then this will
need to be tweaked.
+ float factor = 0.01f; // about ~9 seconds until first discard
+ final KeyedObjectPool pool =
PoolUtils.erodingPool((KeyedObjectPool)createProxy(KeyedObjectPool.class,
handler), factor);
+
+ final List expectedMethods = new ArrayList();
+ assertEquals(expectedMethods, calledMethods);
+
+ final Object key = "key";
+
+ Object o = pool.borrowObject(key);
+ expectedMethods.add("borrowObject");
+
+ assertEquals(expectedMethods, calledMethods);
+
+ pool.returnObject(key, o);
+ expectedMethods.add("returnObject");
+ assertEquals(expectedMethods, calledMethods);
+
+ for (int i=0; i < 5; i ++) {
+ o = pool.borrowObject(key);
+ expectedMethods.add("borrowObject");
+
+ Thread.sleep(50);
+
+ pool.returnObject(key, o);
+ expectedMethods.add("returnObject");
+
+ assertEquals(expectedMethods, calledMethods);
+
+ expectedMethods.clear();
+ calledMethods.clear();
+ }
+
+ Thread.sleep(10000); // 10 seconds
+
+
+ o = pool.borrowObject(key);
+ expectedMethods.add("borrowObject");
+ pool.returnObject(key, o);
+ expectedMethods.add("getNumIdle");
+ expectedMethods.add("invalidateObject");
+ assertEquals(expectedMethods, calledMethods);
+ }
+
private static List invokeEveryMethod(ObjectPool op) throws Exception {
op.addObject();
op.borrowObject();
@@ -606,7 +774,7 @@
op.getNumIdle();
op.invalidateObject(new Object());
op.returnObject(new Object());
-
op.setFactory((PoolableObjectFactory)createProxy(PoolableObjectFactory.class,
null));
+
op.setFactory((PoolableObjectFactory)createProxy(PoolableObjectFactory.class,
(List)null));
op.toString();
final List expectedMethods = Arrays.asList(new String[] {
@@ -629,7 +797,7 @@
kop.getNumIdle(null);
kop.invalidateObject(null, new Object());
kop.returnObject(null, new Object());
-
kop.setFactory((KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class,
null));
+
kop.setFactory((KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class,
(List)null));
kop.toString();
final List expectedMethods = Arrays.asList(new String[] {
@@ -671,8 +839,11 @@
}
private static Object createProxy(final Class clazz, final List logger) {
- return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {
clazz },
- new MethodCallLogger(logger));
+ return createProxy(clazz, new MethodCallLogger(logger));
+ }
+
+ private static Object createProxy(final Class clazz, final
InvocationHandler handler) {
+ return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {
clazz }, handler);
}
private static class MethodCallLogger implements InvocationHandler {
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]