/* * 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.commons.pool; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimerTask; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.commons.pool.impl.GenericKeyedObjectPool; import org.apache.commons.pool.impl.GenericObjectPool; /** * Unit tests for {@link PoolUtils}. * * @author Sandy McArthur * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $ */ public class TestPoolUtils extends TestCase { /** Period between checks for minIdle tests. Increase this if you happen to get too many false failures. */ private static final int CHECK_PERIOD = 300; /** Times to let the minIdle check run. */ private static final int CHECK_COUNT = 4; /** Sleep time to let the minIdle tests run CHECK_COUNT times. */ private static final int CHECK_SLEEP_PERIOD = CHECK_PERIOD * (CHECK_COUNT - 1) + CHECK_PERIOD / 2; public void testJavaBeanInstantiation() { new PoolUtils(); } public void testAdaptKeyedPoolableObjectFactory() throws Exception { try { PoolUtils.adapt((KeyedPoolableObjectFactory<Object, Object>)null); fail("PoolUtils.adapt(KeyedPoolableObjectFactory) must not allow null factory."); } catch (IllegalArgumentException iae) { // expected } } public void testAdaptKeyedPoolableObjectFactoryKey() throws Exception { try { PoolUtils.adapt((KeyedPoolableObjectFactory<Object, Object>)null, new Object()); fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null factory."); } catch (IllegalArgumentException iae) { // expected } try { @SuppressWarnings("unchecked") final KeyedPoolableObjectFactory<Object, Object> proxy = createProxy(KeyedPoolableObjectFactory.class, (List<String>)null); PoolUtils.adapt(proxy, null); fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null key."); } catch (IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final KeyedPoolableObjectFactory<Object, Object> kpof = createProxy(KeyedPoolableObjectFactory.class, calledMethods); final PoolableObjectFactory<Object> pof = PoolUtils.adapt(kpof); final List<String> expectedMethods = invokeEveryMethod(pof); assertEquals(expectedMethods, calledMethods); } public void testAdaptPoolableObjectFactory() throws Exception { try { PoolUtils.adapt((PoolableObjectFactory<?>)null); fail("PoolUtils.adapt(PoolableObjectFactory) must not allow null factory."); } catch (IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); final PoolableObjectFactory<?> pof = createProxy(PoolableObjectFactory.class, calledMethods); final KeyedPoolableObjectFactory<?, ?> kpof = PoolUtils.adapt(pof); final List<String> expectedMethods = invokeEveryMethod(kpof); assertEquals(expectedMethods, calledMethods); } public void testAdaptKeyedObjectPool() throws Exception { try { PoolUtils.adapt((KeyedObjectPool<Object ,?>)null); fail("PoolUtils.adapt(KeyedObjectPool) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } } public void testAdaptKeyedObjectPoolKey() throws Exception { try { PoolUtils.adapt((KeyedObjectPool<Object, Object>)null, new Object()); fail("PoolUtils.adapt(KeyedObjectPool, key) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> proxy = createProxy(KeyedObjectPool.class, (List<String>)null); PoolUtils.adapt(proxy, null); fail("PoolUtils.adapt(KeyedObjectPool, key) must not allow a null key."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> kop = createProxy(KeyedObjectPool.class, calledMethods); final ObjectPool<Object> op = PoolUtils.adapt(kop, new Object()); final List<String> expectedMethods = invokeEveryMethod(op); assertEquals(expectedMethods, calledMethods); } public void testAdaptObjectPool() throws Exception { try { PoolUtils.adapt((ObjectPool<?>)null); fail("PoolUtils.adapt(ObjectPool) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods); final KeyedObjectPool<Object, Object> kop = PoolUtils.adapt(op); final List<String> expectedMethods = invokeEveryMethod(kop); assertEquals(expectedMethods, calledMethods); } public void testCheckedPoolObjectPool() throws Exception { try { PoolUtils.checkedPool((ObjectPool<Object>)null, Object.class); fail("PoolUtils.checkedPool(ObjectPool, Class) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { final ObjectPool<?> proxy = createProxy(ObjectPool.class, (List<String>)null); PoolUtils.checkedPool(proxy, null); fail("PoolUtils.checkedPool(ObjectPool, Class) must not allow a null type."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods); ObjectPool<Object> cop = PoolUtils.checkedPool(op, Object.class); final List<String> expectedMethods = invokeEveryMethod(cop); assertEquals(expectedMethods, calledMethods); op = new BaseObjectPool<Object>() { @Override public Object borrowObject() throws Exception { return new Integer(0); } @Override public void returnObject(Object obj) {} @Override public void invalidateObject(Object obj) {} }; // cast to ObjectPool to be able to use String.class @SuppressWarnings({ "rawtypes", "unchecked" }) final ObjectPool checkedPool = PoolUtils.checkedPool((ObjectPool)op, String.class); @SuppressWarnings("unchecked") final ObjectPool<Object> cop2 = checkedPool; try { cop2.borrowObject(); fail("borrowObject should have failed as Integer !instanceof String."); } catch (ClassCastException cce) { // expected } try { cop2.returnObject(new Integer(1)); fail("returnObject should have failed as Integer !instanceof String."); } catch (ClassCastException cce) { // expected } try { cop2.invalidateObject(new Integer(2)); fail("invalidateObject should have failed as Integer !instanceof String."); } catch (ClassCastException cce) { // expected } } public void testCheckedPoolKeyedObjectPool() throws Exception { try { PoolUtils.checkedPool((KeyedObjectPool<Object, Object>)null, Object.class); fail("PoolUtils.checkedPool(KeyedObjectPool, Class) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.checkedPool((KeyedObjectPool<?, ?>)createProxy(KeyedObjectPool.class, (List<String>)null), null); fail("PoolUtils.checkedPool(KeyedObjectPool, Class) must not allow a null type."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") KeyedObjectPool<Object, Object> op = createProxy(KeyedObjectPool.class, calledMethods); KeyedObjectPool<Object, Object> cop = PoolUtils.checkedPool(op, Object.class); final List<String> expectedMethods = invokeEveryMethod(cop); assertEquals(expectedMethods, calledMethods); op = new BaseKeyedObjectPool<Object, Object>() { @Override public Integer borrowObject(Object key) { return new Integer(0); } @Override public void returnObject(Object key, Object obj) {} @Override public void invalidateObject(Object key, Object obj) {} }; @SuppressWarnings("rawtypes") final KeyedObjectPool rawPool = op; @SuppressWarnings("unchecked") KeyedObjectPool<Object, Object> cop2 = PoolUtils.checkedPool(rawPool, String.class); try { cop2.borrowObject(null); fail("borrowObject should have failed as Integer !instanceof String."); } catch (ClassCastException cce) { // expected } try { cop2.returnObject(null, new Integer(1)); fail("returnObject should have failed as Integer !instanceof String."); } catch (ClassCastException cce) { // expected } try { cop2.invalidateObject(null, new Integer(2)); fail("invalidateObject should have failed as Integer !instanceof String."); } catch (ClassCastException cce) { // expected } } public void testCheckMinIdleObjectPool() throws Exception { try { PoolUtils.checkMinIdle(null, 1, 1); fail("PoolUtils.checkMinIdle(ObjectPool,,) must not allow null pool."); } catch (IllegalArgumentException iae) { // expected } try { final ObjectPool<?> pool = createProxy(ObjectPool.class, (List<String>)null); PoolUtils.checkMinIdle(pool, -1, 1); fail("PoolUtils.checkMinIdle(ObjectPool,,) must not accept negative min idle values."); } catch (IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); // Test that the minIdle check doesn't add too many idle objects @SuppressWarnings("unchecked") final PoolableObjectFactory<Object> pof = createProxy(PoolableObjectFactory.class, calledMethods); final ObjectPool<Object> op = new GenericObjectPool<Object>(pof); PoolUtils.checkMinIdle(op, 2, 100); Thread.sleep(400); assertEquals(2, op.getNumIdle()); op.close(); int makeObjectCount = 0; final Iterator<String> iter = calledMethods.iterator(); while (iter.hasNext()) { final String methodName = iter.next(); if ("makeObject".equals(methodName)) { makeObjectCount++; } } assertEquals("makeObject should have been called two time", 2, makeObjectCount); // Because this isn't deterministic and you can get false failures, try more than once. AssertionFailedError afe = null; int triesLeft = 3; do { afe = null; try { calledMethods.clear(); @SuppressWarnings("unchecked") final ObjectPool<Object> pool = createProxy(ObjectPool.class, calledMethods); final TimerTask task = PoolUtils.checkMinIdle(pool, 1, CHECK_PERIOD); // checks minIdle immediately Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times. task.cancel(); task.toString(); final List<String> expectedMethods = new ArrayList<String>(); for (int i=0; i < CHECK_COUNT; i++) { expectedMethods.add("getNumIdle"); expectedMethods.add("addObject"); } expectedMethods.add("toString"); assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler } catch (AssertionFailedError e) { afe = e; } } while (--triesLeft > 0 && afe != null); if (afe != null) { throw afe; } } public void testCheckMinIdleKeyedObjectPool() throws Exception { try { PoolUtils.checkMinIdle(null, new Object(), 1, 1); fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not allow null pool."); } catch (IllegalArgumentException iae) { // expected } try { @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)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 { @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)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) { // expected } final List<String> calledMethods = new ArrayList<String>(); final Object key = new Object(); // Test that the minIdle check doesn't add too many idle objects @SuppressWarnings("unchecked") final KeyedPoolableObjectFactory<Object, Object> kpof = createProxy(KeyedPoolableObjectFactory.class, calledMethods); final KeyedObjectPool<Object, Object> kop = new GenericKeyedObjectPool<Object, Object>(kpof); PoolUtils.checkMinIdle(kop, key, 2, 100); Thread.sleep(400); assertEquals(2, kop.getNumIdle(key)); assertEquals(2, kop.getNumIdle()); kop.close(); int makeObjectCount = 0; final Iterator<String> iter = calledMethods.iterator(); while (iter.hasNext()) { final String methodName = iter.next(); if ("makeObject".equals(methodName)) { makeObjectCount++; } } assertEquals("makeObject should have been called two time", 2, makeObjectCount); // Because this isn't deterministic and you can get false failures, try more than once. AssertionFailedError afe = null; int triesLeft = 3; do { afe = null; try { calledMethods.clear(); @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, calledMethods); final TimerTask task = PoolUtils.checkMinIdle(pool, key, 1, CHECK_PERIOD); // checks minIdle immediately Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times. task.cancel(); task.toString(); final List<String> expectedMethods = new ArrayList<String>(); for (int i=0; i < CHECK_COUNT; i++) { expectedMethods.add("getNumIdle"); expectedMethods.add("addObject"); } expectedMethods.add("toString"); assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler } catch (AssertionFailedError e) { afe = e; } } while (--triesLeft > 0 && afe != null); if (afe != null) { throw afe; } } public void testCheckMinIdleKeyedObjectPoolKeys() throws Exception { try { @SuppressWarnings("unchecked") final KeyedObjectPool<String, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null); PoolUtils.checkMinIdle(pool, (Collection<String>)null, 1, 1); fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must not accept null keys."); } catch (IllegalArgumentException iae) { // expected } // Because this isn't determinist and you can get false failures, try more than once. AssertionFailedError afe = null; int triesLeft = 3; do { afe = null; try { final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final KeyedObjectPool<String, ?> pool = createProxy(KeyedObjectPool.class, calledMethods); final Collection<String> keys = new ArrayList<String>(2); keys.add("one"); keys.add("two"); final Map<String, TimerTask> tasks = PoolUtils.checkMinIdle(pool, keys, 1, CHECK_PERIOD); // checks minIdle immediately Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times. final Iterator<TimerTask> iter = tasks.values().iterator(); while (iter.hasNext()) { final TimerTask task = iter.next(); task.cancel(); } final List<String> expectedMethods = new ArrayList<String>(); for (int i=0; i < CHECK_COUNT * keys.size(); i++) { expectedMethods.add("getNumIdle"); expectedMethods.add("addObject"); } assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler } catch (AssertionFailedError e) { afe = e; } } while (--triesLeft > 0 && afe != null); if (afe != null) { throw afe; } } public void testPrefillObjectPool() throws Exception { try { PoolUtils.prefill(null, 1); fail("PoolUtils.prefill(ObjectPool,int) must not allow null pool."); } catch (IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); final ObjectPool<?> pool = createProxy(ObjectPool.class, calledMethods); PoolUtils.prefill(pool, 0); final List<String> expectedMethods = new ArrayList<String>(); assertEquals(expectedMethods, calledMethods); calledMethods.clear(); PoolUtils.prefill(pool, 3); for (int i=0; i < 3; i++) { expectedMethods.add("addObject"); } assertEquals(expectedMethods, calledMethods); } public void testPrefillKeyedObjectPool() throws Exception { try { PoolUtils.prefill(null, new Object(), 1); fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null pool."); } catch (IllegalArgumentException iae) { // expected } try { @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null); PoolUtils.prefill(pool, (Object)null, 1); fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null key."); } catch (IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, calledMethods); PoolUtils.prefill(pool, new Object(), 0); final List<String> expectedMethods = new ArrayList<String>(); assertEquals(expectedMethods, calledMethods); calledMethods.clear(); PoolUtils.prefill(pool, new Object(), 3); for (int i=0; i < 3; i++) { expectedMethods.add("addObject"); } assertEquals(expectedMethods, calledMethods); } public void testPrefillKeyedObjectPoolCollection() throws Exception { try { @SuppressWarnings("unchecked") final KeyedObjectPool<String, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null); PoolUtils.prefill(pool, (Collection<String>)null, 1); fail("PoolUtils.prefill(KeyedObjectPool,Collection,int) must not accept null keys."); } catch (IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final KeyedObjectPool<Number, Object> pool = createProxy(KeyedObjectPool.class, calledMethods); final Set<Number> keys = new HashSet<Number>(); PoolUtils.prefill(pool, keys, 0); final List<String> expectedMethods = new ArrayList<String>(); assertEquals(expectedMethods, calledMethods); calledMethods.clear(); keys.add(new Integer(1)); keys.add(new Long(2)); keys.add(new Double(3.1415926)); PoolUtils.prefill(pool, keys, 3); for (int i=0; i < keys.size() * 3; i++) { expectedMethods.add("addObject"); } assertEquals(expectedMethods, calledMethods); } public void testSynchronizedPoolObjectPool() throws Exception { try { PoolUtils.synchronizedPool((ObjectPool<Object>)null); fail("PoolUtils.synchronizedPool(ObjectPool) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods); final ObjectPool<Object> sop = PoolUtils.synchronizedPool(op); final List<String> expectedMethods = invokeEveryMethod(sop); assertEquals(expectedMethods, calledMethods); // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } public void testSynchronizedPoolKeyedObjectPool() throws Exception { try { PoolUtils.synchronizedPool((KeyedObjectPool<Object, Object>)null); fail("PoolUtils.synchronizedPool(KeyedObjectPool) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> kop = createProxy(KeyedObjectPool.class, calledMethods); final KeyedObjectPool<Object, Object> skop = PoolUtils.synchronizedPool(kop); final List<String> expectedMethods = invokeEveryMethod(skop); assertEquals(expectedMethods, calledMethods); // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } public void testSynchronizedPoolableFactoryPoolableObjectFactory() throws Exception { try { PoolUtils.synchronizedPoolableFactory((PoolableObjectFactory<Object>)null); fail("PoolUtils.synchronizedPoolableFactory(PoolableObjectFactory) must not allow a null factory."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final PoolableObjectFactory<Object> pof = createProxy(PoolableObjectFactory.class, calledMethods); final PoolableObjectFactory<Object> spof = PoolUtils.synchronizedPoolableFactory(pof); final List<String> expectedMethods = invokeEveryMethod(spof); assertEquals(expectedMethods, calledMethods); // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } public void testSynchronizedPoolableFactoryKeyedPoolableObjectFactory() throws Exception { try { PoolUtils.synchronizedPoolableFactory((KeyedPoolableObjectFactory<Object, Object>)null); fail("PoolUtils.synchronizedPoolableFactory(KeyedPoolableObjectFactory) must not allow a null factory."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); @SuppressWarnings("unchecked") final KeyedPoolableObjectFactory<Object, Object> kpof = createProxy(KeyedPoolableObjectFactory.class, calledMethods); final KeyedPoolableObjectFactory<Object, Object> skpof = PoolUtils.synchronizedPoolableFactory(kpof); final List<String> expectedMethods = invokeEveryMethod(skpof); assertEquals(expectedMethods, calledMethods); // TODO: Anyone feel motivated to construct a test that verifies proper synchronization? } public void testErodingPoolObjectPool() throws Exception { try { PoolUtils.erodingPool((ObjectPool<Object>)null); fail("PoolUtils.erodingPool(ObjectPool) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((ObjectPool<Object>)null, 1f); fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((ObjectPool<Object>)null, 0); fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); final InvocationHandler handler = new MethodCallLogger(calledMethods) { @Override 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 @SuppressWarnings("unchecked") final ObjectPool<Object> pool = PoolUtils.erodingPool((ObjectPool<Object>)createProxy(ObjectPool.class, handler), factor); final List<String> expectedMethods = new ArrayList<String>(); 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<Object, Object>)null); fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1f); fail("PoolUtils.erodingPool(KeyedObjectPool, float) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 0); fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1f, true); fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 0, false); fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not allow a non-positive factor."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); final InvocationHandler handler = new MethodCallLogger(calledMethods) { @Override 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 @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> pool = PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)createProxy(KeyedObjectPool.class, handler), factor); final List<String> expectedMethods = new ArrayList<String>(); 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); } public void testErodingPerKeyKeyedObjectPool() throws Exception { try { PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1, true); fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 0, true); fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor."); } catch(IllegalArgumentException iae) { // expected } try { PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1f, true); fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool."); } catch(IllegalArgumentException iae) { // expected } final List<String> calledMethods = new ArrayList<String>(); final InvocationHandler handler = new MethodCallLogger(calledMethods) { @Override 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 @SuppressWarnings("unchecked") final KeyedObjectPool<Object, Object> pool = PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)createProxy(KeyedObjectPool.class, handler), factor, true); final List<String> expectedMethods = new ArrayList<String>(); 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<String> invokeEveryMethod(ObjectPool<Object> op) throws Exception { op.addObject(); op.borrowObject(); op.clear(); op.close(); op.getNumActive(); op.getNumIdle(); op.invalidateObject(new Object()); op.returnObject(new Object()); @SuppressWarnings("unchecked") final PoolableObjectFactory<Object> proxy = createProxy(PoolableObjectFactory.class, (List<String>)null); op.setFactory(proxy); op.toString(); final List<String> expectedMethods = Arrays.asList(new String[] { "addObject", "borrowObject", "clear", "close", "getNumActive", "getNumIdle", "invalidateObject", "returnObject", "setFactory", "toString" }); return expectedMethods; } private static List<String> invokeEveryMethod(KeyedObjectPool<Object, Object> kop) throws Exception { kop.addObject(null); kop.borrowObject(null); kop.clear(); kop.clear(null); kop.close(); kop.getNumActive(); kop.getNumActive(null); kop.getNumIdle(); kop.getNumIdle(null); kop.invalidateObject(null, new Object()); kop.returnObject(null, new Object()); @SuppressWarnings("unchecked") final KeyedPoolableObjectFactory<Object, Object> proxy = createProxy(KeyedPoolableObjectFactory.class, (List<String>)null); kop.setFactory(proxy); kop.toString(); final List<String> expectedMethods = Arrays.asList(new String[] { "addObject", "borrowObject", "clear", "clear", "close", "getNumActive", "getNumActive", "getNumIdle", "getNumIdle", "invalidateObject", "returnObject", "setFactory", "toString" }); return expectedMethods; } private static List<String> invokeEveryMethod(PoolableObjectFactory<?> pof) throws Exception { pof.activateObject(null); pof.destroyObject(null); pof.makeObject(); pof.passivateObject(null); pof.validateObject(null); pof.toString(); final List<String> expectedMethods = Arrays.asList(new String[] { "activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString", }); return expectedMethods; } private static List<String> invokeEveryMethod(KeyedPoolableObjectFactory<?, ?> kpof) throws Exception { kpof.activateObject(null, null); kpof.destroyObject(null, null); kpof.makeObject(null); kpof.passivateObject(null, null); kpof.validateObject(null, null); kpof.toString(); final List<String> expectedMethods = Arrays.asList(new String[] { "activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString", }); return expectedMethods; } /** * Call sites can use <code>@SuppressWarnings("unchecked")</code> */ private static <T> T createProxy(final Class<T> clazz, final List<String> logger) { return createProxy(clazz, new MethodCallLogger(logger)); } /** * Call sites can use <code>@SuppressWarnings("unchecked")</code> */ private static <T> T createProxy(final Class<T> clazz, final InvocationHandler handler) { return clazz.cast(Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, handler)); } private static class MethodCallLogger implements InvocationHandler { private final List<String> calledMethods; MethodCallLogger(final List<String> calledMethods) { this.calledMethods = calledMethods; } public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { calledMethods.add(method.getName()); if (boolean.class.equals(method.getReturnType())) { return Boolean.FALSE; } else if (int.class.equals(method.getReturnType())) { return new Integer(0); } else if (long.class.equals(method.getReturnType())) { return new Long(0); } else if (Object.class.equals(method.getReturnType())) { return new Object(); } else { return null; } } } }