View Javadoc

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: 775703 $ $Date: 2009-05-17 09:39:51 -0700 (Sun, 17 May 2009) $
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 
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 }