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