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  
18  package org.apache.commons.pool;
19  
20  import junit.framework.TestCase;
21  import junit.framework.AssertionFailedError;
22  import junit.framework.Test;
23  import junit.framework.TestSuite;
24  
25  import java.lang.reflect.Proxy;
26  import java.lang.reflect.InvocationHandler;
27  import java.lang.reflect.Method;
28  import java.util.List;
29  import java.util.ArrayList;
30  import java.util.Set;
31  import java.util.HashSet;
32  import java.util.TimerTask;
33  import java.util.Collection;
34  import java.util.Map;
35  import java.util.Iterator;
36  import java.util.Arrays;
37  
38  import org.apache.commons.pool.impl.GenericObjectPool;
39  import org.apache.commons.pool.impl.GenericKeyedObjectPool;
40  
41  /**
42   * Unit tests for {@link PoolUtils}.
43   *
44   * @author Sandy McArthur
45   * @version $Revision: 780290 $ $Date: 2009-05-30 11:51:37 -0400 (Sat, 30 May 2009) $
46   */
47  public class TestPoolUtils extends TestCase {
48  
49      /** Period between checks for minIdle tests. Increase this if you happen to get too many false failures. */
50      private static final int CHECK_PERIOD = 300;
51  
52      /** Times to let the minIdle check run. */
53      private static final int CHECK_COUNT = 4;
54  
55      /** Sleep time to let the minIdle tests run CHECK_COUNT times. */
56      private static final int CHECK_SLEEP_PERIOD = CHECK_PERIOD * (CHECK_COUNT - 1) + CHECK_PERIOD / 2;
57  
58      public static Test suite() {
59          return new TestSuite(TestPoolUtils.class);
60      }
61  
62      public void testJavaBeanInstantiation() {
63          new PoolUtils();
64      }
65  
66      public void testAdaptKeyedPoolableObjectFactory() throws Exception {
67          try {
68              PoolUtils.adapt((KeyedPoolableObjectFactory)null);
69              fail("PoolUtils.adapt(KeyedPoolableObjectFactory) must not allow null factory.");
70          } catch (IllegalArgumentException iae) {
71              // expected
72          }
73      }
74  
75      public void testAdaptKeyedPoolableObjectFactoryKey() throws Exception {
76          try {
77              PoolUtils.adapt((KeyedPoolableObjectFactory)null, new Object());
78              fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null factory.");
79          } catch (IllegalArgumentException iae) {
80              // expected
81          }
82          try {
83              PoolUtils.adapt((KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class, (List)null), null);
84              fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null key.");
85          } catch (IllegalArgumentException iae) {
86              // expected
87          }
88  
89          final List calledMethods = new ArrayList();
90          final KeyedPoolableObjectFactory kpof =
91                  (KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class, calledMethods);
92  
93          final PoolableObjectFactory pof = PoolUtils.adapt(kpof);
94          final List expectedMethods = invokeEveryMethod(pof);
95          assertEquals(expectedMethods, calledMethods);
96      }
97  
98      public void testAdaptPoolableObjectFactory() throws Exception {
99          try {
100             PoolUtils.adapt((PoolableObjectFactory)null);
101             fail("PoolUtils.adapt(PoolableObjectFactory) must not allow null factory.");
102         } catch (IllegalArgumentException iae) {
103             // expected
104         }
105 
106         final List calledMethods = new ArrayList();
107         final PoolableObjectFactory pof =
108                 (PoolableObjectFactory)createProxy(PoolableObjectFactory.class, calledMethods);
109 
110         final KeyedPoolableObjectFactory kpof = PoolUtils.adapt(pof);
111         final List expectedMethods = invokeEveryMethod(kpof);
112         assertEquals(expectedMethods, calledMethods);
113     }
114 
115     public void testAdaptKeyedObjectPool() throws Exception {
116         try {
117             PoolUtils.adapt((KeyedObjectPool)null);
118             fail("PoolUtils.adapt(KeyedObjectPool) must not allow a null pool.");
119         } catch(IllegalArgumentException iae) {
120             // expected
121         }
122     }
123 
124     public void testAdaptKeyedObjectPoolKey() throws Exception {
125         try {
126             PoolUtils.adapt((KeyedObjectPool)null, new Object());
127             fail("PoolUtils.adapt(KeyedObjectPool, key) must not allow a null pool.");
128         } catch(IllegalArgumentException iae) {
129             // expected
130         }
131         try {
132             PoolUtils.adapt((KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null), null);
133             fail("PoolUtils.adapt(KeyedObjectPool, key) must not allow a null key.");
134         } catch(IllegalArgumentException iae) {
135             // expected
136         }
137 
138         final List calledMethods = new ArrayList();
139         final KeyedObjectPool kop = (KeyedObjectPool)createProxy(KeyedObjectPool.class, calledMethods);
140 
141         final ObjectPool op = PoolUtils.adapt(kop, new Object());
142         final List expectedMethods = invokeEveryMethod(op);
143         assertEquals(expectedMethods, calledMethods);
144     }
145 
146     public void testAdaptObjectPool() throws Exception {
147         try {
148             PoolUtils.adapt((ObjectPool)null);
149             fail("PoolUtils.adapt(ObjectPool) must not allow a null pool.");
150         } catch(IllegalArgumentException iae) {
151             // expected
152         }
153 
154         final List calledMethods = new ArrayList();
155         final ObjectPool op = (ObjectPool)createProxy(ObjectPool.class, calledMethods);
156 
157         final KeyedObjectPool kop = PoolUtils.adapt(op);
158         final List expectedMethods = invokeEveryMethod(kop);
159         assertEquals(expectedMethods, calledMethods);
160     }
161 
162     public void testCheckedPoolObjectPool() throws Exception {
163         try {
164             PoolUtils.checkedPool((ObjectPool)null, Object.class);
165             fail("PoolUtils.checkedPool(ObjectPool, Class) must not allow a null pool.");
166         } catch(IllegalArgumentException iae) {
167             // expected
168         }
169         try {
170             PoolUtils.checkedPool((ObjectPool)createProxy(ObjectPool.class, (List)null), null);
171             fail("PoolUtils.checkedPool(ObjectPool, Class) must not allow a null type.");
172         } catch(IllegalArgumentException iae) {
173             // expected
174         }
175 
176         final List calledMethods = new ArrayList();
177         ObjectPool op = (ObjectPool)createProxy(ObjectPool.class, calledMethods);
178 
179         ObjectPool cop = PoolUtils.checkedPool(op, Object.class);
180         final List expectedMethods = invokeEveryMethod(cop);
181         assertEquals(expectedMethods, calledMethods);
182 
183         op = new BaseObjectPool() {
184             public Object borrowObject() throws Exception {
185                 return new Integer(0);
186             }
187             public void returnObject(Object obj) {}
188             public void invalidateObject(Object obj) {}
189         };
190         cop = PoolUtils.checkedPool(op, String.class);
191 
192         try {
193             cop.borrowObject();
194             fail("borrowObject should have failed as Integer !instanceof String.");
195         } catch (ClassCastException cce) {
196             // expected
197         }
198         try {
199             cop.returnObject(new Integer(1));
200             fail("returnObject should have failed as Integer !instanceof String.");
201         } catch (ClassCastException cce) {
202             // expected
203         }
204         try {
205             cop.invalidateObject(new Integer(2));
206             fail("invalidateObject should have failed as Integer !instanceof String.");
207         } catch (ClassCastException cce) {
208             // expected
209         }
210     }
211 
212     public void testCheckedPoolKeyedObjectPool() throws Exception {
213         try {
214             PoolUtils.checkedPool((KeyedObjectPool)null, Object.class);
215             fail("PoolUtils.checkedPool(KeyedObjectPool, Class) must not allow a null pool.");
216         } catch(IllegalArgumentException iae) {
217             // expected
218         }
219         try {
220             PoolUtils.checkedPool((KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null), null);
221             fail("PoolUtils.checkedPool(KeyedObjectPool, Class) must not allow a null type.");
222         } catch(IllegalArgumentException iae) {
223             // expected
224         }
225 
226         final List calledMethods = new ArrayList();
227         KeyedObjectPool op = (KeyedObjectPool)createProxy(KeyedObjectPool.class, calledMethods);
228 
229         KeyedObjectPool cop = PoolUtils.checkedPool(op, Object.class);
230         final List expectedMethods = invokeEveryMethod(cop);
231         assertEquals(expectedMethods, calledMethods);
232 
233 
234         op = new BaseKeyedObjectPool() {
235             public Object borrowObject(Object key) {
236                 return new Integer(0);
237             }
238 
239             public void returnObject(Object key, Object obj) {}
240 
241             public void invalidateObject(Object key, Object obj) {}
242         };
243         cop = PoolUtils.checkedPool(op, String.class);
244 
245         try {
246             cop.borrowObject(null);
247             fail("borrowObject should have failed as Integer !instanceof String.");
248         } catch (ClassCastException cce) {
249             // expected
250         }
251         try {
252             cop.returnObject(null, new Integer(1));
253             fail("returnObject should have failed as Integer !instanceof String.");
254         } catch (ClassCastException cce) {
255             // expected
256         }
257         try {
258             cop.invalidateObject(null, new Integer(2));
259             fail("invalidateObject should have failed as Integer !instanceof String.");
260         } catch (ClassCastException cce) {
261             // expected
262         }
263     }
264 
265     public void testCheckMinIdleObjectPool() throws Exception {
266         try {
267             PoolUtils.checkMinIdle(null, 1, 1);
268             fail("PoolUtils.checkMinIdle(ObjectPool,,) must not allow null pool.");
269         } catch (IllegalArgumentException iae) {
270             // expected
271         }
272         try {
273             final ObjectPool pool = (ObjectPool)createProxy(ObjectPool.class, (List)null);
274             PoolUtils.checkMinIdle(pool, -1, 1);
275             fail("PoolUtils.checkMinIdle(ObjectPool,,) must not accept negative min idle values.");
276         } catch (IllegalArgumentException iae) {
277             // expected
278         }
279 
280         final List calledMethods = new ArrayList();
281 
282         // Test that the minIdle check doesn't add too many idle objects
283         final PoolableObjectFactory pof = (PoolableObjectFactory)createProxy(PoolableObjectFactory.class, calledMethods);
284         final ObjectPool op = new GenericObjectPool(pof);
285         PoolUtils.checkMinIdle(op, 2, 100);
286         Thread.sleep(400);
287         assertEquals(2, op.getNumIdle());
288         op.close();
289         int makeObjectCount = 0;
290         final Iterator iter = calledMethods.iterator();
291         while (iter.hasNext()) {
292             final String methodName = (String)iter.next();
293             if ("makeObject".equals(methodName)) {
294                 makeObjectCount++;
295             }
296         }
297         assertEquals("makeObject should have been called two time", 2, makeObjectCount);
298 
299         // Because this isn't deterministic and you can get false failures, try more than once.
300         AssertionFailedError afe = null;
301         int triesLeft = 3;
302         do {
303             afe = null;
304             try {
305                 calledMethods.clear();
306                 final ObjectPool pool = (ObjectPool)createProxy(ObjectPool.class, calledMethods);
307                 final TimerTask task = PoolUtils.checkMinIdle(pool, 1, CHECK_PERIOD); // checks minIdle immediately
308 
309                 Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
310                 task.cancel();
311                 task.toString();
312 
313                 final List expectedMethods = new ArrayList();
314                 for (int i=0; i < CHECK_COUNT; i++) {
315                     expectedMethods.add("getNumIdle");
316                     expectedMethods.add("addObject");
317                 }
318                 expectedMethods.add("toString");
319                 assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
320             } catch (AssertionFailedError e) {
321                 afe = e;
322             }
323         } while (--triesLeft > 0 && afe != null);
324         if (afe != null) {
325             throw afe;
326         }
327     }
328 
329     public void testCheckMinIdleKeyedObjectPool() throws Exception {
330         try {
331             PoolUtils.checkMinIdle(null, new Object(), 1, 1);
332             fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not allow null pool.");
333         } catch (IllegalArgumentException iae) {
334             // expected
335         }
336         try {
337             final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
338             PoolUtils.checkMinIdle(pool, (Object)null, 1, 1);
339             fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not accept null keys.");
340         } catch (IllegalArgumentException iae) {
341             // expected
342         }
343         try {
344             final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
345             PoolUtils.checkMinIdle(pool, new Object(), -1, 1);
346             fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not accept negative min idle values.");
347         } catch (IllegalArgumentException iae) {
348             // expected
349         }
350 
351         final List calledMethods = new ArrayList();
352         final Object key = new Object();
353 
354         // Test that the minIdle check doesn't add too many idle objects
355         final KeyedPoolableObjectFactory kpof = (KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class, calledMethods);
356         final KeyedObjectPool kop = new GenericKeyedObjectPool(kpof);
357         PoolUtils.checkMinIdle(kop, key, 2, 100);
358         Thread.sleep(400);
359         assertEquals(2, kop.getNumIdle(key));
360         assertEquals(2, kop.getNumIdle());
361         kop.close();
362         int makeObjectCount = 0;
363         final Iterator iter = calledMethods.iterator();
364         while (iter.hasNext()) {
365             final String methodName = (String)iter.next();
366             if ("makeObject".equals(methodName)) {
367                 makeObjectCount++;
368             }
369         }
370         assertEquals("makeObject should have been called two time", 2, makeObjectCount);
371 
372         // Because this isn't deterministic and you can get false failures, try more than once.
373         AssertionFailedError afe = null;
374         int triesLeft = 3;
375         do {
376             afe = null;
377             try {
378                 calledMethods.clear();
379                 final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, calledMethods);
380                 final TimerTask task = PoolUtils.checkMinIdle(pool, key, 1, CHECK_PERIOD); // checks minIdle immediately
381 
382                 Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
383                 task.cancel();
384                 task.toString();
385 
386                 final List expectedMethods = new ArrayList();
387                 for (int i=0; i < CHECK_COUNT; i++) {
388                     expectedMethods.add("getNumIdle");
389                     expectedMethods.add("addObject");
390                 }
391                 expectedMethods.add("toString");
392                 assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
393             } catch (AssertionFailedError e) {
394                 afe = e;
395             }
396         } while (--triesLeft > 0 && afe != null);
397         if (afe != null) {
398             throw afe;
399         }
400     }
401 
402     public void testCheckMinIdleKeyedObjectPoolKeys() throws Exception {
403         try {
404             final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
405             PoolUtils.checkMinIdle(pool, null, 1, 1);
406             fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must not accept null keys.");
407         } catch (IllegalArgumentException iae) {
408             // expected
409         }
410 
411         // Because this isn't determinist and you can get false failures, try more than once.
412         AssertionFailedError afe = null;
413         int triesLeft = 3;
414         do {
415             afe = null;
416             try {
417                 final List calledMethods = new ArrayList();
418                 final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, calledMethods);
419                 final Collection keys = new ArrayList(2);
420                 keys.add("one");
421                 keys.add("two");
422                 final Map tasks = PoolUtils.checkMinIdle(pool, keys, 1, CHECK_PERIOD); // checks minIdle immediately
423 
424                 Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
425                 final Iterator iter = tasks.values().iterator();
426                 while (iter.hasNext()) {
427                     final TimerTask task = (TimerTask)iter.next();
428                     task.cancel();
429                 }
430 
431                 final List expectedMethods = new ArrayList();
432                 for (int i=0; i < CHECK_COUNT * keys.size(); i++) {
433                     expectedMethods.add("getNumIdle");
434                     expectedMethods.add("addObject");
435                 }
436                 assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
437             } catch (AssertionFailedError e) {
438                 afe = e;
439             }
440         } while (--triesLeft > 0 && afe != null);
441         if (afe != null) {
442             throw afe;
443         }
444     }
445 
446     public void testPrefillObjectPool() throws Exception {
447         try {
448             PoolUtils.prefill(null, 1);
449             fail("PoolUtils.prefill(ObjectPool,int) must not allow null pool.");
450         } catch (IllegalArgumentException iae) {
451             // expected
452         }
453 
454         final List calledMethods = new ArrayList();
455         final ObjectPool pool = (ObjectPool)createProxy(ObjectPool.class, calledMethods);
456 
457         PoolUtils.prefill(pool, 0);
458         final List expectedMethods = new ArrayList();
459         assertEquals(expectedMethods, calledMethods);
460 
461         calledMethods.clear();
462         PoolUtils.prefill(pool, 3);
463         for (int i=0; i < 3; i++) {
464             expectedMethods.add("addObject");
465         }
466         assertEquals(expectedMethods, calledMethods);
467     }
468 
469     public void testPrefillKeyedObjectPool() throws Exception {
470         try {
471             PoolUtils.prefill(null, new Object(), 1);
472             fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null pool.");
473         } catch (IllegalArgumentException iae) {
474             // expected
475         }
476         try {
477             final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
478             PoolUtils.prefill(pool, (Object)null, 1);
479             fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null key.");
480         } catch (IllegalArgumentException iae) {
481             // expected
482         }
483 
484         final List calledMethods = new ArrayList();
485         final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, calledMethods);
486 
487         PoolUtils.prefill(pool, new Object(), 0);
488         final List expectedMethods = new ArrayList();
489         assertEquals(expectedMethods, calledMethods);
490 
491         calledMethods.clear();
492         PoolUtils.prefill(pool, new Object(), 3);
493         for (int i=0; i < 3; i++) {
494             expectedMethods.add("addObject");
495         }
496         assertEquals(expectedMethods, calledMethods);
497     }
498 
499     public void testPrefillKeyedObjectPoolCollection() throws Exception {
500         try {
501             final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, (List)null);
502             PoolUtils.prefill(pool, null, 1);
503             fail("PoolUtils.prefill(KeyedObjectPool,Collection,int) must not accept null keys.");
504         } catch (IllegalArgumentException iae) {
505             // expected
506         }
507 
508         final List calledMethods = new ArrayList();
509         final KeyedObjectPool pool = (KeyedObjectPool)createProxy(KeyedObjectPool.class, calledMethods);
510 
511         final Set keys = new HashSet();
512         PoolUtils.prefill(pool, keys, 0);
513         final List expectedMethods = new ArrayList();
514         assertEquals(expectedMethods, calledMethods);
515 
516         calledMethods.clear();
517         keys.add(new Integer(1));
518         keys.add("two");
519         keys.add(new Double(3.1415926));
520         PoolUtils.prefill(pool, keys, 3);
521         for (int i=0; i < keys.size() * 3; i++) {
522             expectedMethods.add("addObject");
523         }
524         assertEquals(expectedMethods, calledMethods);
525     }
526 
527     public void testSynchronizedPoolObjectPool() throws Exception {
528         try {
529             PoolUtils.synchronizedPool((ObjectPool)null);
530             fail("PoolUtils.synchronizedPool(ObjectPool) must not allow a null pool.");
531         } catch(IllegalArgumentException iae) {
532             // expected
533         }
534 
535         final List calledMethods = new ArrayList();
536         final ObjectPool op = (ObjectPool)createProxy(ObjectPool.class, calledMethods);
537 
538         final ObjectPool sop = PoolUtils.synchronizedPool(op);
539         final List expectedMethods = invokeEveryMethod(sop);
540         assertEquals(expectedMethods, calledMethods);
541 
542         // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
543     }
544 
545     public void testSynchronizedPoolKeyedObjectPool() throws Exception {
546         try {
547             PoolUtils.synchronizedPool((KeyedObjectPool)null);
548             fail("PoolUtils.synchronizedPool(KeyedObjectPool) must not allow a null pool.");
549         } catch(IllegalArgumentException iae) {
550             // expected
551         }
552 
553         final List calledMethods = new ArrayList();
554         final KeyedObjectPool kop = (KeyedObjectPool)createProxy(KeyedObjectPool.class, calledMethods);
555 
556         final KeyedObjectPool skop = PoolUtils.synchronizedPool(kop);
557         final List expectedMethods = invokeEveryMethod(skop);
558         assertEquals(expectedMethods, calledMethods);
559 
560         // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
561     }
562 
563     public void testSynchronizedPoolableFactoryPoolableObjectFactory() throws Exception {
564         try {
565             PoolUtils.synchronizedPoolableFactory((PoolableObjectFactory)null);
566             fail("PoolUtils.synchronizedPoolableFactory(PoolableObjectFactory) must not allow a null factory.");
567         } catch(IllegalArgumentException iae) {
568             // expected
569         }
570 
571         final List calledMethods = new ArrayList();
572         final PoolableObjectFactory pof =
573                 (PoolableObjectFactory)createProxy(PoolableObjectFactory.class, calledMethods);
574 
575         final PoolableObjectFactory spof = PoolUtils.synchronizedPoolableFactory(pof);
576         final List expectedMethods = invokeEveryMethod(spof);
577         assertEquals(expectedMethods, calledMethods);
578 
579         // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
580     }
581 
582     public void testSynchronizedPoolableFactoryKeyedPoolableObjectFactory() throws Exception {
583         try {
584             PoolUtils.synchronizedPoolableFactory((KeyedPoolableObjectFactory)null);
585             fail("PoolUtils.synchronizedPoolableFactory(KeyedPoolableObjectFactory) must not allow a null factory.");
586         } catch(IllegalArgumentException iae) {
587             // expected
588         }
589 
590         final List calledMethods = new ArrayList();
591         final KeyedPoolableObjectFactory kpof =
592                 (KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class, calledMethods);
593 
594         final KeyedPoolableObjectFactory skpof = PoolUtils.synchronizedPoolableFactory(kpof);
595         final List expectedMethods = invokeEveryMethod(skpof);
596         assertEquals(expectedMethods, calledMethods);
597 
598         // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
599     }
600 
601     public void testErodingPoolObjectPool() throws Exception {
602         try {
603             PoolUtils.erodingPool((ObjectPool)null);
604             fail("PoolUtils.erodingPool(ObjectPool) must not allow a null pool.");
605         } catch(IllegalArgumentException iae) {
606             // expected
607         }
608 
609         try {
610             PoolUtils.erodingPool((ObjectPool)null, 1f);
611             fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a null pool.");
612         } catch(IllegalArgumentException iae) {
613             // expected
614         }
615 
616         try {
617             PoolUtils.erodingPool((ObjectPool)null, 0);
618             fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
619         } catch(IllegalArgumentException iae) {
620             // expected
621         }
622 
623         final List calledMethods = new ArrayList();
624         final InvocationHandler handler = new MethodCallLogger(calledMethods) {
625             public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
626                 Object o = super.invoke(proxy, method, args);
627                 if (o instanceof Integer) {
628                     // so getNumActive/getNumIdle are not zero.
629                     o = new Integer(1);
630                 }
631                 return o;
632             }
633         };
634 
635         // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
636         float factor = 0.01f; // about ~9 seconds until first discard
637         final ObjectPool pool = PoolUtils.erodingPool((ObjectPool)createProxy(ObjectPool.class, handler), factor);
638 
639         final List expectedMethods = new ArrayList();
640         assertEquals(expectedMethods, calledMethods);
641 
642         Object o = pool.borrowObject();
643         expectedMethods.add("borrowObject");
644 
645         assertEquals(expectedMethods, calledMethods);
646 
647         pool.returnObject(o);
648         expectedMethods.add("returnObject");
649         assertEquals(expectedMethods, calledMethods);
650 
651         for (int i=0; i < 5; i ++) {
652             o = pool.borrowObject();
653             expectedMethods.add("borrowObject");
654 
655             Thread.sleep(50);
656 
657             pool.returnObject(o);
658             expectedMethods.add("returnObject");
659 
660             assertEquals(expectedMethods, calledMethods);
661 
662             expectedMethods.clear();
663             calledMethods.clear();
664         }
665 
666         Thread.sleep(10000); // 10 seconds
667 
668         
669         o = pool.borrowObject();
670         expectedMethods.add("borrowObject");
671         pool.returnObject(o);
672         expectedMethods.add("getNumIdle");
673         expectedMethods.add("invalidateObject");
674         assertEquals(expectedMethods, calledMethods);
675     }
676 
677     public void testErodingPoolKeyedObjectPool() throws Exception {
678         try {
679             PoolUtils.erodingPool((KeyedObjectPool)null);
680             fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool.");
681         } catch(IllegalArgumentException iae) {
682             // expected
683         }
684 
685         try {
686             PoolUtils.erodingPool((KeyedObjectPool)null, 1f);
687             fail("PoolUtils.erodingPool(KeyedObjectPool, float) must not allow a null pool.");
688         } catch(IllegalArgumentException iae) {
689             // expected
690         }
691 
692         try {
693             PoolUtils.erodingPool((KeyedObjectPool)null, 0);
694             fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
695         } catch(IllegalArgumentException iae) {
696             // expected
697         }
698 
699         try {
700             PoolUtils.erodingPool((KeyedObjectPool)null, 1f, true);
701             fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool.");
702         } catch(IllegalArgumentException iae) {
703             // expected
704         }
705 
706         try {
707             PoolUtils.erodingPool((KeyedObjectPool)null, 0, false);
708             fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not allow a non-positive factor.");
709         } catch(IllegalArgumentException iae) {
710             // expected
711         }
712 
713         final List calledMethods = new ArrayList();
714         final InvocationHandler handler = new MethodCallLogger(calledMethods) {
715             public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
716                 Object o = super.invoke(proxy, method, args);
717                 if (o instanceof Integer) {
718                     // so getNumActive/getNumIdle are not zero.
719                     o = new Integer(1);
720                 }
721                 return o;
722             }
723         };
724 
725         // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
726         float factor = 0.01f; // about ~9 seconds until first discard
727         final KeyedObjectPool pool = PoolUtils.erodingPool((KeyedObjectPool)createProxy(KeyedObjectPool.class, handler), factor);
728 
729         final List expectedMethods = new ArrayList();
730         assertEquals(expectedMethods, calledMethods);
731 
732         final Object key = "key";
733 
734         Object o = pool.borrowObject(key);
735         expectedMethods.add("borrowObject");
736 
737         assertEquals(expectedMethods, calledMethods);
738 
739         pool.returnObject(key, o);
740         expectedMethods.add("returnObject");
741         assertEquals(expectedMethods, calledMethods);
742 
743         for (int i=0; i < 5; i ++) {
744             o = pool.borrowObject(key);
745             expectedMethods.add("borrowObject");
746 
747             Thread.sleep(50);
748 
749             pool.returnObject(key, o);
750             expectedMethods.add("returnObject");
751 
752             assertEquals(expectedMethods, calledMethods);
753 
754             expectedMethods.clear();
755             calledMethods.clear();
756         }
757 
758         Thread.sleep(10000); // 10 seconds
759 
760 
761         o = pool.borrowObject(key);
762         expectedMethods.add("borrowObject");
763         pool.returnObject(key, o);
764         expectedMethods.add("getNumIdle");
765         expectedMethods.add("invalidateObject");
766         assertEquals(expectedMethods, calledMethods);
767     }
768     
769     public void testErodingPerKeyKeyedObjectPool() throws Exception {
770         try {
771             PoolUtils.erodingPool((KeyedObjectPool)null, 1, true);
772             fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool.");
773         } catch(IllegalArgumentException iae) {
774             // expected
775         }
776 
777         try {
778             PoolUtils.erodingPool((KeyedObjectPool)null, 0, true);
779             fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
780         } catch(IllegalArgumentException iae) {
781             // expected
782         }
783 
784         try {
785             PoolUtils.erodingPool((KeyedObjectPool)null, 1f, true);
786             fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool.");
787         } catch(IllegalArgumentException iae) {
788             // expected
789         }
790 
791         final List calledMethods = new ArrayList();
792         final InvocationHandler handler = new MethodCallLogger(calledMethods) {
793             public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
794                 Object o = super.invoke(proxy, method, args);
795                 if (o instanceof Integer) {
796                     // so getNumActive/getNumIdle are not zero.
797                     o = new Integer(1);
798                 }
799                 return o;
800             }
801         };
802 
803         // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
804         float factor = 0.01f; // about ~9 seconds until first discard
805         final KeyedObjectPool pool = PoolUtils.erodingPool((KeyedObjectPool)createProxy(KeyedObjectPool.class, handler), factor, true);
806 
807         final List expectedMethods = new ArrayList();
808         assertEquals(expectedMethods, calledMethods);
809 
810         final Object key = "key";
811 
812         Object o = pool.borrowObject(key);
813         expectedMethods.add("borrowObject");
814 
815         assertEquals(expectedMethods, calledMethods);
816 
817         pool.returnObject(key, o);
818         expectedMethods.add("returnObject");
819         assertEquals(expectedMethods, calledMethods);
820 
821         for (int i=0; i < 5; i ++) {
822             o = pool.borrowObject(key);
823             expectedMethods.add("borrowObject");
824 
825             Thread.sleep(50);
826 
827             pool.returnObject(key, o);
828             expectedMethods.add("returnObject");
829 
830             assertEquals(expectedMethods, calledMethods);
831 
832             expectedMethods.clear();
833             calledMethods.clear();
834         }
835 
836         Thread.sleep(10000); // 10 seconds
837 
838 
839         o = pool.borrowObject(key);
840         expectedMethods.add("borrowObject");
841         pool.returnObject(key, o);
842         expectedMethods.add("getNumIdle");
843         expectedMethods.add("invalidateObject");
844         assertEquals(expectedMethods, calledMethods);
845     }
846 
847     private static List invokeEveryMethod(ObjectPool op) throws Exception {
848         op.addObject();
849         op.borrowObject();
850         op.clear();
851         op.close();
852         op.getNumActive();
853         op.getNumIdle();
854         op.invalidateObject(new Object());
855         op.returnObject(new Object());
856         op.setFactory((PoolableObjectFactory)createProxy(PoolableObjectFactory.class, (List)null));
857         op.toString();
858 
859         final List expectedMethods = Arrays.asList(new String[] {
860                 "addObject", "borrowObject", "clear", "close",
861                 "getNumActive", "getNumIdle", "invalidateObject",
862                 "returnObject", "setFactory", "toString"
863         });
864         return expectedMethods;
865     }
866 
867     private static List invokeEveryMethod(KeyedObjectPool kop) throws Exception {
868         kop.addObject(null);
869         kop.borrowObject(null);
870         kop.clear();
871         kop.clear(null);
872         kop.close();
873         kop.getNumActive();
874         kop.getNumActive(null);
875         kop.getNumIdle();
876         kop.getNumIdle(null);
877         kop.invalidateObject(null, new Object());
878         kop.returnObject(null, new Object());
879         kop.setFactory((KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class, (List)null));
880         kop.toString();
881 
882         final List expectedMethods = Arrays.asList(new String[] {
883                 "addObject", "borrowObject", "clear", "clear", "close",
884                 "getNumActive", "getNumActive", "getNumIdle", "getNumIdle", "invalidateObject",
885                 "returnObject", "setFactory", "toString"
886         });
887         return expectedMethods;
888     }
889 
890     private static List invokeEveryMethod(PoolableObjectFactory pof) throws Exception {
891         pof.activateObject(null);
892         pof.destroyObject(null);
893         pof.makeObject();
894         pof.passivateObject(null);
895         pof.validateObject(null);
896         pof.toString();
897 
898         final List expectedMethods = Arrays.asList(new String[] {
899                 "activateObject", "destroyObject", "makeObject",
900                 "passivateObject", "validateObject", "toString",
901         });
902         return expectedMethods;
903     }
904 
905     private static List invokeEveryMethod(KeyedPoolableObjectFactory kpof) throws Exception {
906         kpof.activateObject(null, null);
907         kpof.destroyObject(null, null);
908         kpof.makeObject(null);
909         kpof.passivateObject(null, null);
910         kpof.validateObject(null, null);
911         kpof.toString();
912 
913         final List expectedMethods = Arrays.asList(new String[] {
914                 "activateObject", "destroyObject", "makeObject",
915                 "passivateObject", "validateObject", "toString",
916         });
917         return expectedMethods;
918     }
919 
920     private static Object createProxy(final Class clazz, final List logger) {
921         return createProxy(clazz, new MethodCallLogger(logger));
922     }
923 
924     private static Object createProxy(final Class clazz, final InvocationHandler handler) {
925         return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, handler);
926     }
927 
928     private static class MethodCallLogger implements InvocationHandler {
929         private final List calledMethods;
930 
931         MethodCallLogger(final List calledMethods) {
932             this.calledMethods = calledMethods;
933         }
934 
935         public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
936             calledMethods.add(method.getName());
937             if (boolean.class.equals(method.getReturnType())) {
938                 return Boolean.FALSE;
939             } else if (int.class.equals(method.getReturnType())) {
940                 return new Integer(0);
941             } else if (long.class.equals(method.getReturnType())) {
942                 return new Long(0);
943             } else if (Object.class.equals(method.getReturnType())) {
944                 return new Object();
945             } else {
946                 return null;
947             }
948         }
949     }
950 }