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.impl;
019    
020    import junit.framework.Test;
021    import junit.framework.TestSuite;
022    import org.apache.commons.pool.BasePoolableObjectFactory;
023    import org.apache.commons.pool.ObjectPool;
024    import org.apache.commons.pool.PoolableObjectFactory;
025    import org.apache.commons.pool.PoolUtils;
026    import org.apache.commons.pool.TestBaseObjectPool;
027    import org.apache.commons.pool.VisitTracker;
028    import org.apache.commons.pool.VisitTrackerFactory;
029    
030    import java.util.NoSuchElementException;
031    import java.util.Random;
032    
033    /**
034     * @author Rodney Waldhoff
035     * @author Dirk Verbeeck
036     * @author Sandy McArthur
037     * @version $Revision: 791860 $ $Date: 2009-07-07 11:10:30 -0400 (Tue, 07 Jul 2009) $
038     */
039    public class TestGenericObjectPool extends TestBaseObjectPool {
040        public TestGenericObjectPool(String testName) {
041            super(testName);
042        }
043    
044        public static Test suite() {
045            return new TestSuite(TestGenericObjectPool.class);
046        }
047    
048        protected ObjectPool makeEmptyPool(int mincap) {
049           GenericObjectPool pool = new GenericObjectPool(new SimpleFactory());
050           pool.setMaxActive(mincap);
051           pool.setMaxIdle(mincap);
052           return pool;
053        }
054    
055        protected ObjectPool makeEmptyPool(final PoolableObjectFactory factory) {
056            return new GenericObjectPool(factory);
057        }
058    
059        protected Object getNthObject(int n) {
060            return String.valueOf(n);
061        }
062    
063        public void setUp() throws Exception {
064            super.setUp();
065            pool = new GenericObjectPool(new SimpleFactory());
066        }
067    
068        public void tearDown() throws Exception {
069            super.tearDown();
070            pool.clear();
071            pool.close();
072            pool = null;
073        }
074    
075        public void testWhenExhaustedGrow() throws Exception {
076            pool.setMaxActive(1);
077            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
078            Object obj1 = pool.borrowObject();
079            assertNotNull(obj1);
080            Object obj2 = pool.borrowObject();
081            assertNotNull(obj2);
082            pool.returnObject(obj2);
083            pool.returnObject(obj1);
084            pool.close();
085        }
086    
087        public void testWhenExhaustedFail() throws Exception {
088            pool.setMaxActive(1);
089            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
090            Object obj1 = pool.borrowObject();
091            assertNotNull(obj1);
092            try {
093                pool.borrowObject();
094                fail("Expected NoSuchElementException");
095            } catch(NoSuchElementException e) {
096                // expected
097            }
098            pool.returnObject(obj1);
099            assertEquals(1, pool.getNumIdle());
100            pool.close();
101        }
102    
103        public void testWhenExhaustedBlock() throws Exception {
104            pool.setMaxActive(1);
105            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
106            pool.setMaxWait(10L);
107            Object obj1 = pool.borrowObject();
108            assertNotNull(obj1);
109            try {
110                pool.borrowObject();
111                fail("Expected NoSuchElementException");
112            } catch(NoSuchElementException e) {
113                // expected
114            }
115            pool.returnObject(obj1);
116            pool.close();
117        }
118    
119        public void testEvictWhileEmpty() throws Exception {
120            pool.evict();
121            pool.evict();
122            pool.close();
123        }
124        
125        /**
126         * Tests addObject contention between ensureMinIdle triggered by
127         * the Evictor with minIdle > 0 and borrowObject. 
128         */
129        public void testEvictAddObjects() throws Exception {
130            SimpleFactory factory = new SimpleFactory();
131            factory.setMakeLatency(300);
132            factory.setMaxActive(2);
133            GenericObjectPool pool = new GenericObjectPool(factory);
134            pool.setMaxActive(2);
135            pool.setMinIdle(1);
136            pool.borrowObject(); // numActive = 1, numIdle = 0
137            // Create a test thread that will run once and try a borrow after
138            // 150ms fixed delay
139            TestThread borrower = new TestThread(pool, 1, 150, false);
140            Thread borrowerThread = new Thread(borrower);
141            // Set evictor to run in 100 ms - will create idle instance
142            pool.setTimeBetweenEvictionRunsMillis(100);
143            borrowerThread.start();  // Off to the races
144            borrowerThread.join();
145            assertTrue(!borrower.failed());
146            pool.close();
147        }
148    
149        public void testEvictLIFO() throws Exception {
150            checkEvict(true);   
151        }
152        
153        public void testEvictFIFO() throws Exception {
154            checkEvict(false);
155        }
156        
157        public void checkEvict(boolean lifo) throws Exception {
158            // yea this is hairy but it tests all the code paths in GOP.evict()
159            final SimpleFactory factory = new SimpleFactory();
160            final GenericObjectPool pool = new GenericObjectPool(factory);
161            pool.setSoftMinEvictableIdleTimeMillis(10);
162            pool.setMinIdle(2);
163            pool.setTestWhileIdle(true);
164            pool.setLifo(lifo);
165            PoolUtils.prefill(pool, 5);
166            pool.evict();
167            factory.setEvenValid(false);
168            factory.setOddValid(false);
169            factory.setThrowExceptionOnActivate(true);
170            pool.evict();
171            PoolUtils.prefill(pool, 5);
172            factory.setThrowExceptionOnActivate(false);
173            factory.setThrowExceptionOnPassivate(true);
174            pool.evict();
175            factory.setThrowExceptionOnPassivate(false);
176            factory.setEvenValid(true);
177            factory.setOddValid(true);
178            Thread.sleep(125);
179            pool.evict();
180            assertEquals(2, pool.getNumIdle());
181        }
182        
183        /**
184         * Test to make sure evictor visits least recently used objects first,
185         * regardless of FIFO/LIFO 
186         * 
187         * JIRA: POOL-86
188         */ 
189        public void testEvictionOrder() throws Exception {
190            checkEvictionOrder(false);
191            checkEvictionOrder(true);
192        }
193        
194        private void checkEvictionOrder(boolean lifo) throws Exception {
195            SimpleFactory factory = new SimpleFactory();
196            GenericObjectPool pool = new GenericObjectPool(factory);
197            pool.setNumTestsPerEvictionRun(2);
198            pool.setMinEvictableIdleTimeMillis(100);
199            pool.setLifo(lifo);
200            for (int i = 0; i < 5; i++) {
201                pool.addObject();
202                Thread.sleep(100);
203            }
204            // Order, oldest to youngest, is "0", "1", ...,"4"
205            pool.evict(); // Should evict "0" and "1"
206            Object obj = pool.borrowObject();
207            assertTrue("oldest not evicted", !obj.equals("0"));
208            assertTrue("second oldest not evicted", !obj.equals("1"));
209            // 2 should be next out for FIFO, 4 for LIFO
210            assertEquals("Wrong instance returned", lifo ? "4" : "2" , obj); 
211            
212            // Two eviction runs in sequence
213            factory = new SimpleFactory();
214            pool = new GenericObjectPool(factory);
215            pool.setNumTestsPerEvictionRun(2);
216            pool.setMinEvictableIdleTimeMillis(100);
217            pool.setLifo(lifo);
218            for (int i = 0; i < 5; i++) {
219                pool.addObject();
220                Thread.sleep(100);
221            }
222            pool.evict(); // Should evict "0" and "1"
223            pool.evict(); // Should evict "2" and "3"
224            obj = pool.borrowObject();
225            assertEquals("Wrong instance remaining in pool", "4", obj);     
226        }
227        
228        /**
229         * Verifies that the evictor visits objects in expected order
230         * and frequency. 
231         */
232        public void testEvictorVisiting() throws Exception {
233            checkEvictorVisiting(true);
234            checkEvictorVisiting(false);  
235        }
236        
237        private void checkEvictorVisiting(boolean lifo) throws Exception {
238            VisitTrackerFactory factory = new VisitTrackerFactory();
239            GenericObjectPool pool = new GenericObjectPool(factory);
240            pool.setNumTestsPerEvictionRun(2);
241            pool.setMinEvictableIdleTimeMillis(-1);
242            pool.setTestWhileIdle(true);
243            pool.setLifo(lifo);
244            pool.setTestOnReturn(false);
245            pool.setTestOnBorrow(false);
246            for (int i = 0; i < 8; i++) {
247                pool.addObject();
248            }
249            pool.evict(); // Visit oldest 2 - 0 and 1
250            Object obj = pool.borrowObject();
251            pool.returnObject(obj);
252            obj = pool.borrowObject();
253            pool.returnObject(obj);
254            //  borrow, return, borrow, return 
255            //  FIFO will move 0 and 1 to end
256            //  LIFO, 7 out, then in, then out, then in
257            pool.evict();  // Should visit 2 and 3 in either case
258            for (int i = 0; i < 8; i++) {
259                VisitTracker tracker = (VisitTracker) pool.borrowObject();    
260                if (tracker.getId() >= 4) {
261                    assertEquals("Unexpected instance visited " + tracker.getId(),
262                            0, tracker.getValidateCount());
263                } else {
264                    assertEquals("Instance " +  tracker.getId() + 
265                            " visited wrong number of times.",
266                            1, tracker.getValidateCount());
267                }
268            } 
269    
270            factory = new VisitTrackerFactory();
271            pool = new GenericObjectPool(factory);
272            pool.setNumTestsPerEvictionRun(3);
273            pool.setMinEvictableIdleTimeMillis(-1);
274            pool.setTestWhileIdle(true);
275            pool.setLifo(lifo);
276            pool.setTestOnReturn(false);
277            pool.setTestOnBorrow(false);
278            for (int i = 0; i < 8; i++) {
279                pool.addObject();
280            }
281            pool.evict(); // 0, 1, 2
282            pool.evict(); // 3, 4, 5
283            obj = pool.borrowObject();
284            pool.returnObject(obj);
285            obj = pool.borrowObject();
286            pool.returnObject(obj);
287            obj = pool.borrowObject();
288            pool.returnObject(obj);
289            // borrow, return, borrow, return 
290            //  FIFO 3,4,5,6,7,0,1,2
291            //  LIFO 7,6,5,4,3,2,1,0
292            // In either case, pointer should be at 6
293            pool.evict();
294            // Should hit 6,7,0 - 0 for second time
295            for (int i = 0; i < 8; i++) {
296                VisitTracker tracker = (VisitTracker) pool.borrowObject();
297                if (tracker.getId() != 0) {
298                    assertEquals("Instance " +  tracker.getId() + 
299                            " visited wrong number of times.",
300                            1, tracker.getValidateCount());
301                } else {
302                    assertEquals("Instance " +  tracker.getId() + 
303                            " visited wrong number of times.",
304                            2, tracker.getValidateCount());
305                }
306            } 
307            // Randomly generate a pools with random numTests
308            // and make sure evictor cycles through elements appropriately
309            int[] smallPrimes = {2, 3, 5, 7};
310            Random random = new Random();
311            random.setSeed(System.currentTimeMillis());
312            for (int i = 0; i < 4; i++) {
313                pool.setNumTestsPerEvictionRun(smallPrimes[i]);
314                for (int j = 0; j < 5; j++) {
315                    pool = new GenericObjectPool(factory);
316                    pool.setNumTestsPerEvictionRun(3);
317                    pool.setMinEvictableIdleTimeMillis(-1);
318                    pool.setTestWhileIdle(true);
319                    pool.setLifo(lifo);
320                    pool.setTestOnReturn(false);
321                    pool.setTestOnBorrow(false);
322                    pool.setMaxIdle(-1);
323                    int instanceCount = 10 + random.nextInt(20);
324                    pool.setMaxActive(instanceCount);
325                    for (int k = 0; k < instanceCount; k++) {
326                        pool.addObject();
327                    }
328    
329                    // Execute a random number of evictor runs
330                    int runs = 10 + random.nextInt(50);
331                    for (int k = 0; k < runs; k++) {
332                        pool.evict();
333                    }
334    
335                    // Number of times evictor should have cycled through the pool
336                    int cycleCount = (runs * pool.getNumTestsPerEvictionRun())
337                    / instanceCount;
338    
339                    // Look at elements and make sure they are visited cycleCount
340                    // or cycleCount + 1 times
341                    VisitTracker tracker = null;
342                    int visitCount = 0;
343                    for (int k = 0; k < instanceCount; k++) {
344                        tracker = (VisitTracker) pool.borrowObject(); 
345                        assertTrue(pool.getNumActive() <= pool.getMaxActive());
346                        visitCount = tracker.getValidateCount();                  
347                        assertTrue(visitCount >= cycleCount && 
348                                visitCount <= cycleCount + 1);
349                    }
350                }
351            }
352        }
353    
354        public void testExceptionOnPassivateDuringReturn() throws Exception {
355            SimpleFactory factory = new SimpleFactory();        
356            GenericObjectPool pool = new GenericObjectPool(factory);
357            Object obj = pool.borrowObject();
358            factory.setThrowExceptionOnPassivate(true);
359            pool.returnObject(obj);
360            assertEquals(0,pool.getNumIdle());
361            pool.close();
362        }
363        
364        public void testExceptionOnDestroyDuringBorrow() throws Exception {
365            SimpleFactory factory = new SimpleFactory(); 
366            factory.setThrowExceptionOnDestroy(true);
367            GenericObjectPool pool = new GenericObjectPool(factory);
368            pool.setTestOnBorrow(true);
369            pool.borrowObject();
370            factory.setValid(false); // Make validation fail on next borrow attempt
371            try {
372                pool.borrowObject();
373                fail("Expecting NoSuchElementException");
374            } catch (NoSuchElementException ex) {
375                // expected
376            }
377            assertEquals(1, pool.getNumActive());
378            assertEquals(0, pool.getNumIdle());
379        }
380        
381        public void testExceptionOnDestroyDuringReturn() throws Exception {
382            SimpleFactory factory = new SimpleFactory(); 
383            factory.setThrowExceptionOnDestroy(true);
384            GenericObjectPool pool = new GenericObjectPool(factory);
385            pool.setTestOnReturn(true);
386            Object obj1 = pool.borrowObject();
387            pool.borrowObject();
388            factory.setValid(false); // Make validation fail
389            pool.returnObject(obj1);
390            assertEquals(1, pool.getNumActive());
391            assertEquals(0, pool.getNumIdle());
392        }
393        
394        public void testExceptionOnActivateDuringBorrow() throws Exception {
395            SimpleFactory factory = new SimpleFactory(); 
396            GenericObjectPool pool = new GenericObjectPool(factory);
397            Object obj1 = pool.borrowObject();
398            Object obj2 = pool.borrowObject();
399            pool.returnObject(obj1);
400            pool.returnObject(obj2);
401            factory.setThrowExceptionOnActivate(true);
402            factory.setEvenValid(false);  
403            // Activation will now throw every other time
404            // First attempt throws, but loop continues and second succeeds
405            Object obj = pool.borrowObject();
406            assertEquals(1, pool.getNumActive());
407            assertEquals(0, pool.getNumIdle());
408            
409            pool.returnObject(obj);
410            factory.setValid(false);
411            // Validation will now fail on activation when borrowObject returns
412            // an idle instance, and then when attempting to create a new instance
413            try {
414                obj1 = pool.borrowObject();
415                fail("Expecting NoSuchElementException");
416            } catch (NoSuchElementException ex) {
417                // expected
418            }
419            assertEquals(0, pool.getNumActive());
420            assertEquals(0, pool.getNumIdle());
421        }
422    
423        public void testSetFactoryWithActiveObjects() throws Exception {
424            GenericObjectPool pool = new GenericObjectPool();
425            pool.setMaxIdle(10);
426            pool.setFactory(new SimpleFactory());
427            Object obj = pool.borrowObject();
428            assertNotNull(obj);
429            try {
430                pool.setFactory(null);
431                fail("Expected IllegalStateException");
432            } catch(IllegalStateException e) {
433                // expected
434            }
435            try {
436                pool.setFactory(new SimpleFactory());
437                fail("Expected IllegalStateException");
438            } catch(IllegalStateException e) {
439                // expected
440            }
441        }
442    
443        public void testSetFactoryWithNoActiveObjects() throws Exception {
444            GenericObjectPool pool = new GenericObjectPool();
445            pool.setMaxIdle(10);
446            pool.setFactory(new SimpleFactory());
447            Object obj = pool.borrowObject();
448            pool.returnObject(obj);
449            assertEquals(1,pool.getNumIdle());
450            pool.setFactory(new SimpleFactory());
451            assertEquals(0,pool.getNumIdle());
452        }
453        
454        public void testNegativeMaxActive() throws Exception {
455            pool.setMaxActive(-1);
456            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
457            Object obj = pool.borrowObject();
458            assertEquals(getNthObject(0),obj);
459            pool.returnObject(obj);
460        }
461    
462        public void testMaxIdle() throws Exception {
463            pool.setMaxActive(100);
464            pool.setMaxIdle(8);
465            Object[] active = new Object[100];
466            for(int i=0;i<100;i++) {
467                active[i] = pool.borrowObject();
468            }
469            assertEquals(100,pool.getNumActive());
470            assertEquals(0,pool.getNumIdle());
471            for(int i=0;i<100;i++) {
472                pool.returnObject(active[i]);
473                assertEquals(99 - i,pool.getNumActive());
474                assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle());
475            }
476        }
477    
478        public void testMaxIdleZero() throws Exception {
479            pool.setMaxActive(100);
480            pool.setMaxIdle(0);
481            Object[] active = new Object[100];
482            for(int i=0;i<100;i++) {
483                active[i] = pool.borrowObject();
484            }
485            assertEquals(100,pool.getNumActive());
486            assertEquals(0,pool.getNumIdle());
487            for(int i=0;i<100;i++) {
488                pool.returnObject(active[i]);
489                assertEquals(99 - i,pool.getNumActive());
490                assertEquals(0, pool.getNumIdle());
491            }
492        }
493    
494        public void testMaxActive() throws Exception {
495            pool.setMaxActive(3);
496            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
497    
498            pool.borrowObject();
499            pool.borrowObject();
500            pool.borrowObject();
501            try {
502                pool.borrowObject();
503                fail("Expected NoSuchElementException");
504            } catch(NoSuchElementException e) {
505                // expected
506            }
507        }
508        
509        public void testTimeoutNoLeak() throws Exception {
510            pool.setMaxActive(2);
511            pool.setMaxWait(10);
512            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
513            Object obj = pool.borrowObject();
514            Object obj2 = pool.borrowObject();
515            try {
516                pool.borrowObject();
517                fail("Expecting NoSuchElementException");
518            } catch (NoSuchElementException ex) {
519                //xpected
520            }
521            pool.returnObject(obj2);
522            pool.returnObject(obj);
523            
524            obj = pool.borrowObject();
525            obj2 = pool.borrowObject();
526        }
527    
528        public void testMaxActiveZero() throws Exception {
529            pool.setMaxActive(0);
530            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
531    
532            try {
533                pool.borrowObject();
534                fail("Expected NoSuchElementException");
535            } catch(NoSuchElementException e) {
536                // expected
537            }
538        }
539    
540        public void testMaxActiveUnderLoad() {
541            // Config
542            int numThreads = 199; // And main thread makes a round 200.
543            int numIter = 20;
544            int delay = 25;
545            int maxActive = 10;
546            
547            SimpleFactory factory = new SimpleFactory();
548            factory.setMaxActive(maxActive);
549            pool.setFactory(factory);
550            pool.setMaxActive(maxActive);
551            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
552            pool.setTimeBetweenEvictionRunsMillis(-1);
553            
554            // Start threads to borrow objects
555            TestThread[] threads = new TestThread[numThreads];
556            for(int i=0;i<numThreads;i++) {
557                // Factor of 2 on iterations so main thread does work whilst other
558                // threads are running. Factor of 2 on delay so average delay for
559                // other threads == actual delay for main thread
560                threads[i] = new TestThread(pool, numIter * 2, delay * 2);
561                Thread t = new Thread(threads[i]);
562                t.start();
563            }
564            // Give the threads a chance to start doing some work
565            try {
566                Thread.sleep(5000);
567            } catch(InterruptedException e) {
568                // ignored
569            }
570            
571            for (int i = 0; i < numIter; i++) {
572                Object obj = null;
573                try {
574                    try {
575                        Thread.sleep(delay);
576                    } catch(InterruptedException e) {
577                        // ignored
578                    }
579                    obj = pool.borrowObject();
580                    // Under load, observed _numActive > _maxActive 
581                    if (pool.getNumActive() > pool.getMaxActive()) {
582                        throw new IllegalStateException("Too many active objects");
583                    }
584                    try {
585                        Thread.sleep(delay);
586                    } catch(InterruptedException e) {
587                        // ignored
588                    }
589                } catch (Exception e) {
590                    // Shouldn't happen
591                    e.printStackTrace();
592                    fail("Exception on borrow");
593                } finally {
594                    if (obj != null) {
595                        try {
596                            pool.returnObject(obj);
597                        } catch (Exception e) {
598                            // Ignore
599                        }
600                    }
601                }
602            }
603    
604            for(int i=0;i<numThreads;i++) {
605                while(!(threads[i]).complete()) {
606                    try {
607                        Thread.sleep(500L);
608                    } catch(InterruptedException e) {
609                        // ignored
610                    }
611                }
612                if(threads[i].failed()) {
613                    fail("Thread "+i+" failed: "+threads[i]._error.toString());
614                }
615            }
616        }
617    
618        public void testInvalidWhenExhaustedAction() throws Exception {
619            try {
620                pool.setWhenExhaustedAction(Byte.MAX_VALUE);
621                fail("Expected IllegalArgumentException");
622            } catch(IllegalArgumentException e) {
623                // expected
624            }
625    
626            try {
627                ObjectPool pool = new GenericObjectPool(
628                    new SimpleFactory(),
629                    GenericObjectPool.DEFAULT_MAX_ACTIVE, 
630                    Byte.MAX_VALUE,
631                    GenericObjectPool.DEFAULT_MAX_WAIT, 
632                    GenericObjectPool.DEFAULT_MAX_IDLE,
633                    false,
634                    false,
635                    GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
636                    GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
637                    GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
638                    false
639                );
640                assertNotNull(pool);
641                fail("Expected IllegalArgumentException");
642            } catch(IllegalArgumentException e) {
643                // expected
644            }
645        }
646    
647        public void testSettersAndGetters() throws Exception {
648            GenericObjectPool pool = new GenericObjectPool();
649            {
650                pool.setFactory(new SimpleFactory());
651            }
652            {
653                pool.setMaxActive(123);
654                assertEquals(123,pool.getMaxActive());
655            }
656            {
657                pool.setMaxIdle(12);
658                assertEquals(12,pool.getMaxIdle());
659            }
660            {
661                pool.setMaxWait(1234L);
662                assertEquals(1234L,pool.getMaxWait());
663            }
664            {
665                pool.setMinEvictableIdleTimeMillis(12345L);
666                assertEquals(12345L,pool.getMinEvictableIdleTimeMillis());
667            }
668            {
669                pool.setNumTestsPerEvictionRun(11);
670                assertEquals(11,pool.getNumTestsPerEvictionRun());
671            }
672            {
673                pool.setTestOnBorrow(true);
674                assertTrue(pool.getTestOnBorrow());
675                pool.setTestOnBorrow(false);
676                assertTrue(!pool.getTestOnBorrow());
677            }
678            {
679                pool.setTestOnReturn(true);
680                assertTrue(pool.getTestOnReturn());
681                pool.setTestOnReturn(false);
682                assertTrue(!pool.getTestOnReturn());
683            }
684            {
685                pool.setTestWhileIdle(true);
686                assertTrue(pool.getTestWhileIdle());
687                pool.setTestWhileIdle(false);
688                assertTrue(!pool.getTestWhileIdle());
689            }
690            {
691                pool.setTimeBetweenEvictionRunsMillis(11235L);
692                assertEquals(11235L,pool.getTimeBetweenEvictionRunsMillis());
693            }
694            {
695                pool.setSoftMinEvictableIdleTimeMillis(12135L);
696                assertEquals(12135L,pool.getSoftMinEvictableIdleTimeMillis());
697            }
698            {
699                pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
700                assertEquals(GenericObjectPool.WHEN_EXHAUSTED_BLOCK,pool.getWhenExhaustedAction());
701                pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
702                assertEquals(GenericObjectPool.WHEN_EXHAUSTED_FAIL,pool.getWhenExhaustedAction());
703                pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
704                assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW,pool.getWhenExhaustedAction());
705            }
706        }
707        
708        public void testDefaultConfiguration() throws Exception {
709            GenericObjectPool pool = new GenericObjectPool();
710            assertConfiguration(new GenericObjectPool.Config(),pool);
711        }
712    
713        public void testConstructors() throws Exception {
714            {
715                GenericObjectPool pool = new GenericObjectPool();
716                assertConfiguration(new GenericObjectPool.Config(),pool);
717            }
718            {
719                GenericObjectPool pool = new GenericObjectPool(new SimpleFactory());
720                assertConfiguration(new GenericObjectPool.Config(),pool);
721            }
722            {
723                GenericObjectPool.Config expected = new GenericObjectPool.Config();
724                expected.maxActive = 2;
725                expected.maxIdle = 3;
726                expected.maxWait = 5L;
727                expected.minEvictableIdleTimeMillis = 7L;
728                expected.numTestsPerEvictionRun = 9;
729                expected.testOnBorrow = true;
730                expected.testOnReturn = true;
731                expected.testWhileIdle = true;
732                expected.timeBetweenEvictionRunsMillis = 11L;
733                expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
734                GenericObjectPool pool = new GenericObjectPool(null,expected);
735                assertConfiguration(expected,pool);
736            }
737            {
738                GenericObjectPool.Config expected = new GenericObjectPool.Config();
739                expected.maxActive = 2;
740                GenericObjectPool pool = new GenericObjectPool(null,expected.maxActive);
741                assertConfiguration(expected,pool);
742            }
743            {
744                GenericObjectPool.Config expected = new GenericObjectPool.Config();
745                expected.maxActive = 2;
746                expected.maxWait = 5L;
747                expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
748                GenericObjectPool pool = new GenericObjectPool(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait);
749                assertConfiguration(expected,pool);
750            }
751            {
752                GenericObjectPool.Config expected = new GenericObjectPool.Config();
753                expected.maxActive = 2;
754                expected.maxWait = 5L;
755                expected.testOnBorrow = true;
756                expected.testOnReturn = true;
757                expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
758                GenericObjectPool pool = new GenericObjectPool(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait,expected.testOnBorrow,expected.testOnReturn);
759                assertConfiguration(expected,pool);
760            }
761            {
762                GenericObjectPool.Config expected = new GenericObjectPool.Config();
763                expected.maxActive = 2;
764                expected.maxIdle = 3;
765                expected.maxWait = 5L;
766                expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
767                GenericObjectPool pool = new GenericObjectPool(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait,expected.maxIdle);
768                assertConfiguration(expected,pool);
769            }
770            {
771                GenericObjectPool.Config expected = new GenericObjectPool.Config();
772                expected.maxActive = 2;
773                expected.maxIdle = 3;
774                expected.maxWait = 5L;
775                expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
776                expected.testOnBorrow = true;
777                expected.testOnReturn = true;
778                GenericObjectPool pool = new GenericObjectPool(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait,expected.maxIdle,expected.testOnBorrow,expected.testOnReturn);
779                assertConfiguration(expected,pool);
780            }
781            {
782                GenericObjectPool.Config expected = new GenericObjectPool.Config();
783                expected.maxActive = 2;
784                expected.maxIdle = 3;
785                expected.maxWait = 5L;
786                expected.minEvictableIdleTimeMillis = 7L;
787                expected.numTestsPerEvictionRun = 9;
788                expected.testOnBorrow = true;
789                expected.testOnReturn = true;
790                expected.testWhileIdle = true;
791                expected.timeBetweenEvictionRunsMillis = 11L;
792                expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
793                GenericObjectPool pool = new GenericObjectPool(null,expected.maxActive, expected.whenExhaustedAction, expected.maxWait, expected.maxIdle, expected.testOnBorrow, expected.testOnReturn, expected.timeBetweenEvictionRunsMillis, expected.numTestsPerEvictionRun, expected.minEvictableIdleTimeMillis, expected.testWhileIdle);
794                assertConfiguration(expected,pool);
795            }
796            {
797                GenericObjectPool.Config expected = new GenericObjectPool.Config();
798                expected.maxActive = 2;
799                expected.maxIdle = 3;
800                expected.minIdle = 1;
801                expected.maxWait = 5L;
802                expected.minEvictableIdleTimeMillis = 7L;
803                expected.numTestsPerEvictionRun = 9;
804                expected.testOnBorrow = true;
805                expected.testOnReturn = true;
806                expected.testWhileIdle = true;
807                expected.timeBetweenEvictionRunsMillis = 11L;
808                expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
809                GenericObjectPool pool = new GenericObjectPool(null,expected.maxActive, expected.whenExhaustedAction, expected.maxWait, expected.maxIdle, expected.minIdle, expected.testOnBorrow, expected.testOnReturn, expected.timeBetweenEvictionRunsMillis, expected.numTestsPerEvictionRun, expected.minEvictableIdleTimeMillis, expected.testWhileIdle);
810                assertConfiguration(expected,pool);
811            }
812        }
813    
814        public void testSetConfig() throws Exception {
815            GenericObjectPool.Config expected = new GenericObjectPool.Config();
816            GenericObjectPool pool = new GenericObjectPool();
817            assertConfiguration(expected,pool);
818            expected.maxActive = 2;
819            expected.maxIdle = 3;
820            expected.maxWait = 5L;
821            expected.minEvictableIdleTimeMillis = 7L;
822            expected.numTestsPerEvictionRun = 9;
823            expected.testOnBorrow = true;
824            expected.testOnReturn = true;
825            expected.testWhileIdle = true;
826            expected.timeBetweenEvictionRunsMillis = 11L;
827            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
828            pool.setConfig(expected);
829            assertConfiguration(expected,pool);
830        }
831    
832        public void testDebugInfo() throws Exception {
833            GenericObjectPool pool = new GenericObjectPool(new SimpleFactory());
834            pool.setMaxIdle(3);
835            assertNotNull(pool.debugInfo());
836            Object obj = pool.borrowObject();
837            assertNotNull(pool.debugInfo());
838            pool.returnObject(obj);
839            assertNotNull(pool.debugInfo());
840        }
841    
842        public void testStartAndStopEvictor() throws Exception {
843            // set up pool without evictor
844            pool.setMaxIdle(6);
845            pool.setMaxActive(6);
846            pool.setNumTestsPerEvictionRun(6);
847            pool.setMinEvictableIdleTimeMillis(100L);
848    
849            for(int j=0;j<2;j++) {
850                // populate the pool
851                {
852                    Object[] active = new Object[6];
853                    for(int i=0;i<6;i++) {
854                        active[i] = pool.borrowObject();
855                    }
856                    for(int i=0;i<6;i++) {
857                        pool.returnObject(active[i]);
858                    }
859                }
860        
861                // note that it stays populated
862                assertEquals("Should have 6 idle",6,pool.getNumIdle());
863        
864                // start the evictor
865                pool.setTimeBetweenEvictionRunsMillis(50L);
866                
867                // wait a second (well, .2 seconds)
868                try { Thread.sleep(200L); } catch(InterruptedException e) { }
869                
870                // assert that the evictor has cleared out the pool
871                assertEquals("Should have 0 idle",0,pool.getNumIdle());
872        
873                // stop the evictor 
874                pool.startEvictor(0L);
875            }
876        }
877    
878        public void testEvictionWithNegativeNumTests() throws Exception {
879            // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test
880            pool.setMaxIdle(6);
881            pool.setMaxActive(6);
882            pool.setNumTestsPerEvictionRun(-2);
883            pool.setMinEvictableIdleTimeMillis(50L);
884            pool.setTimeBetweenEvictionRunsMillis(100L);
885    
886            Object[] active = new Object[6];
887            for(int i=0;i<6;i++) {
888                active[i] = pool.borrowObject();
889            }
890            for(int i=0;i<6;i++) {
891                pool.returnObject(active[i]);
892            }
893    
894            try { Thread.sleep(100L); } catch(InterruptedException e) { }
895            assertTrue("Should at most 6 idle, found " + pool.getNumIdle(),pool.getNumIdle() <= 6);
896            try { Thread.sleep(100L); } catch(InterruptedException e) { }
897            assertTrue("Should at most 3 idle, found " + pool.getNumIdle(),pool.getNumIdle() <= 3);
898            try { Thread.sleep(100L); } catch(InterruptedException e) { }
899            assertTrue("Should be at most 2 idle, found " + pool.getNumIdle(),pool.getNumIdle() <= 2);
900            try { Thread.sleep(100L); } catch(InterruptedException e) { }
901            assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
902        }
903    
904        public void testEviction() throws Exception {
905            pool.setMaxIdle(500);
906            pool.setMaxActive(500);
907            pool.setNumTestsPerEvictionRun(100);
908            pool.setMinEvictableIdleTimeMillis(250L);
909            pool.setTimeBetweenEvictionRunsMillis(500L);
910            pool.setTestWhileIdle(true);
911    
912            Object[] active = new Object[500];
913            for(int i=0;i<500;i++) {
914                active[i] = pool.borrowObject();
915            }
916            for(int i=0;i<500;i++) {
917                pool.returnObject(active[i]);
918            }
919    
920            try { Thread.sleep(1000L); } catch(InterruptedException e) { }
921            assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 500);
922            try { Thread.sleep(600L); } catch(InterruptedException e) { }
923            assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 400);
924            try { Thread.sleep(600L); } catch(InterruptedException e) { }
925            assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 300);
926            try { Thread.sleep(600L); } catch(InterruptedException e) { }
927            assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 200);
928            try { Thread.sleep(600L); } catch(InterruptedException e) { }
929            assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 100);
930            try { Thread.sleep(600L); } catch(InterruptedException e) { }
931            assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
932    
933            for(int i=0;i<500;i++) {
934                active[i] = pool.borrowObject();
935            }
936            for(int i=0;i<500;i++) {
937                pool.returnObject(active[i]);
938            }
939    
940            try { Thread.sleep(1000L); } catch(InterruptedException e) { }
941            assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 500);
942            try { Thread.sleep(600L); } catch(InterruptedException e) { }
943            assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 400);
944            try { Thread.sleep(600L); } catch(InterruptedException e) { }
945            assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 300);
946            try { Thread.sleep(600L); } catch(InterruptedException e) { }
947            assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 200);
948            try { Thread.sleep(600L); } catch(InterruptedException e) { }
949            assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 100);
950            try { Thread.sleep(600L); } catch(InterruptedException e) { }
951            assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
952        }
953     
954        public void testEvictionSoftMinIdle() throws Exception {
955            GenericObjectPool pool = null;
956            
957            class TimeTest extends BasePoolableObjectFactory {
958                private final long createTime;
959                public TimeTest() {
960                    createTime = System.currentTimeMillis();
961                }
962                public Object makeObject() throws Exception {
963                    return new TimeTest();
964                }
965                public long getCreateTime() {
966                    return createTime;
967                }
968            }
969            
970            pool = new GenericObjectPool(new TimeTest());
971            
972            pool.setMaxIdle(5);
973            pool.setMaxActive(5);
974            pool.setNumTestsPerEvictionRun(5);
975            pool.setMinEvictableIdleTimeMillis(3000L);
976            pool.setSoftMinEvictableIdleTimeMillis(1000L);
977            pool.setMinIdle(2);
978    
979            Object[] active = new Object[5];
980            Long[] creationTime = new Long[5] ;
981            for(int i=0;i<5;i++) {
982                active[i] = pool.borrowObject();
983                creationTime[i] = new Long(((TimeTest)active[i]).getCreateTime());
984            }
985            
986            for(int i=0;i<5;i++) {
987                pool.returnObject(active[i]);
988            }
989    
990            // Soft evict all but minIdle(2)
991            Thread.sleep(1500L);
992            pool.evict();
993            assertEquals("Idle count different than expected.", 2, pool.getNumIdle());
994    
995            // Hard evict the rest.
996            Thread.sleep(2000L);
997            pool.evict();
998            assertEquals("Idle count different than expected.", 0, pool.getNumIdle());
999        }
1000    
1001        public void testMinIdle() throws Exception {
1002            pool.setMaxIdle(500);
1003            pool.setMinIdle(5);
1004            pool.setMaxActive(10);
1005            pool.setNumTestsPerEvictionRun(0);
1006            pool.setMinEvictableIdleTimeMillis(50L);
1007            pool.setTimeBetweenEvictionRunsMillis(100L);
1008            pool.setTestWhileIdle(true);
1009    
1010            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1011            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
1012    
1013            Object[] active = new Object[5];
1014            active[0] = pool.borrowObject();
1015    
1016            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1017            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
1018    
1019            for(int i=1 ; i<5 ; i++) {
1020                active[i] = pool.borrowObject();
1021            }
1022    
1023            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1024            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
1025    
1026            for(int i=0 ; i<5 ; i++) {
1027                pool.returnObject(active[i]);
1028            }
1029    
1030            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1031            assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
1032        }
1033    
1034        public void testMinIdleMaxActive() throws Exception {
1035            pool.setMaxIdle(500);
1036            pool.setMinIdle(5);
1037            pool.setMaxActive(10);
1038            pool.setNumTestsPerEvictionRun(0);
1039            pool.setMinEvictableIdleTimeMillis(50L);
1040            pool.setTimeBetweenEvictionRunsMillis(100L);
1041            pool.setTestWhileIdle(true);
1042    
1043            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1044            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
1045    
1046            Object[] active = new Object[10];
1047    
1048            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1049            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
1050    
1051            for(int i=0 ; i<5 ; i++) {
1052                active[i] = pool.borrowObject();
1053            }
1054    
1055            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1056            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
1057    
1058            for(int i=0 ; i<5 ; i++) {
1059                pool.returnObject(active[i]);
1060            }
1061    
1062            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1063            assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
1064    
1065            for(int i=0 ; i<10 ; i++) {
1066                active[i] = pool.borrowObject();
1067            }
1068    
1069            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1070            assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
1071    
1072            for(int i=0 ; i<10 ; i++) {
1073                pool.returnObject(active[i]);
1074            }
1075    
1076            try { Thread.sleep(150L); } catch(InterruptedException e) { }
1077            assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
1078        }
1079    
1080        /**
1081         * Kicks off <numThreads> test threads, each of which will go through
1082         * <iterations> borrow-return cycles with random delay times <= delay
1083         * in between.
1084         */
1085        public void runTestThreads(int numThreads, int iterations, int delay) {
1086            TestThread[] threads = new TestThread[numThreads];
1087            for(int i=0;i<numThreads;i++) {
1088                threads[i] = new TestThread(pool,iterations,delay);
1089                Thread t = new Thread(threads[i]);
1090                t.start();
1091            }
1092            for(int i=0;i<numThreads;i++) {
1093                while(!(threads[i]).complete()) {
1094                    try {
1095                        Thread.sleep(500L);
1096                    } catch(InterruptedException e) {
1097                        // ignored
1098                    }
1099                }
1100                if(threads[i].failed()) {
1101                    fail("Thread "+i+" failed: "+threads[i]._error.toString());
1102                }
1103            }
1104        }
1105        
1106        public void testThreaded1() throws Exception {
1107            pool.setMaxActive(15);
1108            pool.setMaxIdle(15);
1109            pool.setMaxWait(1000L);
1110            runTestThreads(20, 100, 50);
1111        }
1112        
1113        /**
1114         * Verifies that maxActive is not exceeded when factory destroyObject
1115         * has high latency, testOnReturn is set and there is high incidence of
1116         * validation failures. 
1117         */
1118        public void testMaxActiveInvariant() throws Exception {
1119            int maxActive = 15;
1120            SimpleFactory factory = new SimpleFactory();
1121            factory.setEvenValid(false);     // Every other validation fails
1122            factory.setDestroyLatency(100);  // Destroy takes 100 ms
1123            factory.setMaxActive(maxActive); // (makes - destroys) bound
1124            factory.setValidationEnabled(true);
1125            pool = new GenericObjectPool(factory);
1126            pool.setMaxActive(maxActive);
1127            pool.setMaxIdle(-1);
1128            pool.setTestOnReturn(true);
1129            pool.setMaxWait(1000L);
1130            runTestThreads(5, 10, 50);
1131        }
1132    
1133        static class TestThread implements Runnable {
1134            private final java.util.Random _random = new java.util.Random();
1135            
1136            // Thread config items
1137            private final ObjectPool _pool;
1138            private final int _iter;
1139            private final int _delay;
1140            private final boolean _randomDelay;
1141            private final Object _expectedObject;
1142            
1143            private volatile boolean _complete = false;
1144            private volatile boolean _failed = false;
1145            private volatile Throwable _error;
1146    
1147            public TestThread(ObjectPool pool) {
1148                this(pool, 100, 50, true, null);
1149            }
1150    
1151            public TestThread(ObjectPool pool, int iter) {
1152                this(pool, iter, 50, true, null);
1153            }
1154    
1155            public TestThread(ObjectPool pool, int iter, int delay) {
1156                this(pool, iter, delay, true, null);
1157            }
1158            
1159            public TestThread(ObjectPool pool, int iter, int delay,
1160                    boolean randomDelay) {
1161                this(pool, iter, delay, randomDelay, null);
1162            }
1163    
1164            public TestThread(ObjectPool pool, int iter, int delay,
1165                    boolean randomDelay, Object obj) {
1166                _pool = pool;
1167                _iter = iter;
1168                _delay = delay;
1169                _randomDelay = randomDelay;
1170                _expectedObject = obj;
1171            }
1172    
1173            public boolean complete() {
1174                return _complete;
1175            }
1176    
1177            public boolean failed() {
1178                return _failed;
1179            }
1180    
1181            public void run() {
1182                for(int i=0;i<_iter;i++) {
1183                    long delay = 
1184                        _randomDelay ? (long)_random.nextInt(_delay) : _delay;
1185                    try {
1186                        Thread.sleep(delay);
1187                    } catch(InterruptedException e) {
1188                        // ignored
1189                    }
1190                    Object obj = null;
1191                    try {
1192                        obj = _pool.borrowObject();
1193                    } catch(Exception e) {
1194                        _error = e;
1195                        _failed = true;
1196                        _complete = true;
1197                        break;
1198                    }
1199    
1200                    if (_expectedObject != null && !_expectedObject.equals(obj)) {
1201                        _error = new Throwable("Expected: "+_expectedObject+ " found: "+obj);
1202                        _failed = true;
1203                        _complete = true;
1204                        break;
1205                    }
1206                    
1207                    try {
1208                        Thread.sleep(delay);
1209                    } catch(InterruptedException e) {
1210                        // ignored
1211                    }
1212                    try {
1213                        _pool.returnObject(obj);
1214                    } catch(Exception e) {
1215                        _error = e;
1216                        _failed = true;
1217                        _complete = true;
1218                        break;
1219                    }
1220                }
1221                _complete = true;
1222            }
1223        }
1224    
1225        public void testFIFO() throws Exception {
1226            pool.setLifo(false);
1227            pool.addObject(); // "0"
1228            pool.addObject(); // "1"
1229            pool.addObject(); // "2"
1230            assertEquals("Oldest", "0", pool.borrowObject());
1231            assertEquals("Middle", "1", pool.borrowObject());
1232            assertEquals("Youngest", "2", pool.borrowObject());
1233            assertEquals("new-3", "3", pool.borrowObject());
1234            pool.returnObject("r");
1235            assertEquals("returned", "r", pool.borrowObject());
1236            assertEquals("new-4", "4", pool.borrowObject());
1237        }
1238        
1239        public void testLIFO() throws Exception {
1240            pool.setLifo(true);
1241            pool.addObject(); // "0"
1242            pool.addObject(); // "1"
1243            pool.addObject(); // "2"
1244            assertEquals("Youngest", "2", pool.borrowObject());
1245            assertEquals("Middle", "1", pool.borrowObject());
1246            assertEquals("Oldest", "0", pool.borrowObject());
1247            assertEquals("new-3", "3", pool.borrowObject());
1248            pool.returnObject("r");
1249            assertEquals("returned", "r", pool.borrowObject());
1250            assertEquals("new-4", "4", pool.borrowObject());
1251        }
1252    
1253        public void testAddObject() throws Exception {
1254            assertEquals("should be zero idle", 0, pool.getNumIdle());
1255            pool.addObject();
1256            assertEquals("should be one idle", 1, pool.getNumIdle());
1257            assertEquals("should be zero active", 0, pool.getNumActive());
1258            Object obj = pool.borrowObject();
1259            assertEquals("should be zero idle", 0, pool.getNumIdle());
1260            assertEquals("should be one active", 1, pool.getNumActive());
1261            pool.returnObject(obj);
1262            assertEquals("should be one idle", 1, pool.getNumIdle());
1263            assertEquals("should be zero active", 0, pool.getNumActive());
1264    
1265            ObjectPool op = new GenericObjectPool();
1266            try {
1267                op.addObject();
1268                fail("Expected IllegalStateException when there is no factory.");
1269            } catch (IllegalStateException ise) {
1270                //expected
1271            }
1272            op.close();
1273        }
1274        
1275        protected GenericObjectPool pool = null;
1276    
1277        private void assertConfiguration(GenericObjectPool.Config expected, GenericObjectPool actual) throws Exception {
1278            assertEquals("testOnBorrow",expected.testOnBorrow,actual.getTestOnBorrow());
1279            assertEquals("testOnReturn",expected.testOnReturn,actual.getTestOnReturn());
1280            assertEquals("testWhileIdle",expected.testWhileIdle,actual.getTestWhileIdle());
1281            assertEquals("whenExhaustedAction",expected.whenExhaustedAction,actual.getWhenExhaustedAction());
1282            assertEquals("maxActive",expected.maxActive,actual.getMaxActive());
1283            assertEquals("maxIdle",expected.maxIdle,actual.getMaxIdle());
1284            assertEquals("maxWait",expected.maxWait,actual.getMaxWait());
1285            assertEquals("minEvictableIdleTimeMillis",expected.minEvictableIdleTimeMillis,actual.getMinEvictableIdleTimeMillis());
1286            assertEquals("numTestsPerEvictionRun",expected.numTestsPerEvictionRun,actual.getNumTestsPerEvictionRun());
1287            assertEquals("timeBetweenEvictionRunsMillis",expected.timeBetweenEvictionRunsMillis,actual.getTimeBetweenEvictionRunsMillis());
1288        }
1289    
1290        public class SimpleFactory implements PoolableObjectFactory {
1291            public SimpleFactory() {
1292                this(true);
1293            }
1294            public SimpleFactory(boolean valid) {
1295                this(valid,valid);
1296            }
1297            public SimpleFactory(boolean evalid, boolean ovalid) {
1298                evenValid = evalid;
1299                oddValid = ovalid;
1300            }
1301            void setValid(boolean valid) {
1302                setEvenValid(valid);
1303                setOddValid(valid);            
1304            }
1305            void setEvenValid(boolean valid) {
1306                evenValid = valid;
1307            }
1308            void setOddValid(boolean valid) {
1309                oddValid = valid;
1310            }
1311            public void setThrowExceptionOnPassivate(boolean bool) {
1312                exceptionOnPassivate = bool;
1313            }
1314            public void setMaxActive(int maxActive) {
1315                this.maxActive = maxActive;
1316            }
1317            public void setDestroyLatency(long destroyLatency) {
1318                this.destroyLatency = destroyLatency;
1319            }
1320            public void setMakeLatency(long makeLatency) {
1321                this.makeLatency = makeLatency;
1322            }
1323            public Object makeObject() { 
1324                synchronized(this) {
1325                    activeCount++;
1326                    if (activeCount > maxActive) {
1327                        throw new IllegalStateException(
1328                            "Too many active instances: " + activeCount);
1329                    }
1330                }
1331                if (makeLatency > 0) {
1332                    doWait(makeLatency);
1333                }
1334                return String.valueOf(makeCounter++);
1335            }
1336            public void destroyObject(Object obj) throws Exception {
1337                if (destroyLatency > 0) {
1338                    doWait(destroyLatency);
1339                }
1340                synchronized(this) {
1341                    activeCount--;
1342                }
1343                if (exceptionOnDestroy) {
1344                    throw new Exception();
1345                }
1346            }
1347            public boolean validateObject(Object obj) {
1348                if (enableValidation) { 
1349                    return validateCounter++%2 == 0 ? evenValid : oddValid; 
1350                }
1351                else {
1352                    return true;
1353                }
1354            }
1355            public void activateObject(Object obj) throws Exception {
1356                if (exceptionOnActivate) {
1357                    if (!(validateCounter++%2 == 0 ? evenValid : oddValid)) {
1358                        throw new Exception();
1359                    }
1360                }
1361            }
1362            public void passivateObject(Object obj) throws Exception {
1363                if(exceptionOnPassivate) {
1364                    throw new Exception();
1365                }
1366            }
1367            int makeCounter = 0;
1368            int validateCounter = 0;
1369            int activeCount = 0;
1370            boolean evenValid = true;
1371            boolean oddValid = true;
1372            boolean exceptionOnPassivate = false;
1373            boolean exceptionOnActivate = false;
1374            boolean exceptionOnDestroy = false;
1375            boolean enableValidation = true;
1376            long destroyLatency = 0;
1377            long makeLatency = 0;
1378            int maxActive = Integer.MAX_VALUE;
1379    
1380            public boolean isThrowExceptionOnActivate() {
1381                return exceptionOnActivate;
1382            }
1383    
1384            public void setThrowExceptionOnActivate(boolean b) {
1385                exceptionOnActivate = b;
1386            }
1387            
1388            public void setThrowExceptionOnDestroy(boolean b) {
1389                exceptionOnDestroy = b;
1390            }
1391    
1392            public boolean isValidationEnabled() {
1393                return enableValidation;
1394            }
1395    
1396            public void setValidationEnabled(boolean b) {
1397                enableValidation = b;
1398            }
1399            
1400            private void doWait(long latency) {
1401                try {
1402                    Thread.sleep(latency);
1403                } catch (InterruptedException ex) {
1404                    // ignore
1405                }
1406            }
1407        }
1408        protected boolean isLifo() {
1409            return true;
1410        }
1411    
1412        protected boolean isFifo() {
1413            return false;
1414        }
1415    
1416        /*
1417         * Note: This test relies on timing for correct execution. There *should* be
1418         * enough margin for this to work correctly on most (all?) systems but be
1419         * aware of this if you see a failure of this test.
1420         */
1421        public void testBorrowObjectFairness() {
1422            // Config
1423            int numThreads = 30;
1424            int maxActive = 10;
1425    
1426            SimpleFactory factory = new SimpleFactory();
1427            factory.setMaxActive(maxActive);
1428            pool.setFactory(factory);
1429            pool.setMaxActive(maxActive);
1430            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
1431            pool.setTimeBetweenEvictionRunsMillis(-1);
1432    
1433            // Start threads to borrow objects
1434            TestThread[] threads = new TestThread[numThreads];
1435            for(int i=0;i<numThreads;i++) {
1436                threads[i] = new TestThread(pool, 1, 2000, false, String.valueOf(i % maxActive));
1437                Thread t = new Thread(threads[i]);
1438                t.start();
1439                // Short delay to ensure threads start in correct order
1440                try {
1441                    Thread.sleep(50);
1442                } catch (InterruptedException e) {
1443                    fail(e.toString());
1444                }
1445            }
1446    
1447            // Wait for threads to finish
1448            for(int i=0;i<numThreads;i++) {
1449                while(!(threads[i]).complete()) {
1450                    try {
1451                        Thread.sleep(500L);
1452                    } catch(InterruptedException e) {
1453                        // ignored
1454                    }
1455                }
1456                if(threads[i].failed()) {
1457                    fail("Thread "+i+" failed: "+threads[i]._error.toString());
1458                }
1459            }
1460        }
1461    }