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