/* * 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.pool2; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.junit.After; import org.junit.Test; /** * Abstract test case for {@link ObjectPool} implementations. */ public abstract class TestKeyedObjectPool { /** * Create an {@code KeyedObjectPool} with the specified factory. * The pool should be in a default configuration and conform to the expected * behaviors described in {@link KeyedObjectPool}. * Generally speaking there should be no limits on the various object counts. * * @param factory Factory to use to associate with the pool * @return The newly created empty pool */ protected abstract KeyedObjectPool<Object,Object> makeEmptyPool( KeyedPooledObjectFactory<Object,Object> factory); protected static final String KEY = "key"; private KeyedObjectPool<Object,Object> _pool = null; protected abstract Object makeKey(int n); protected abstract boolean isFifo(); protected abstract boolean isLifo(); @After public void tearDown() throws Exception { _pool = null; } /** * Create an {@link KeyedObjectPool} instance * that can contain at least <i>minCapacity</i> * idle and active objects, or * throw {@link IllegalArgumentException} * if such a pool cannot be created. * @param minCapacity Minimum capacity of the pool to create * * @return the newly created keyed object pool */ protected abstract KeyedObjectPool<Object,Object> makeEmptyPool(int minCapacity); /** * Return what we expect to be the n<sup>th</sup> * object (zero indexed) created by the pool * for the given key. * @param key Key for the object to be obtained * @param n index of the object to be obtained * * @return the requested object */ protected abstract Object getNthObject(Object key, int n); @Test public void testClosedPoolBehavior() throws Exception { final KeyedObjectPool<Object,Object> pool; try { pool = makeEmptyPool(new TestFactory()); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final Object o1 = pool.borrowObject(KEY); final Object o2 = pool.borrowObject(KEY); pool.close(); try { pool.addObject(KEY); fail("A closed pool must throw an IllegalStateException when addObject is called."); } catch (final IllegalStateException ise) { // expected } try { pool.borrowObject(KEY); fail("A closed pool must throw an IllegalStateException when borrowObject is called."); } catch (final IllegalStateException ise) { // expected } // The following should not throw exceptions just because the pool is closed. assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle(KEY)); assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle()); pool.getNumActive(); pool.getNumActive(KEY); pool.returnObject(KEY, o1); assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle(KEY)); assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle()); pool.invalidateObject(KEY, o2); pool.clear(KEY); pool.clear(); pool.close(); } // Deliberate choice to create a new object in case future unit tests check // for a specific object. private final Integer ZERO = new Integer(0); private final Integer ONE = new Integer(1); @Test public void testKPOFAddObjectUsage() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool<Object,Object> pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List<MethodCall> expectedMethods = new ArrayList<>(); // addObject should make a new object, passivate it and put it in the pool pool.addObject(KEY); expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO)); expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of addObject reset(pool, factory, expectedMethods); // makeObject Exceptions should be propagated to client code from addObject factory.setMakeObjectFail(true); try { pool.addObject(KEY); fail("Expected addObject to propagate makeObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject", KEY)); assertEquals(expectedMethods, factory.getMethodCalls()); clear(factory, expectedMethods); // passivateObject Exceptions should be propagated to client code from addObject factory.setMakeObjectFail(false); factory.setPassivateObjectFail(true); try { pool.addObject(KEY); fail("Expected addObject to propagate passivateObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); expectedMethods.add(new MethodCall("passivateObject", KEY, ONE)); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testKPOFBorrowObjectUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool<Object,Object> pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List<MethodCall> expectedMethods = new ArrayList<>(); Object obj; if (pool instanceof GenericKeyedObjectPool) { ((GenericKeyedObjectPool<Object,Object>) pool).setTestOnBorrow(true); } /// Test correct behavior code paths // existing idle object should be activated and validated pool.addObject(KEY); clear(factory, expectedMethods); obj = pool.borrowObject(KEY); expectedMethods.add(new MethodCall("activateObject", KEY, ZERO)); expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE)); assertEquals(expectedMethods, factory.getMethodCalls()); pool.returnObject(KEY, obj); //// Test exception handling of borrowObject reset(pool, factory, expectedMethods); // makeObject Exceptions should be propagated to client code from borrowObject factory.setMakeObjectFail(true); try { obj = pool.borrowObject(KEY); fail("Expected borrowObject to propagate makeObject exception."); } catch (final PrivateException pe) { // expected } expectedMethods.add(new MethodCall("makeObject", KEY)); assertEquals(expectedMethods, factory.getMethodCalls()); // when activateObject fails in borrowObject, a new object should be borrowed/created reset(pool, factory, expectedMethods); pool.addObject(KEY); clear(factory, expectedMethods); factory.setActivateObjectFail(true); expectedMethods.add(new MethodCall("activateObject", KEY, obj)); try { pool.borrowObject(KEY); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException e) { //Activate should fail } // After idle object fails validation, new on is created and activation // fails again for the new one. expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); expectedMethods.add(new MethodCall("activateObject", KEY, ONE)); TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. assertEquals(expectedMethods, factory.getMethodCalls()); // when validateObject fails in borrowObject, a new object should be borrowed/created reset(pool, factory, expectedMethods); pool.addObject(KEY); clear(factory, expectedMethods); factory.setValidateObjectFail(true); // testOnBorrow is on, so this will throw when the newly created instance // fails validation try { pool.borrowObject(KEY); fail("Expecting NoSuchElementException"); } catch (final NoSuchElementException ex) { // expected } // Activate, then validate for idle instance expectedMethods.add(new MethodCall("activateObject", KEY, ZERO)); expectedMethods.add(new MethodCall("validateObject", KEY, ZERO)); // Make new instance, activate succeeds, validate fails expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); expectedMethods.add(new MethodCall("activateObject", KEY, ONE)); expectedMethods.add(new MethodCall("validateObject", KEY, ONE)); TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testKPOFReturnObjectUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool<Object,Object> pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List<MethodCall> expectedMethods = new ArrayList<>(); Object obj; /// Test correct behavior code paths obj = pool.borrowObject(KEY); clear(factory, expectedMethods); // returned object should be passivated pool.returnObject(KEY, obj); expectedMethods.add(new MethodCall("passivateObject", KEY, obj)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of returnObject reset(pool, factory, expectedMethods); // passivateObject should swallow exceptions and not add the object to the pool pool.addObject(KEY); pool.addObject(KEY); pool.addObject(KEY); assertEquals(3, pool.getNumIdle(KEY)); obj = pool.borrowObject(KEY); obj = pool.borrowObject(KEY); assertEquals(1, pool.getNumIdle(KEY)); assertEquals(2, pool.getNumActive(KEY)); clear(factory, expectedMethods); factory.setPassivateObjectFail(true); pool.returnObject(KEY, obj); expectedMethods.add(new MethodCall("passivateObject", KEY, obj)); TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. assertEquals(expectedMethods, factory.getMethodCalls()); assertEquals(1, pool.getNumIdle(KEY)); // Not added assertEquals(1, pool.getNumActive(KEY)); // But not active reset(pool, factory, expectedMethods); obj = pool.borrowObject(KEY); clear(factory, expectedMethods); factory.setPassivateObjectFail(true); factory.setDestroyObjectFail(true); try { pool.returnObject(KEY, obj); if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat fail("Expecting destroyObject exception to be propagated"); } } catch (final PrivateException ex) { // Expected } pool.close(); } @Test public void testKPOFInvalidateObjectUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool<Object,Object> pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List<MethodCall> expectedMethods = new ArrayList<>(); Object obj; /// Test correct behavior code paths obj = pool.borrowObject(KEY); clear(factory, expectedMethods); // invalidated object should be destroyed pool.invalidateObject(KEY, obj); expectedMethods.add(new MethodCall("destroyObject", KEY, obj)); assertEquals(expectedMethods, factory.getMethodCalls()); //// Test exception handling of invalidateObject reset(pool, factory, expectedMethods); obj = pool.borrowObject(KEY); clear(factory, expectedMethods); factory.setDestroyObjectFail(true); try { pool.invalidateObject(KEY, obj); fail("Expecting destroy exception to propagate"); } catch (final PrivateException ex) { // Expected } Thread.sleep(250); // could be defered TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); assertEquals(expectedMethods, factory.getMethodCalls()); pool.close(); } @Test public void testKPOFClearUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); final KeyedObjectPool<Object,Object> pool; try { pool = makeEmptyPool(factory); } catch(final UnsupportedOperationException uoe) { return; // test not supported } final List<MethodCall> expectedMethods = new ArrayList<>(); /// Test correct behavior code paths pool.addObjects(KEY, 5); pool.clear(); //// Test exception handling clear should swallow destroy object failures reset(pool, factory, expectedMethods); factory.setDestroyObjectFail(true); pool.addObjects(KEY, 5); pool.clear(); pool.close(); } @Test public void testKPOFCloseUsages() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); KeyedObjectPool<Object, Object> pool; try { pool = makeEmptyPool(factory); } catch (final UnsupportedOperationException uoe) { return; // test not supported } final List<MethodCall> expectedMethods = new ArrayList<>(); /// Test correct behavior code paths pool.addObjects(KEY, 5); pool.close(); //// Test exception handling close should swallow failures try (final KeyedObjectPool<Object, Object> pool2 = makeEmptyPool(factory)) { reset(pool2, factory, expectedMethods); factory.setDestroyObjectFail(true); pool2.addObjects(KEY, 5); } } @Test public void testToString() throws Exception { final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory(); try (final KeyedObjectPool<Object,Object> pool = makeEmptyPool(factory)) { pool.toString(); } catch(final UnsupportedOperationException uoe) { return; // test not supported } } @Test public void testBaseBorrowReturn() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); Object obj0 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,0),obj0); Object obj1 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,1),obj1); Object obj2 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,2),obj2); _pool.returnObject(keya,obj2); obj2 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,2),obj2); _pool.returnObject(keya,obj1); obj1 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,1),obj1); _pool.returnObject(keya,obj0); _pool.returnObject(keya,obj2); obj2 = _pool.borrowObject(keya); if (isLifo()) { assertEquals(getNthObject(keya,2),obj2); } if (isFifo()) { assertEquals(getNthObject(keya,0),obj2); } obj0 = _pool.borrowObject(keya); if (isLifo()) { assertEquals(getNthObject(keya,0),obj0); } if (isFifo()) { assertEquals(getNthObject(keya,2),obj0); } _pool.close(); } @Test public void testBaseBorrow() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); final Object keyb = makeKey(1); assertEquals("1",getNthObject(keya,0),_pool.borrowObject(keya)); assertEquals("2",getNthObject(keyb,0),_pool.borrowObject(keyb)); assertEquals("3",getNthObject(keyb,1),_pool.borrowObject(keyb)); assertEquals("4",getNthObject(keya,1),_pool.borrowObject(keya)); assertEquals("5",getNthObject(keyb,2),_pool.borrowObject(keyb)); assertEquals("6",getNthObject(keya,2),_pool.borrowObject(keya)); _pool.close(); } @Test public void testBaseNumActiveNumIdle() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj0 = _pool.borrowObject(keya); assertEquals(1,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj1 = _pool.borrowObject(keya); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.returnObject(keya,obj1); assertEquals(1,_pool.getNumActive(keya)); assertEquals(1,_pool.getNumIdle(keya)); _pool.returnObject(keya,obj0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(2,_pool.getNumIdle(keya)); assertEquals(0,_pool.getNumActive("xyzzy12345")); assertEquals(0,_pool.getNumIdle("xyzzy12345")); _pool.close(); } @Test public void testBaseNumActiveNumIdle2() throws Exception { try { _pool = makeEmptyPool(6); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); final Object keyb = makeKey(1); assertEquals(0,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); assertEquals(0,_pool.getNumActive(keyb)); assertEquals(0,_pool.getNumIdle(keyb)); final Object objA0 = _pool.borrowObject(keya); final Object objB0 = _pool.borrowObject(keyb); assertEquals(2,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle()); assertEquals(1,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); assertEquals(1,_pool.getNumActive(keyb)); assertEquals(0,_pool.getNumIdle(keyb)); final Object objA1 = _pool.borrowObject(keya); final Object objB1 = _pool.borrowObject(keyb); assertEquals(4,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle()); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); assertEquals(2,_pool.getNumActive(keyb)); assertEquals(0,_pool.getNumIdle(keyb)); _pool.returnObject(keya,objA0); _pool.returnObject(keyb,objB0); assertEquals(2,_pool.getNumActive()); assertEquals(2,_pool.getNumIdle()); assertEquals(1,_pool.getNumActive(keya)); assertEquals(1,_pool.getNumIdle(keya)); assertEquals(1,_pool.getNumActive(keyb)); assertEquals(1,_pool.getNumIdle(keyb)); _pool.returnObject(keya,objA1); _pool.returnObject(keyb,objB1); assertEquals(0,_pool.getNumActive()); assertEquals(4,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive(keya)); assertEquals(2,_pool.getNumIdle(keya)); assertEquals(0,_pool.getNumActive(keyb)); assertEquals(2,_pool.getNumIdle(keyb)); _pool.close(); } @Test public void testBaseClear() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj0 = _pool.borrowObject(keya); final Object obj1 = _pool.borrowObject(keya); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.returnObject(keya,obj1); _pool.returnObject(keya,obj0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(2,_pool.getNumIdle(keya)); _pool.clear(keya); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj2 = _pool.borrowObject(keya); assertEquals(getNthObject(keya,2),obj2); _pool.close(); } @Test public void testBaseInvalidateObject() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object keya = makeKey(0); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); final Object obj0 = _pool.borrowObject(keya); final Object obj1 = _pool.borrowObject(keya); assertEquals(2,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.invalidateObject(keya,obj0); assertEquals(1,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.invalidateObject(keya,obj1); assertEquals(0,_pool.getNumActive(keya)); assertEquals(0,_pool.getNumIdle(keya)); _pool.close(); } @Test public void testBaseAddObject() throws Exception { try { _pool = makeEmptyPool(3); } catch(final UnsupportedOperationException uoe) { return; // skip this test if unsupported } final Object key = makeKey(0); try { assertEquals(0,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle(key)); assertEquals(0,_pool.getNumActive(key)); _pool.addObject(key); assertEquals(1,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive()); assertEquals(1,_pool.getNumIdle(key)); assertEquals(0,_pool.getNumActive(key)); final Object obj = _pool.borrowObject(key); assertEquals(getNthObject(key,0),obj); assertEquals(0,_pool.getNumIdle()); assertEquals(1,_pool.getNumActive()); assertEquals(0,_pool.getNumIdle(key)); assertEquals(1,_pool.getNumActive(key)); _pool.returnObject(key,obj); assertEquals(1,_pool.getNumIdle()); assertEquals(0,_pool.getNumActive()); assertEquals(1,_pool.getNumIdle(key)); assertEquals(0,_pool.getNumActive(key)); } catch(final UnsupportedOperationException e) { return; // skip this test if one of those calls is unsupported } finally { _pool.close(); } } private void reset(final KeyedObjectPool<Object,Object> pool, final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) throws Exception { pool.clear(); clear(factory, expectedMethods); factory.reset(); } private void clear(final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) { factory.getMethodCalls().clear(); expectedMethods.clear(); } private static class TestFactory extends BaseKeyedPooledObjectFactory<Object,Object> { @Override public Object create(final Object key) throws Exception { return new Object(); } @Override public PooledObject<Object> wrap(final Object value) { return new DefaultPooledObject<>(value); } } protected static class FailingKeyedPooledObjectFactory implements KeyedPooledObjectFactory<Object,Object> { private final List<MethodCall> methodCalls = new ArrayList<>(); private int count = 0; private boolean makeObjectFail; private boolean activateObjectFail; private boolean validateObjectFail; private boolean passivateObjectFail; private boolean destroyObjectFail; public FailingKeyedPooledObjectFactory() { } public void reset() { count = 0; getMethodCalls().clear(); setMakeObjectFail(false); setActivateObjectFail(false); setValidateObjectFail(false); setPassivateObjectFail(false); setDestroyObjectFail(false); } public List<MethodCall> getMethodCalls() { return methodCalls; } public int getCurrentCount() { return count; } public void setCurrentCount(final int count) { this.count = count; } public boolean isMakeObjectFail() { return makeObjectFail; } public void setMakeObjectFail(final boolean makeObjectFail) { this.makeObjectFail = makeObjectFail; } public boolean isDestroyObjectFail() { return destroyObjectFail; } public void setDestroyObjectFail(final boolean destroyObjectFail) { this.destroyObjectFail = destroyObjectFail; } public boolean isValidateObjectFail() { return validateObjectFail; } public void setValidateObjectFail(final boolean validateObjectFail) { this.validateObjectFail = validateObjectFail; } public boolean isActivateObjectFail() { return activateObjectFail; } public void setActivateObjectFail(final boolean activateObjectFail) { this.activateObjectFail = activateObjectFail; } public boolean isPassivateObjectFail() { return passivateObjectFail; } public void setPassivateObjectFail(final boolean passivateObjectFail) { this.passivateObjectFail = passivateObjectFail; } @Override public PooledObject<Object> makeObject(final Object key) throws Exception { final MethodCall call = new MethodCall("makeObject", key); methodCalls.add(call); final int originalCount = this.count++; if (makeObjectFail) { throw new PrivateException("makeObject"); } // Deliberate choice to create new object in case future unit test // checks for a specific object final Integer obj = new Integer(originalCount); call.setReturned(obj); return new DefaultPooledObject<>(obj); } @Override public void activateObject(final Object key, final PooledObject<Object> obj) throws Exception { methodCalls.add(new MethodCall("activateObject", key, obj.getObject())); if (activateObjectFail) { throw new PrivateException("activateObject"); } } @Override public boolean validateObject(final Object key, final PooledObject<Object> obj) { final MethodCall call = new MethodCall("validateObject", key, obj.getObject()); methodCalls.add(call); if (validateObjectFail) { throw new PrivateException("validateObject"); } final boolean r = true; call.returned(Boolean.valueOf(r)); return r; } @Override public void passivateObject(final Object key, final PooledObject<Object> obj) throws Exception { methodCalls.add(new MethodCall("passivateObject", key, obj.getObject())); if (passivateObjectFail) { throw new PrivateException("passivateObject"); } } @Override public void destroyObject(final Object key, final PooledObject<Object> obj) throws Exception { methodCalls.add(new MethodCall("destroyObject", key, obj.getObject())); if (destroyObjectFail) { throw new PrivateException("destroyObject"); } } } }