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]