001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.pool;
018    
019    import junit.framework.TestCase;
020    import org.apache.commons.pool.impl.GenericObjectPool;
021    import org.apache.commons.pool.impl.StackObjectPool;
022    import org.apache.commons.pool.impl.SoftReferenceObjectPool;
023    
024    import java.util.List;
025    import java.util.ArrayList;
026    import java.util.Iterator;
027    import java.util.NoSuchElementException;
028    
029    /**
030     * Abstract {@link TestCase} for {@link ObjectPool} implementations.
031     * @author Rodney Waldhoff
032     * @author Sandy McArthur
033     * @version $Revision: 775703 $ $Date: 2009-05-17 09:39:51 -0700 (Sun, 17 May 2009) $
034     */
035    public abstract class TestObjectPool extends TestCase {
036        public TestObjectPool(String testName) {
037            super(testName);
038        }
039    
040        /**
041         * Create an <code>ObjectPool</code> with the specified factory.
042         * The pool should be in a default configuration and conform to the expected
043         * behaviors described in {@link ObjectPool}.
044         * Generally speaking there should be no limits on the various object counts.
045         * @throws UnsupportedOperationException if the pool being tested does not follow pool contracts.
046         */
047        protected abstract ObjectPool makeEmptyPool(PoolableObjectFactory factory) throws UnsupportedOperationException;
048    
049        public void testClosedPoolBehavior() throws Exception {
050            final ObjectPool pool;
051            try {
052                pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
053            } catch (UnsupportedOperationException uoe) {
054                return; // test not supported
055            }
056            Object o1 = pool.borrowObject();
057            Object o2 = pool.borrowObject();
058    
059            pool.close();
060    
061            try {
062                pool.addObject();
063                fail("A closed pool must throw an IllegalStateException when addObject is called.");
064            } catch (IllegalStateException ise) {
065                // expected
066            }
067    
068            try {
069                pool.borrowObject();
070                fail("A closed pool must throw an IllegalStateException when borrowObject is called.");
071            } catch (IllegalStateException ise) {
072                // expected
073            }
074    
075            // The following should not throw exceptions just because the pool is closed.
076            if (pool.getNumIdle() >= 0) {
077                assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle());
078            }
079            if (pool.getNumActive() >= 0) {
080                assertEquals("A closed pool should still keep count of active objects.", 2, pool.getNumActive());
081            }
082            pool.returnObject(o1);
083            if (pool.getNumIdle() >= 0) {
084                assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle());
085            }
086            if (pool.getNumActive() >= 0) {
087                assertEquals("A closed pool should still keep count of active objects.", 1, pool.getNumActive());
088            }
089            pool.invalidateObject(o2);
090            if (pool.getNumIdle() >= 0) {
091                assertEquals("invalidateObject must not add items back into the idle object pool.", 0, pool.getNumIdle());
092            }
093            if (pool.getNumActive() >= 0) {
094                assertEquals("A closed pool should still keep count of active objects.", 0, pool.getNumActive());
095            }
096            pool.clear();
097            pool.close();
098        }
099    
100        private final Integer ZERO = new Integer(0);
101        private final Integer ONE = new Integer(1);
102    
103        public void testPOFAddObjectUsage() throws Exception {
104            final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
105            final ObjectPool pool;
106            try {
107                pool = makeEmptyPool(factory);
108            } catch(UnsupportedOperationException uoe) {
109                return; // test not supported
110            }
111            final List expectedMethods = new ArrayList();
112    
113            assertEquals(0, pool.getNumActive());
114            assertEquals(0, pool.getNumIdle());
115            // addObject should make a new object, pasivate it and put it in the pool
116            pool.addObject();
117            assertEquals(0, pool.getNumActive());
118            assertEquals(1, pool.getNumIdle());
119            expectedMethods.add(new MethodCall("makeObject").returned(ZERO));
120            // StackObjectPool, SoftReferenceObjectPool also validate on add
121            if (pool instanceof StackObjectPool || 
122                    pool instanceof SoftReferenceObjectPool) {
123                expectedMethods.add(new MethodCall(
124                        "validateObject", ZERO).returned(Boolean.TRUE));
125            }
126            expectedMethods.add(new MethodCall("passivateObject", ZERO));
127            assertEquals(expectedMethods, factory.getMethodCalls());
128    
129            //// Test exception handling of addObject
130            reset(pool, factory, expectedMethods);
131    
132            // makeObject Exceptions should be propagated to client code from addObject
133            factory.setMakeObjectFail(true);
134            try {
135                pool.addObject();
136                fail("Expected addObject to propagate makeObject exception.");
137            } catch (PrivateException pe) {
138                // expected
139            }
140            expectedMethods.add(new MethodCall("makeObject"));
141            assertEquals(expectedMethods, factory.getMethodCalls());
142    
143            clear(factory, expectedMethods);
144    
145            // passivateObject Exceptions should be propagated to client code from addObject
146            factory.setMakeObjectFail(false);
147            factory.setPassivateObjectFail(true);
148            try {
149                pool.addObject();
150                fail("Expected addObject to propagate passivateObject exception.");
151            } catch (PrivateException pe) {
152                // expected
153            }
154            expectedMethods.add(new MethodCall("makeObject").returned(ONE));
155            // StackObjectPool, SofReferenceObjectPool also validate on add
156            if (pool instanceof StackObjectPool || 
157                    pool instanceof SoftReferenceObjectPool) {
158                expectedMethods.add(new MethodCall(
159                        "validateObject", ONE).returned(Boolean.TRUE));
160            }
161            expectedMethods.add(new MethodCall("passivateObject", ONE));
162            assertEquals(expectedMethods, factory.getMethodCalls());
163        }
164    
165        public void testPOFBorrowObjectUsages() throws Exception {
166            final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
167            final ObjectPool pool;
168            try {
169                pool = makeEmptyPool(factory);
170            } catch (UnsupportedOperationException uoe) {
171                return; // test not supported
172            }
173            if (pool instanceof GenericObjectPool) {
174                ((GenericObjectPool) pool).setTestOnBorrow(true);
175            }
176            final List expectedMethods = new ArrayList();
177            Object obj;
178    
179            /// Test correct behavior code paths
180    
181            // existing idle object should be activated and validated
182            pool.addObject();
183            clear(factory, expectedMethods);
184            obj = pool.borrowObject();
185            expectedMethods.add(new MethodCall("activateObject", ZERO));
186            expectedMethods.add(new MethodCall("validateObject", ZERO).returned(Boolean.TRUE));
187            assertEquals(expectedMethods, factory.getMethodCalls());
188            pool.returnObject(obj);
189    
190            //// Test exception handling of borrowObject
191            reset(pool, factory, expectedMethods);
192    
193            // makeObject Exceptions should be propagated to client code from borrowObject
194            factory.setMakeObjectFail(true);
195            try {
196                obj = pool.borrowObject();
197                fail("Expected borrowObject to propagate makeObject exception.");
198            } catch (PrivateException pe) {
199                // expected
200            }
201            expectedMethods.add(new MethodCall("makeObject"));
202            assertEquals(expectedMethods, factory.getMethodCalls());
203    
204    
205            // when activateObject fails in borrowObject, a new object should be borrowed/created
206            reset(pool, factory, expectedMethods);
207            pool.addObject();
208            clear(factory, expectedMethods);
209    
210            factory.setActivateObjectFail(true);
211            expectedMethods.add(new MethodCall("activateObject", obj));
212            try {
213                obj = pool.borrowObject();
214                fail("Expecting NoSuchElementException");
215            } catch (NoSuchElementException ex) {
216                // Expected - newly created object will also fail to activate
217            }
218            // Idle object fails activation, new one created, also fails
219            expectedMethods.add(new MethodCall("makeObject").returned(ONE));
220            expectedMethods.add(new MethodCall("activateObject", ONE));
221            removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
222            assertEquals(expectedMethods, factory.getMethodCalls());
223    
224            // when validateObject fails in borrowObject, a new object should be borrowed/created
225            reset(pool, factory, expectedMethods);
226            pool.addObject();
227            clear(factory, expectedMethods);
228    
229            factory.setValidateObjectFail(true);
230            expectedMethods.add(new MethodCall("activateObject", ZERO));
231            expectedMethods.add(new MethodCall("validateObject", ZERO));
232            try {
233                obj = pool.borrowObject();
234            } catch (NoSuchElementException ex) {
235                // Expected - newly created object will also fail to validate
236            }
237            // Idle object is activated, but fails validation.
238            // New instance is created, activated and then fails validation
239            expectedMethods.add(new MethodCall("makeObject").returned(ONE));
240            expectedMethods.add(new MethodCall("activateObject", ONE));
241            expectedMethods.add(new MethodCall("validateObject", ONE));
242            removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
243            // Second activate and validate are missing from expectedMethods
244            assertTrue(factory.getMethodCalls().containsAll(expectedMethods));
245        }
246    
247        public void testPOFReturnObjectUsages() throws Exception {
248            final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
249            final ObjectPool pool;
250            try {
251                pool = makeEmptyPool(factory);
252            } catch (UnsupportedOperationException uoe) {
253                return; // test not supported
254            }
255            final List expectedMethods = new ArrayList();
256            Object obj;
257    
258            /// Test correct behavior code paths
259            obj = pool.borrowObject();
260            clear(factory, expectedMethods);
261    
262            // returned object should be passivated
263            pool.returnObject(obj);
264            // StackObjectPool, SoftReferenceObjectPool also validate on return
265            if (pool instanceof StackObjectPool || 
266                    pool instanceof SoftReferenceObjectPool) {
267                expectedMethods.add(new MethodCall(
268                        "validateObject", obj).returned(Boolean.TRUE));
269            }
270            expectedMethods.add(new MethodCall("passivateObject", obj));
271            assertEquals(expectedMethods, factory.getMethodCalls());
272    
273            //// Test exception handling of returnObject
274            reset(pool, factory, expectedMethods);
275            pool.addObject();
276            pool.addObject();
277            pool.addObject();
278            assertEquals(3, pool.getNumIdle());
279            // passivateObject should swallow exceptions and not add the object to the pool
280            obj = pool.borrowObject();
281            pool.borrowObject();
282            assertEquals(1, pool.getNumIdle());
283            assertEquals(2, pool.getNumActive());
284            clear(factory, expectedMethods);
285            factory.setPassivateObjectFail(true);
286            pool.returnObject(obj);
287            // StackObjectPool, SoftReferenceObjectPool also validate on return
288            if (pool instanceof StackObjectPool || 
289                    pool instanceof SoftReferenceObjectPool) {
290                expectedMethods.add(new MethodCall(
291                        "validateObject", obj).returned(Boolean.TRUE));
292            }
293            expectedMethods.add(new MethodCall("passivateObject", obj));
294            removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
295            assertEquals(expectedMethods, factory.getMethodCalls());
296            assertEquals(1, pool.getNumIdle());   // Not returned
297            assertEquals(1, pool.getNumActive()); // But not in active count
298    
299            // destroyObject should swallow exceptions too
300            reset(pool, factory, expectedMethods);
301            obj = pool.borrowObject();
302            clear(factory, expectedMethods);
303            factory.setPassivateObjectFail(true);
304            factory.setDestroyObjectFail(true);
305            pool.returnObject(obj);
306        }
307    
308        public void testPOFInvalidateObjectUsages() throws Exception {
309            final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
310            final ObjectPool pool;
311            try {
312                pool = makeEmptyPool(factory);
313            } catch (UnsupportedOperationException uoe) {
314                return; // test not supported
315            }
316            final List expectedMethods = new ArrayList();
317            Object obj;
318    
319            /// Test correct behavior code paths
320    
321            obj = pool.borrowObject();
322            clear(factory, expectedMethods);
323    
324            // invalidated object should be destroyed
325            pool.invalidateObject(obj);
326            expectedMethods.add(new MethodCall("destroyObject", obj));
327            assertEquals(expectedMethods, factory.getMethodCalls());
328    
329            //// Test exception handling of invalidateObject
330            reset(pool, factory, expectedMethods);
331            obj = pool.borrowObject();
332            clear(factory, expectedMethods);
333            factory.setDestroyObjectFail(true);
334            try {
335                pool.invalidateObject(obj);
336                fail("Expecting destroy exception to propagate");
337            } catch (PrivateException ex) {
338                // Expected
339            }
340            Thread.sleep(250); // could be defered
341            removeDestroyObjectCall(factory.getMethodCalls());
342            assertEquals(expectedMethods, factory.getMethodCalls());
343        }
344    
345        public void testPOFClearUsages() throws Exception {
346            final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
347            final ObjectPool pool;
348            try {
349                pool = makeEmptyPool(factory);
350            } catch (UnsupportedOperationException uoe) {
351                return; // test not supported
352            }
353            final List expectedMethods = new ArrayList();
354    
355            /// Test correct behavior code paths
356            PoolUtils.prefill(pool, 5);
357            pool.clear();
358    
359            //// Test exception handling clear should swallow destory object failures
360            reset(pool, factory, expectedMethods);
361            factory.setDestroyObjectFail(true);
362            PoolUtils.prefill(pool, 5);
363            pool.clear();
364        }
365    
366        public void testPOFCloseUsages() throws Exception {
367            final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
368            ObjectPool pool;
369            try {
370                pool = makeEmptyPool(factory);
371            } catch (UnsupportedOperationException uoe) {
372                return; // test not supported
373            }
374            final List expectedMethods = new ArrayList();
375    
376            /// Test correct behavior code paths
377            PoolUtils.prefill(pool, 5);
378            pool.close();
379    
380    
381            //// Test exception handling close should swallow failures
382            try {
383                pool = makeEmptyPool(factory);
384            } catch (UnsupportedOperationException uoe) {
385                return; // test not supported
386            }
387            reset(pool, factory, expectedMethods);
388            factory.setDestroyObjectFail(true);
389            PoolUtils.prefill(pool, 5);
390            pool.close();
391        }
392    
393        public void testSetFactory() throws Exception {
394            ObjectPool pool;
395            try {
396                pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
397            } catch (UnsupportedOperationException uoe) {
398                return; // test not supported
399            }
400            final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
401            try {
402                pool.setFactory(factory);
403            } catch (UnsupportedOperationException uoe) {
404                return;
405            }
406        }
407    
408        public void testToString() {
409            ObjectPool pool;
410            try {
411                pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
412            } catch (UnsupportedOperationException uoe) {
413                return; // test not supported
414            }
415            pool.toString();
416        }
417    
418        static void removeDestroyObjectCall(List calls) {
419            Iterator iter = calls.iterator();
420            while (iter.hasNext()) {
421                MethodCall call = (MethodCall)iter.next();
422                if ("destroyObject".equals(call.getName())) {
423                    iter.remove();
424                }
425            }
426        }
427    
428        private static void reset(final ObjectPool pool, final MethodCallPoolableObjectFactory factory, final List expectedMethods) throws Exception {
429            pool.clear();
430            clear(factory, expectedMethods);
431            factory.reset();
432        }
433    
434        private static void clear(final MethodCallPoolableObjectFactory factory, final List expectedMethods) {
435            factory.getMethodCalls().clear();
436            expectedMethods.clear();
437        }
438    }