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