1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.pool;
18  
19  import junit.framework.TestCase;
20  import org.apache.commons.pool.impl.GenericObjectPool;
21  import org.apache.commons.pool.impl.StackObjectPool;
22  import org.apache.commons.pool.impl.SoftReferenceObjectPool;
23  
24  import java.util.List;
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.NoSuchElementException;
28  
29  /***
30   * Abstract {@link TestCase} for {@link ObjectPool} implementations.
31   * @author Rodney Waldhoff
32   * @author Sandy McArthur
33   * @version $Revision: 607137 $ $Date: 2007-12-27 16:06:49 -0700 (Thu, 27 Dec 2007) $
34   */
35  public abstract class TestObjectPool extends TestCase {
36      public TestObjectPool(String testName) {
37          super(testName);
38      }
39  
40      /***
41       * Create an <code>ObjectPool</code> with the specified factory.
42       * The pool should be in a default configuration and conform to the expected
43       * behaviors described in {@link ObjectPool}.
44       * Generally speaking there should be no limits on the various object counts.
45       * @throws UnsupportedOperationException if the pool being tested does not follow pool contracts.
46       */
47      protected abstract ObjectPool makeEmptyPool(PoolableObjectFactory factory) throws UnsupportedOperationException;
48  
49      public void testClosedPoolBehavior() throws Exception {
50          final ObjectPool pool;
51          try {
52              pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
53          } catch (UnsupportedOperationException uoe) {
54              return; // test not supported
55          }
56          Object o1 = pool.borrowObject();
57          Object o2 = pool.borrowObject();
58  
59          pool.close();
60  
61          try {
62              pool.addObject();
63              fail("A closed pool must throw an IllegalStateException when addObject is called.");
64          } catch (IllegalStateException ise) {
65              // expected
66          }
67  
68          try {
69              pool.borrowObject();
70              fail("A closed pool must throw an IllegalStateException when borrowObject is called.");
71          } catch (IllegalStateException ise) {
72              // expected
73          }
74  
75          // The following should not throw exceptions just because the pool is closed.
76          if (pool.getNumIdle() >= 0) {
77              assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle());
78          }
79          if (pool.getNumActive() >= 0) {
80              assertEquals("A closed pool should still keep count of active objects.", 2, pool.getNumActive());
81          }
82          pool.returnObject(o1);
83          if (pool.getNumIdle() >= 0) {
84              assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle());
85          }
86          if (pool.getNumActive() >= 0) {
87              assertEquals("A closed pool should still keep count of active objects.", 1, pool.getNumActive());
88          }
89          pool.invalidateObject(o2);
90          if (pool.getNumIdle() >= 0) {
91              assertEquals("invalidateObject must not add items back into the idle object pool.", 0, pool.getNumIdle());
92          }
93          if (pool.getNumActive() >= 0) {
94              assertEquals("A closed pool should still keep count of active objects.", 0, pool.getNumActive());
95          }
96          pool.clear();
97          pool.close();
98      }
99  
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         int idleCount;
258 
259         /// Test correct behavior code paths
260         obj = pool.borrowObject();
261         clear(factory, expectedMethods);
262 
263         // returned object should be passivated
264         pool.returnObject(obj);
265         // StackObjectPool, SoftReferenceObjectPool also validate on return
266         if (pool instanceof StackObjectPool || 
267                 pool instanceof SoftReferenceObjectPool) {
268             expectedMethods.add(new MethodCall(
269                     "validateObject", obj).returned(Boolean.TRUE));
270         }
271         expectedMethods.add(new MethodCall("passivateObject", obj));
272         assertEquals(expectedMethods, factory.getMethodCalls());
273 
274         //// Test exception handling of returnObject
275         reset(pool, factory, expectedMethods);
276         pool.addObject();
277         pool.addObject();
278         pool.addObject();
279         assertEquals(3, pool.getNumIdle());
280         // passivateObject should swallow exceptions and not add the object to the pool
281         obj = pool.borrowObject();
282         Object obj2 = pool.borrowObject();
283         assertEquals(1, pool.getNumIdle());
284         assertEquals(2, pool.getNumActive());
285         clear(factory, expectedMethods);
286         factory.setPassivateObjectFail(true);
287         pool.returnObject(obj);
288         // StackObjectPool, SoftReferenceObjectPool also validate on return
289         if (pool instanceof StackObjectPool || 
290                 pool instanceof SoftReferenceObjectPool) {
291             expectedMethods.add(new MethodCall(
292                     "validateObject", obj).returned(Boolean.TRUE));
293         }
294         expectedMethods.add(new MethodCall("passivateObject", obj));
295         removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
296         assertEquals(expectedMethods, factory.getMethodCalls());
297         assertEquals(1, pool.getNumIdle());   // Not returned
298         assertEquals(1, pool.getNumActive()); // But not in active count
299 
300         // destroyObject should swallow exceptions too
301         reset(pool, factory, expectedMethods);
302         obj = pool.borrowObject();
303         clear(factory, expectedMethods);
304         factory.setPassivateObjectFail(true);
305         factory.setDestroyObjectFail(true);
306         pool.returnObject(obj);
307     }
308 
309     public void testPOFInvalidateObjectUsages() throws Exception {
310         final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
311         final ObjectPool pool;
312         try {
313             pool = makeEmptyPool(factory);
314         } catch (UnsupportedOperationException uoe) {
315             return; // test not supported
316         }
317         final List expectedMethods = new ArrayList();
318         Object obj;
319 
320         /// Test correct behavior code paths
321 
322         obj = pool.borrowObject();
323         clear(factory, expectedMethods);
324 
325         // invalidated object should be destroyed
326         pool.invalidateObject(obj);
327         expectedMethods.add(new MethodCall("destroyObject", obj));
328         assertEquals(expectedMethods, factory.getMethodCalls());
329 
330         //// Test exception handling of invalidateObject
331         reset(pool, factory, expectedMethods);
332         obj = pool.borrowObject();
333         clear(factory, expectedMethods);
334         factory.setDestroyObjectFail(true);
335         try {
336             pool.invalidateObject(obj);
337             fail("Expecting destroy exception to propagate");
338         } catch (PrivateException ex) {
339             // Expected
340         }
341         Thread.sleep(250); // could be defered
342         removeDestroyObjectCall(factory.getMethodCalls());
343         assertEquals(expectedMethods, factory.getMethodCalls());
344     }
345 
346     public void testPOFClearUsages() throws Exception {
347         final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
348         final ObjectPool pool;
349         try {
350             pool = makeEmptyPool(factory);
351         } catch (UnsupportedOperationException uoe) {
352             return; // test not supported
353         }
354         final List expectedMethods = new ArrayList();
355 
356         /// Test correct behavior code paths
357         PoolUtils.prefill(pool, 5);
358         pool.clear();
359 
360         //// Test exception handling clear should swallow destory object failures
361         reset(pool, factory, expectedMethods);
362         factory.setDestroyObjectFail(true);
363         PoolUtils.prefill(pool, 5);
364         pool.clear();
365     }
366 
367     public void testPOFCloseUsages() throws Exception {
368         final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
369         ObjectPool pool;
370         try {
371             pool = makeEmptyPool(factory);
372         } catch (UnsupportedOperationException uoe) {
373             return; // test not supported
374         }
375         final List expectedMethods = new ArrayList();
376 
377         /// Test correct behavior code paths
378         PoolUtils.prefill(pool, 5);
379         pool.close();
380 
381 
382         //// Test exception handling close should swallow failures
383         try {
384             pool = makeEmptyPool(factory);
385         } catch (UnsupportedOperationException uoe) {
386             return; // test not supported
387         }
388         reset(pool, factory, expectedMethods);
389         factory.setDestroyObjectFail(true);
390         PoolUtils.prefill(pool, 5);
391         pool.close();
392     }
393 
394     public void testSetFactory() throws Exception {
395         ObjectPool pool;
396         try {
397             pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
398         } catch (UnsupportedOperationException uoe) {
399             return; // test not supported
400         }
401         final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
402         try {
403             pool.setFactory(factory);
404         } catch (UnsupportedOperationException uoe) {
405             return;
406         }
407     }
408 
409     public void testToString() {
410         ObjectPool pool;
411         try {
412             pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
413         } catch (UnsupportedOperationException uoe) {
414             return; // test not supported
415         }
416         pool.toString();
417     }
418 
419     static void removeDestroyObjectCall(List calls) {
420         Iterator iter = calls.iterator();
421         while (iter.hasNext()) {
422             MethodCall call = (MethodCall)iter.next();
423             if ("destroyObject".equals(call.getName())) {
424                 iter.remove();
425             }
426         }
427     }
428 
429     private static void reset(final ObjectPool pool, final MethodCallPoolableObjectFactory factory, final List expectedMethods) throws Exception {
430         pool.clear();
431         clear(factory, expectedMethods);
432         factory.reset();
433     }
434 
435     private static void clear(final MethodCallPoolableObjectFactory factory, final List expectedMethods) {
436         factory.getMethodCalls().clear();
437         expectedMethods.clear();
438     }
439 }