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    
018    package org.apache.commons.pool;
019    
020    import junit.framework.TestCase;
021    import junit.framework.AssertionFailedError;
022    import junit.framework.Test;
023    import junit.framework.TestSuite;
024    
025    import java.lang.reflect.Proxy;
026    import java.lang.reflect.InvocationHandler;
027    import java.lang.reflect.Method;
028    import java.util.List;
029    import java.util.ArrayList;
030    import java.util.Set;
031    import java.util.HashSet;
032    import java.util.TimerTask;
033    import java.util.Collection;
034    import java.util.Map;
035    import java.util.Iterator;
036    import java.util.Arrays;
037    
038    import org.apache.commons.pool.impl.GenericObjectPool;
039    import org.apache.commons.pool.impl.GenericKeyedObjectPool;
040    
041    /**
042     * Unit tests for {@link PoolUtils}.
043     *
044     * @author Sandy McArthur
045     * @version $Revision: 780290 $ $Date: 2009-05-30 11:51:37 -0400 (Sat, 30 May 2009) $
046     */
047    public class TestPoolUtils extends TestCase {
048    
049        /** Period between checks for minIdle tests. Increase this if you happen to get too many false failures. */
050        private static final int CHECK_PERIOD = 300;
051    
052        /** Times to let the minIdle check run. */
053        private static final int CHECK_COUNT = 4;
054    
055        /** Sleep time to let the minIdle tests run CHECK_COUNT times. */
056        private static final int CHECK_SLEEP_PERIOD = CHECK_PERIOD * (CHECK_COUNT - 1) + CHECK_PERIOD / 2;
057    
058        public static Test suite() {
059            return new TestSuite(TestPoolUtils.class);
060        }
061    
062        public void testJavaBeanInstantiation() {
063            new PoolUtils();
064        }
065    
066        public void testAdaptKeyedPoolableObjectFactory() throws Exception {
067            try {
068                PoolUtils.adapt((KeyedPoolableObjectFactory)null);
069                fail("PoolUtils.adapt(KeyedPoolableObjectFactory) must not allow null factory.");
070            } catch (IllegalArgumentException iae) {
071                // expected
072            }
073        }
074    
075        public void testAdaptKeyedPoolableObjectFactoryKey() throws Exception {
076            try {
077                PoolUtils.adapt((KeyedPoolableObjectFactory)null, new Object());
078                fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null factory.");
079            } catch (IllegalArgumentException iae) {
080                // expected
081            }
082            try {
083                PoolUtils.adapt((KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class, (List)null), null);
084                fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null key.");
085            } catch (IllegalArgumentException iae) {
086                // expected
087            }
088    
089            final List calledMethods = new ArrayList();
090            final KeyedPoolableObjectFactory kpof =
091                    (KeyedPoolableObjectFactory)createProxy(KeyedPoolableObjectFactory.class, calledMethods);
092    
093            final PoolableObjectFactory pof = PoolUtils.adapt(kpof);
094            final List expectedMethods = invokeEveryMethod(pof);
095            assertEquals(expectedMethods, calledMethods);
096        }
097    
098        public void testAdaptPoolableObjectFactory() throws Exception {
099            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    }