View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.pool.impl;
19  
20  import java.util.NoSuchElementException;
21  import java.util.Random;
22  
23  import org.apache.commons.pool.BasePoolableObjectFactory;
24  import org.apache.commons.pool.ObjectPool;
25  import org.apache.commons.pool.PoolUtils;
26  import org.apache.commons.pool.PoolableObjectFactory;
27  import org.apache.commons.pool.TestBaseObjectPool;
28  import org.apache.commons.pool.VisitTracker;
29  import org.apache.commons.pool.VisitTrackerFactory;
30  
31  /**
32   * @author Rodney Waldhoff
33   * @author Dirk Verbeeck
34   * @author Sandy McArthur
35   * @version $Revision: 1205211 $ $Date: 2011-11-22 15:47:57 -0700 (Tue, 22 Nov 2011) $
36   */
37  public class TestGenericObjectPool extends TestBaseObjectPool {
38      public TestGenericObjectPool(String testName) {
39          super(testName);
40      }
41  
42      protected ObjectPool makeEmptyPool(int mincap) {
43         GenericObjectPool pool = new GenericObjectPool(new SimpleFactory());
44         pool.setMaxActive(mincap);
45         pool.setMaxIdle(mincap);
46         return pool;
47      }
48  
49      protected ObjectPool makeEmptyPool(final PoolableObjectFactory factory) {
50          return new GenericObjectPool(factory);
51      }
52  
53      protected Object getNthObject(int n) {
54          return String.valueOf(n);
55      }
56  
57      public void setUp() throws Exception {
58          super.setUp();
59          pool = new GenericObjectPool(new SimpleFactory());
60      }
61  
62      public void tearDown() throws Exception {
63          super.tearDown();
64          pool.clear();
65          pool.close();
66          pool = null;
67      }
68  
69      public void testWhenExhaustedGrow() throws Exception {
70          pool.setMaxActive(1);
71          pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
72          Object obj1 = pool.borrowObject();
73          assertNotNull(obj1);
74          Object obj2 = pool.borrowObject();
75          assertNotNull(obj2);
76          pool.returnObject(obj2);
77          pool.returnObject(obj1);
78          pool.close();
79      }
80  
81      public void testWhenExhaustedFail() throws Exception {
82          pool.setMaxActive(1);
83          pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
84          Object obj1 = pool.borrowObject();
85          assertNotNull(obj1);
86          try {
87              pool.borrowObject();
88              fail("Expected NoSuchElementException");
89          } catch(NoSuchElementException e) {
90              // expected
91          }
92          pool.returnObject(obj1);
93          assertEquals(1, pool.getNumIdle());
94          pool.close();
95      }
96  
97      public void testWhenExhaustedBlock() throws Exception {
98          pool.setMaxActive(1);
99          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 }