001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.pool.impl;
019    
020    import junit.framework.Test;
021    import junit.framework.TestSuite;
022    import org.apache.commons.pool.KeyedObjectPool;
023    import org.apache.commons.pool.KeyedPoolableObjectFactory;
024    import org.apache.commons.pool.TestBaseKeyedObjectPool;
025    import org.apache.commons.pool.VisitTracker;
026    import org.apache.commons.pool.VisitTrackerFactory;
027    
028    import java.io.PrintWriter;
029    import java.io.StringWriter;
030    import java.util.HashMap;
031    import java.util.NoSuchElementException;
032    import java.util.Random;
033    
034    /**
035     * @author Rodney Waldhoff
036     * @version $Revision: 791860 $ $Date: 2009-07-07 11:10:30 -0400 (Tue, 07 Jul 2009) $
037     */
038    public class TestGenericKeyedObjectPool extends TestBaseKeyedObjectPool {
039        public TestGenericKeyedObjectPool(String testName) {
040            super(testName);
041        }
042    
043        public static Test suite() {
044            return new TestSuite(TestGenericKeyedObjectPool.class);
045        }
046    
047        protected KeyedObjectPool makeEmptyPool(int mincapacity) {
048            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(
049                new KeyedPoolableObjectFactory()  {
050                    HashMap map = new HashMap();
051                    public Object makeObject(Object key) {
052                        int counter = 0;
053                        Integer Counter = (Integer)(map.get(key));
054                        if(null != Counter) {
055                            counter = Counter.intValue();
056                        }
057                        map.put(key,new Integer(counter + 1));
058                        return String.valueOf(key) + String.valueOf(counter);
059                    }
060                    public void destroyObject(Object key, Object obj) { }
061                    public boolean validateObject(Object key, Object obj) { return true; }
062                    public void activateObject(Object key, Object obj) { }
063                    public void passivateObject(Object key, Object obj) { }
064                }
065            );
066            pool.setMaxActive(mincapacity);
067            pool.setMaxIdle(mincapacity);
068            return pool;
069        }
070    
071        protected KeyedObjectPool makeEmptyPool(KeyedPoolableObjectFactory factory) {
072            return new GenericKeyedObjectPool(factory);
073        }
074    
075        protected Object getNthObject(Object key, int n) {
076            return String.valueOf(key) + String.valueOf(n);
077        }
078    
079        protected Object makeKey(int n) {
080            return String.valueOf(n);
081        }
082    
083        private GenericKeyedObjectPool pool = null;
084        private final Integer zero = new Integer(0);
085        private final Integer one = new Integer(1);
086        private final Integer two = new Integer(2);
087    
088        public void setUp() throws Exception {
089            super.setUp();
090            pool = new GenericKeyedObjectPool(new SimpleFactory());
091        }
092    
093        public void tearDown() throws Exception {
094            super.tearDown();
095            pool.clear();
096            pool.close();
097            pool = null;
098        }
099    
100        public void testNegativeMaxActive() throws Exception {
101            pool.setMaxActive(-1);
102            pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
103            Object obj = pool.borrowObject("");
104            assertEquals("0",obj);
105            pool.returnObject("",obj);
106        }
107    
108        public void testNumActiveNumIdle2() throws Exception {
109            assertEquals(0,pool.getNumActive());
110            assertEquals(0,pool.getNumIdle());
111            assertEquals(0,pool.getNumActive("A"));
112            assertEquals(0,pool.getNumIdle("A"));
113            assertEquals(0,pool.getNumActive("B"));
114            assertEquals(0,pool.getNumIdle("B"));
115    
116            Object objA0 = pool.borrowObject("A");
117            Object objB0 = pool.borrowObject("B");
118    
119            assertEquals(2,pool.getNumActive());
120            assertEquals(0,pool.getNumIdle());
121            assertEquals(1,pool.getNumActive("A"));
122            assertEquals(0,pool.getNumIdle("A"));
123            assertEquals(1,pool.getNumActive("B"));
124            assertEquals(0,pool.getNumIdle("B"));
125    
126            Object objA1 = pool.borrowObject("A");
127            Object objB1 = pool.borrowObject("B");
128    
129            assertEquals(4,pool.getNumActive());
130            assertEquals(0,pool.getNumIdle());
131            assertEquals(2,pool.getNumActive("A"));
132            assertEquals(0,pool.getNumIdle("A"));
133            assertEquals(2,pool.getNumActive("B"));
134            assertEquals(0,pool.getNumIdle("B"));
135    
136            pool.returnObject("A",objA0);
137            pool.returnObject("B",objB0);
138    
139            assertEquals(2,pool.getNumActive());
140            assertEquals(2,pool.getNumIdle());
141            assertEquals(1,pool.getNumActive("A"));
142            assertEquals(1,pool.getNumIdle("A"));
143            assertEquals(1,pool.getNumActive("B"));
144            assertEquals(1,pool.getNumIdle("B"));
145    
146            pool.returnObject("A",objA1);
147            pool.returnObject("B",objB1);
148    
149            assertEquals(0,pool.getNumActive());
150            assertEquals(4,pool.getNumIdle());
151            assertEquals(0,pool.getNumActive("A"));
152            assertEquals(2,pool.getNumIdle("A"));
153            assertEquals(0,pool.getNumActive("B"));
154            assertEquals(2,pool.getNumIdle("B"));
155        }
156    
157        public void testMaxIdle() throws Exception {
158            pool.setMaxActive(100);
159            pool.setMaxIdle(8);
160            Object[] active = new Object[100];
161            for(int i=0;i<100;i++) {
162                active[i] = pool.borrowObject("");
163            }
164            assertEquals(100,pool.getNumActive(""));
165            assertEquals(0,pool.getNumIdle(""));
166            for(int i=0;i<100;i++) {
167                pool.returnObject("",active[i]);
168                assertEquals(99 - i,pool.getNumActive(""));
169                assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle(""));
170            }
171            
172            for(int i=0;i<100;i++) {
173                active[i] = pool.borrowObject("a");
174            }
175            assertEquals(100,pool.getNumActive("a"));
176            assertEquals(0,pool.getNumIdle("a"));
177            for(int i=0;i<100;i++) {
178                pool.returnObject("a",active[i]);
179                assertEquals(99 - i,pool.getNumActive("a"));
180                assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle("a"));
181            }
182            
183            // total number of idle instances is twice maxIdle
184            assertEquals(16, pool.getNumIdle());
185            // Each pool is at the sup
186            assertEquals(8, pool.getNumIdle(""));
187            assertEquals(8, pool.getNumIdle("a"));
188                 
189        }
190    
191        public void testMaxActive() throws Exception {
192            pool.setMaxActive(3);
193            pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
194    
195            pool.borrowObject("");
196            pool.borrowObject("");
197            pool.borrowObject("");
198            try {
199                pool.borrowObject("");
200                fail("Expected NoSuchElementException");
201            } catch(NoSuchElementException e) {
202                // expected
203            }
204        }
205    
206        public void testMaxActiveZero() throws Exception {
207            pool.setMaxActive(0);
208            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
209    
210            try {
211                pool.borrowObject("a");
212                fail("Expected NoSuchElementException");
213            } catch(NoSuchElementException e) {
214                // expected
215            }
216        }
217        
218        public void testWhenExhaustedGrow() throws Exception {
219            pool.setMaxActive(1);
220            pool.setMaxTotal(1);
221            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
222            for (int i = 0; i < 10; i++) {
223                pool.borrowObject("a");
224            }
225        }
226    
227        public void testMaxTotal() throws Exception {
228            pool.setMaxActive(2);
229            pool.setMaxTotal(3);
230            pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
231    
232            Object o1 = pool.borrowObject("a");
233            assertNotNull(o1);
234            Object o2 = pool.borrowObject("a");
235            assertNotNull(o2);
236            Object o3 = pool.borrowObject("b");
237            assertNotNull(o3);
238            try {
239                pool.borrowObject("c");
240                fail("Expected NoSuchElementException");
241            } catch(NoSuchElementException e) {
242                // expected
243            }
244    
245            assertEquals(0, pool.getNumIdle());
246    
247            pool.returnObject("b", o3);
248            assertEquals(1, pool.getNumIdle());
249            assertEquals(1, pool.getNumIdle("b"));
250    
251            Object o4 = pool.borrowObject("b");
252            assertNotNull(o4);
253            assertEquals(0, pool.getNumIdle());
254            assertEquals(0, pool.getNumIdle("b"));
255            
256            pool.setMaxTotal(4);
257            Object o5 = pool.borrowObject("b");
258            assertNotNull(o5);
259            
260            assertEquals(2, pool.getNumActive("a"));
261            assertEquals(2, pool.getNumActive("b"));
262            assertEquals(pool.getMaxTotal(),
263                    pool.getNumActive("b") + pool.getNumActive("b"));
264            assertEquals(pool.getNumActive(),
265                    pool.getMaxTotal());
266        }
267    
268        public void testMaxTotalZero() throws Exception {
269            pool.setMaxTotal(0);
270            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
271    
272            try {
273                pool.borrowObject("a");
274                fail("Expected NoSuchElementException");
275            } catch(NoSuchElementException e) {
276                // expected
277            }
278        }
279    
280        public void testMaxTotalLRU() throws Exception {
281            pool.setMaxActive(2);
282            pool.setMaxTotal(3);
283    //        pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW);
284    
285            Object o1 = pool.borrowObject("a");
286            assertNotNull(o1);
287            pool.returnObject("a", o1);
288            Thread.sleep(25);
289    
290            Object o2 = pool.borrowObject("b");
291            assertNotNull(o2);
292            pool.returnObject("b", o2);
293            Thread.sleep(25);
294    
295            Object o3 = pool.borrowObject("c");
296            assertNotNull(o3);
297            pool.returnObject("c", o3);
298            Thread.sleep(25);
299    
300            Object o4 = pool.borrowObject("a");
301            assertNotNull(o4);
302            pool.returnObject("a", o4);
303            Thread.sleep(25);
304    
305            assertSame(o1, o4);
306    
307            // this should cause b to be bumped out of the pool
308            Object o5 = pool.borrowObject("d");
309            assertNotNull(o5);
310            pool.returnObject("d", o5);
311            Thread.sleep(25);
312    
313            // now re-request b, we should get a different object because it should
314            // have been expelled from pool (was oldest because a was requested after b)
315            Object o6 = pool.borrowObject("b");
316            assertNotNull(o6);
317            pool.returnObject("b", o6);
318    
319            assertNotSame(o1, o6);
320    
321            // second a is still in there
322            Object o7 = pool.borrowObject("a");
323            assertNotNull(o7);
324            pool.returnObject("a", o7);
325    
326            assertSame(o4, o7);
327        }
328    
329        public void testSettersAndGetters() throws Exception {
330            GenericKeyedObjectPool pool = new GenericKeyedObjectPool();
331            {
332                pool.setFactory(new SimpleFactory());
333            }
334            {
335                pool.setMaxActive(123);
336                assertEquals(123,pool.getMaxActive());
337            }
338            {
339                pool.setMaxIdle(12);
340                assertEquals(12,pool.getMaxIdle());
341            }
342            {
343                pool.setMaxWait(1234L);
344                assertEquals(1234L,pool.getMaxWait());
345            }
346            {
347                pool.setMinEvictableIdleTimeMillis(12345L);
348                assertEquals(12345L,pool.getMinEvictableIdleTimeMillis());
349            }
350            {
351                pool.setNumTestsPerEvictionRun(11);
352                assertEquals(11,pool.getNumTestsPerEvictionRun());
353            }
354            {
355                pool.setTestOnBorrow(true);
356                assertTrue(pool.getTestOnBorrow());
357                pool.setTestOnBorrow(false);
358                assertTrue(!pool.getTestOnBorrow());
359            }
360            {
361                pool.setTestOnReturn(true);
362                assertTrue(pool.getTestOnReturn());
363                pool.setTestOnReturn(false);
364                assertTrue(!pool.getTestOnReturn());
365            }
366            {
367                pool.setTestWhileIdle(true);
368                assertTrue(pool.getTestWhileIdle());
369                pool.setTestWhileIdle(false);
370                assertTrue(!pool.getTestWhileIdle());
371            }
372            {
373                pool.setTimeBetweenEvictionRunsMillis(11235L);
374                assertEquals(11235L,pool.getTimeBetweenEvictionRunsMillis());
375            }
376            {
377                pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK);
378                assertEquals(GenericObjectPool.WHEN_EXHAUSTED_BLOCK,pool.getWhenExhaustedAction());
379                pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
380                assertEquals(GenericObjectPool.WHEN_EXHAUSTED_FAIL,pool.getWhenExhaustedAction());
381                pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW);
382                assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW,pool.getWhenExhaustedAction());
383            }
384        }
385    
386        public void testEviction() throws Exception {
387            pool.setMaxIdle(500);
388            pool.setMaxActive(500);
389            pool.setNumTestsPerEvictionRun(100);
390            pool.setMinEvictableIdleTimeMillis(250L);
391            pool.setTimeBetweenEvictionRunsMillis(500L);
392    
393            Object[] active = new Object[500];
394            for(int i=0;i<500;i++) {
395                active[i] = pool.borrowObject("");
396            }
397            for(int i=0;i<500;i++) {
398                pool.returnObject("",active[i]);
399            }
400    
401            try { Thread.sleep(1000L); } catch(InterruptedException e) { }
402            assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 500);
403            try { Thread.sleep(600L); } catch(InterruptedException e) { }
404            assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 400);
405            try { Thread.sleep(600L); } catch(InterruptedException e) { }
406            assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 300);
407            try { Thread.sleep(600L); } catch(InterruptedException e) { }
408            assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 200);
409            try { Thread.sleep(600L); } catch(InterruptedException e) { }
410            assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 100);
411            try { Thread.sleep(600L); } catch(InterruptedException e) { }
412            assertEquals("Should be zero idle, found " + pool.getNumIdle(""),0,pool.getNumIdle(""));
413    
414            for(int i=0;i<500;i++) {
415                active[i] = pool.borrowObject("");
416            }
417            for(int i=0;i<500;i++) {
418                pool.returnObject("",active[i]);
419            }
420    
421            try { Thread.sleep(1000L); } catch(InterruptedException e) { }
422            assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 500);
423            try { Thread.sleep(600L); } catch(InterruptedException e) { }
424            assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 400);
425            try { Thread.sleep(600L); } catch(InterruptedException e) { }
426            assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 300);
427            try { Thread.sleep(600L); } catch(InterruptedException e) { }
428            assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 200);
429            try { Thread.sleep(600L); } catch(InterruptedException e) { }
430            assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 100);
431            try { Thread.sleep(600L); } catch(InterruptedException e) { }
432            assertEquals("Should be zero idle, found " + pool.getNumIdle(""),0,pool.getNumIdle(""));
433        }
434    
435        public void testEviction2() throws Exception {
436            pool.setMaxIdle(500);
437            pool.setMaxActive(500);
438            pool.setNumTestsPerEvictionRun(100);
439            pool.setMinEvictableIdleTimeMillis(500L);
440            pool.setTimeBetweenEvictionRunsMillis(500L);
441    
442            Object[] active = new Object[500];
443            Object[] active2 = new Object[500];
444            for(int i=0;i<500;i++) {
445                active[i] = pool.borrowObject("");
446                active2[i] = pool.borrowObject("2");
447            }
448            for(int i=0;i<500;i++) {
449                pool.returnObject("",active[i]);
450                pool.returnObject("2",active2[i]);
451            }
452    
453            try { Thread.sleep(1100L); } catch(InterruptedException e) { }
454            assertTrue("Should be less than 1000 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 1000);
455            try { Thread.sleep(600L); } catch(InterruptedException e) { }
456            assertTrue("Should be less than 900 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 900);
457            try { Thread.sleep(600L); } catch(InterruptedException e) { }
458            assertTrue("Should be less than 800 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 800);
459            try { Thread.sleep(600L); } catch(InterruptedException e) { }
460            assertTrue("Should be less than 700 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 700);
461            try { Thread.sleep(600L); } catch(InterruptedException e) { }
462            assertTrue("Should be less than 600 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 600);
463            try { Thread.sleep(600L); } catch(InterruptedException e) { }
464            assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 500);
465            try { Thread.sleep(600L); } catch(InterruptedException e) { }
466            assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 400);
467            try { Thread.sleep(600L); } catch(InterruptedException e) { }
468            assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 300);
469            try { Thread.sleep(600L); } catch(InterruptedException e) { }
470            assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 200);
471            try { Thread.sleep(600L); } catch(InterruptedException e) { }
472            assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 100);
473            try { Thread.sleep(600L); } catch(InterruptedException e) { }
474            assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
475        }
476    
477        /**
478         * Kicks off <numThreads> test threads, each of which will go through
479         * <iterations> borrow-return cycles with random delay times <= delay
480         * in between.
481         */
482        public void runTestThreads(int numThreads, int iterations, int delay) {
483            TestThread[] threads = new TestThread[numThreads];
484            for(int i=0;i<numThreads;i++) {
485                threads[i] = new TestThread(pool,iterations,delay);
486                Thread t = new Thread(threads[i]);
487                t.start();
488            }
489            for(int i=0;i<numThreads;i++) {
490                while(!(threads[i]).complete()) {
491                    try {
492                        Thread.sleep(500L);
493                    } catch(InterruptedException e) {
494                        // ignored
495                    }
496                }
497                if(threads[i].failed()) {
498                    fail("Thread failed: "+i+"\n"+getExceptionTrace(threads[i]._exception));
499                }
500            }
501        }
502        
503        public void testThreaded1() throws Exception {
504            pool.setMaxActive(15);
505            pool.setMaxIdle(15);
506            pool.setMaxWait(1000L);
507            runTestThreads(20, 100, 50);
508        }
509        
510        /**
511         * Verifies that maxTotal is not exceeded when factory destroyObject
512         * has high latency, testOnReturn is set and there is high incidence of
513         * validation failures. 
514         */
515        public void testMaxTotalInvariant() throws Exception {
516            int maxTotal = 15;
517            SimpleFactory factory = new SimpleFactory();
518            factory.setEvenValid(false);     // Every other validation fails
519            factory.setDestroyLatency(100);  // Destroy takes 100 ms
520            factory.setMaxActive(maxTotal);  // (makes - destroys) bound
521            factory.setValidationEnabled(true);
522            pool = new GenericKeyedObjectPool(factory);
523            pool.setMaxTotal(maxTotal);
524            pool.setMaxIdle(-1);
525            pool.setTestOnReturn(true);
526            pool.setMaxWait(10000L);
527            runTestThreads(5, 10, 50);
528        }
529    
530        public void testMinIdle() throws Exception {
531            pool.setMaxIdle(500);
532            pool.setMinIdle(5);
533            pool.setMaxActive(10);
534            pool.setNumTestsPerEvictionRun(0);
535            pool.setMinEvictableIdleTimeMillis(50L);
536            pool.setTimeBetweenEvictionRunsMillis(100L);
537            pool.setTestWhileIdle(true);
538    
539    
540            //Generate a random key
541            String key = "A";
542    
543            pool.preparePool(key, true);
544    
545            try { Thread.sleep(150L); } catch(InterruptedException e) { }
546            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
547    
548            Object[] active = new Object[5];
549            active[0] = pool.borrowObject(key);
550    
551            try { Thread.sleep(150L); } catch(InterruptedException e) { }
552            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
553    
554            for(int i=1 ; i<5 ; i++) {
555                active[i] = pool.borrowObject(key);
556            }
557    
558            try { Thread.sleep(150L); } catch(InterruptedException e) { }
559            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
560    
561            for(int i=0 ; i<5 ; i++) {
562                pool.returnObject(key, active[i]);
563            }
564    
565            try { Thread.sleep(150L); } catch(InterruptedException e) { }
566            assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
567        }
568    
569        public void testMinIdleMaxActive() throws Exception {
570            pool.setMaxIdle(500);
571            pool.setMinIdle(5);
572            pool.setMaxActive(10);
573            pool.setNumTestsPerEvictionRun(0);
574            pool.setMinEvictableIdleTimeMillis(50L);
575            pool.setTimeBetweenEvictionRunsMillis(100L);
576            pool.setTestWhileIdle(true);
577    
578            String key = "A";
579    
580            pool.preparePool(key, true);
581            assertTrue("Should be 5 idle, found " + 
582                    pool.getNumIdle(),pool.getNumIdle() == 5);
583    
584            try { Thread.sleep(150L); } catch(InterruptedException e) { }
585            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
586    
587            Object[] active = new Object[10];
588    
589            try { Thread.sleep(150L); } catch(InterruptedException e) { }
590            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
591    
592            for(int i=0 ; i<5 ; i++) {
593                active[i] = pool.borrowObject(key);
594            }
595    
596            try { Thread.sleep(150L); } catch(InterruptedException e) { }
597            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
598    
599            for(int i=0 ; i<5 ; i++) {
600                pool.returnObject(key, active[i]);
601            }
602    
603            try { Thread.sleep(150L); } catch(InterruptedException e) { }
604            assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
605    
606            for(int i=0 ; i<10 ; i++) {
607                active[i] = pool.borrowObject(key);
608            }
609    
610            try { Thread.sleep(150L); } catch(InterruptedException e) { }
611            assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
612    
613            for(int i=0 ; i<10 ; i++) {
614                pool.returnObject(key, active[i]);
615            }
616    
617            try { Thread.sleep(150L); } catch(InterruptedException e) { }
618            assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
619        }
620    
621        public void testMinIdleNoPopulateImmediately() throws Exception {
622            pool.setMaxIdle(500);
623            pool.setMinIdle(5);
624            pool.setMaxActive(10);
625            pool.setNumTestsPerEvictionRun(0);
626            pool.setMinEvictableIdleTimeMillis(50L);
627            pool.setTimeBetweenEvictionRunsMillis(1000L);
628            pool.setTestWhileIdle(true);
629    
630    
631            //Generate a random key
632            String key = "A";
633    
634            pool.preparePool(key, false);
635    
636            assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
637    
638            try { Thread.sleep(1500L); } catch(InterruptedException e) { }
639            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
640        }
641    
642        public void testMinIdleNoPreparePool() throws Exception {
643            pool.setMaxIdle(500);
644            pool.setMinIdle(5);
645            pool.setMaxActive(10);
646            pool.setNumTestsPerEvictionRun(0);
647            pool.setMinEvictableIdleTimeMillis(50L);
648            pool.setTimeBetweenEvictionRunsMillis(100L);
649            pool.setTestWhileIdle(true);
650    
651    
652            //Generate a random key
653            String key = "A";
654    
655            try { Thread.sleep(150L); } catch(InterruptedException e) { }
656            assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
657    
658            Object active = pool.borrowObject(key);
659            assertNotNull(active);
660    
661            try { Thread.sleep(150L); } catch(InterruptedException e) { }
662            assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
663        }
664    
665        public void testFIFO() throws Exception {
666            pool.setLifo(false);
667            final Object key = "key";
668            pool.addObject(key); // "key0"
669            pool.addObject(key); // "key1"
670            pool.addObject(key); // "key2"
671            assertEquals("Oldest", "key0", pool.borrowObject(key));
672            assertEquals("Middle", "key1", pool.borrowObject(key));
673            assertEquals("Youngest", "key2", pool.borrowObject(key));
674            assertEquals("new-3", "key3", pool.borrowObject(key));
675            pool.returnObject(key, "r");
676            assertEquals("returned", "r", pool.borrowObject(key));
677            assertEquals("new-4", "key4", pool.borrowObject(key));
678        }
679        
680        public void testLIFO() throws Exception {
681            pool.setLifo(true);
682            final Object key = "key";
683            pool.addObject(key); // "key0"
684            pool.addObject(key); // "key1"
685            pool.addObject(key); // "key2"
686            assertEquals("Youngest", "key2", pool.borrowObject(key));
687            assertEquals("Middle", "key1", pool.borrowObject(key));
688            assertEquals("Oldest", "key0", pool.borrowObject(key));
689            assertEquals("new-3", "key3", pool.borrowObject(key));
690            pool.returnObject(key, "r");
691            assertEquals("returned", "r", pool.borrowObject(key));
692            assertEquals("new-4", "key4", pool.borrowObject(key));
693        }
694        
695        /**
696         * Test to make sure evictor visits least recently used objects first,
697         * regardless of FIFO/LIFO 
698         * 
699         * JIRA: POOL-86
700         */ 
701        public void testEvictionOrder() throws Exception {
702            checkEvictionOrder(false);
703            checkEvictionOrder(true);
704        }
705        
706        private void checkEvictionOrder(boolean lifo) throws Exception {
707            SimpleFactory factory = new SimpleFactory();
708            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(factory);
709            pool.setNumTestsPerEvictionRun(2);
710            pool.setMinEvictableIdleTimeMillis(100);
711            pool.setLifo(lifo);
712            
713            for (int i = 0; i < 3; i ++) {
714                Integer key = new Integer(i);
715                for (int j = 0; j < 5; j++) {
716                    pool.addObject(key);
717                }
718            }
719            
720            // Make all evictable
721            Thread.sleep(200);
722            
723            /* 
724             * Initial state (Key, Object) pairs in order of age:
725             * 
726             * (0,0), (0,1), (0,2), (0,3), (0,4)
727             * (1,5), (1,6), (1,7), (1,8), (1,9)
728             * (2,10), (2,11), (2,12), (2,13), (2,14)
729             */
730            
731            pool.evict(); // Kill (0,0),(0,1)
732            assertEquals(3, pool.getNumIdle(zero));
733            Object obj = pool.borrowObject(zero);
734            assertTrue(lifo ? obj.equals("04") : obj.equals("02"));
735            assertEquals(2, pool.getNumIdle(zero));
736            obj = pool.borrowObject(zero);
737            assertTrue(obj.equals("03"));
738            assertEquals(1, pool.getNumIdle(zero));
739            
740            pool.evict(); // Kill remaining 0 survivor and (1,5)
741            assertEquals(0, pool.getNumIdle(zero));
742            assertEquals(4, pool.getNumIdle(one));
743            obj = pool.borrowObject(one);
744            assertTrue(lifo ? obj.equals("19") : obj.equals("16"));
745            assertEquals(3, pool.getNumIdle(one));
746            obj = pool.borrowObject(one);
747            assertTrue(lifo ? obj.equals("18") : obj.equals("17"));
748            assertEquals(2, pool.getNumIdle(one));
749            
750            pool.evict(); // Kill remaining 1 survivors
751            assertEquals(0, pool.getNumIdle(one));
752            pool.evict(); // Kill (2,10), (2,11)
753            assertEquals(3, pool.getNumIdle(two));
754            obj = pool.borrowObject(two);
755            assertTrue(lifo ? obj.equals("214") : obj.equals("212"));
756            assertEquals(2, pool.getNumIdle(two));
757            pool.evict(); // All dead now
758            assertEquals(0, pool.getNumIdle(two));  
759            
760            pool.evict(); // Should do nothing - make sure no exception
761            pool.evict();
762            
763            // Reload
764            pool.setMinEvictableIdleTimeMillis(500);
765            factory.counter = 0; // Reset counter
766            for (int i = 0; i < 3; i ++) {
767                Integer key = new Integer(i);
768                for (int j = 0; j < 5; j++) {
769                    pool.addObject(key);
770                }
771                Thread.sleep(200);
772            }
773            
774            // 0's are evictable, others not 
775            pool.evict(); // Kill (0,0),(0,1)
776            assertEquals(3, pool.getNumIdle(zero));
777            pool.evict(); // Kill (0,2),(0,3)
778            assertEquals(1, pool.getNumIdle(zero));
779            pool.evict(); // Kill (0,4), leave (1,5)
780            assertEquals(0, pool.getNumIdle(zero));
781            assertEquals(5, pool.getNumIdle(one));
782            assertEquals(5, pool.getNumIdle(two));
783            pool.evict(); // (1,6), (1,7)
784            assertEquals(5, pool.getNumIdle(one));
785            assertEquals(5, pool.getNumIdle(two));
786            pool.evict(); // (1,8), (1,9)
787            assertEquals(5, pool.getNumIdle(one));
788            assertEquals(5, pool.getNumIdle(two));
789            pool.evict(); // (2,10), (2,11)
790            assertEquals(5, pool.getNumIdle(one));
791            assertEquals(5, pool.getNumIdle(two));
792            pool.evict(); // (2,12), (2,13)
793            assertEquals(5, pool.getNumIdle(one));
794            assertEquals(5, pool.getNumIdle(two));
795            pool.evict(); // (2,14), (1,5)
796            assertEquals(5, pool.getNumIdle(one));
797            assertEquals(5, pool.getNumIdle(two));
798            Thread.sleep(200); // Ones now timed out
799            pool.evict(); // kill (1,6), (1,7) - (1,5) missed
800            assertEquals(3, pool.getNumIdle(one));
801            assertEquals(5, pool.getNumIdle(two));
802            obj = pool.borrowObject(one);
803            assertTrue(lifo ? obj.equals("19") : obj.equals("15"));  
804        }
805        
806        
807        /**
808         * Verifies that the evictor visits objects in expected order
809         * and frequency. 
810         */
811        public void testEvictorVisiting() throws Exception {
812            checkEvictorVisiting(true);
813            checkEvictorVisiting(false);  
814        }
815        
816        private void checkEvictorVisiting(boolean lifo) throws Exception {
817            VisitTrackerFactory factory = new VisitTrackerFactory();
818            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(factory);
819            pool.setNumTestsPerEvictionRun(2);
820            pool.setMinEvictableIdleTimeMillis(-1);
821            pool.setTestWhileIdle(true);
822            pool.setLifo(lifo);
823            pool.setTestOnReturn(false);
824            pool.setTestOnBorrow(false);
825            for (int i = 0; i < 3; i ++) {
826                factory.resetId();
827                Integer key = new Integer(i);
828                for (int j = 0; j < 8; j++) {
829                    pool.addObject(key);
830                }
831            }
832            pool.evict(); // Visit oldest 2 - 00 and 01
833            Object obj = pool.borrowObject(zero);
834            pool.returnObject(zero, obj);
835            obj = pool.borrowObject(zero);
836            pool.returnObject(zero, obj);
837            //  borrow, return, borrow, return 
838            //  FIFO will move 0 and 1 to end - 2,3,4,5,6,7,0,1
839            //  LIFO, 7 out, then in, then out, then in - 7,6,5,4,3,2,1,0
840            pool.evict();  // Should visit 02 and 03 in either case
841            for (int i = 0; i < 8; i++) {
842                VisitTracker tracker = (VisitTracker) pool.borrowObject(zero);    
843                if (tracker.getId() >= 4) {
844                    assertEquals("Unexpected instance visited " + tracker.getId(),
845                            0, tracker.getValidateCount());
846                } else {
847                    assertEquals("Instance " +  tracker.getId() + 
848                            " visited wrong number of times.",
849                            1, tracker.getValidateCount());
850                }
851            } 
852            // 0's are all out
853            
854            pool.setNumTestsPerEvictionRun(3);
855            
856            pool.evict(); // 10, 11, 12
857            pool.evict(); // 13, 14, 15
858            
859            obj = pool.borrowObject(one);
860            pool.returnObject(one, obj);
861            obj = pool.borrowObject(one);
862            pool.returnObject(one, obj);
863            obj = pool.borrowObject(one);
864            pool.returnObject(one, obj);
865            // borrow, return, borrow, return 
866            //  FIFO 3,4,5,^,6,7,0,1,2
867            //  LIFO 7,6,^,5,4,3,2,1,0
868            // In either case, pointer should be at 6
869            pool.evict();
870            // LIFO - 16, 17, 20
871            // FIFO - 16, 17, 10
872            pool.evict();
873            // LIFO - 21, 22, 23
874            // FIFO - 11, 12, 20
875            pool.evict();
876            // LIFO - 24, 25, 26
877            // FIFO - 21, 22, 23
878            pool.evict();
879            // LIFO - 27, skip, 10
880            // FIFO - 24, 25, 26
881            for (int i = 0; i < 8; i++) {
882                VisitTracker tracker = (VisitTracker) pool.borrowObject(one);    
883                if ((lifo && tracker.getId() > 0) || 
884                        (!lifo && tracker.getId() > 2)) {
885                    assertEquals("Instance " +  tracker.getId() + 
886                            " visited wrong number of times.",
887                            1, tracker.getValidateCount());
888                } else {
889                    assertEquals("Instance " +  tracker.getId() + 
890                            " visited wrong number of times.",
891                            2, tracker.getValidateCount());
892                }
893            } 
894            
895            // Randomly generate some pools with random numTests
896            // and make sure evictor cycles through elements appropriately
897            int[] smallPrimes = {2, 3, 5, 7};
898            Random random = new Random();
899            random.setSeed(System.currentTimeMillis());
900            pool.setMaxIdle(-1);
901            for (int i = 0; i < smallPrimes.length; i++) {
902                pool.setNumTestsPerEvictionRun(smallPrimes[i]);
903                for (int j = 0; j < 5; j++) {// Try the tests a few times
904                    pool.clear();
905                    assertEquals("NumIdle should be zero after clearing the pool",0,pool.getNumIdle());
906                    int zeroLength = 10 + random.nextInt(20);
907                    for (int k = 0; k < zeroLength; k++) {
908                        pool.addObject(zero);
909                    }
910                    int oneLength = 10 + random.nextInt(20);
911                    for (int k = 0; k < oneLength; k++) {
912                        pool.addObject(one);
913                    }
914                    int twoLength = 10 + random.nextInt(20);
915                    for (int k = 0; k < twoLength; k++) {
916                        pool.addObject(two);
917                    }
918                    
919                    // Choose a random number of evictor runs
920                    int runs = 10 + random.nextInt(50);
921                    for (int k = 0; k < runs; k++) {
922                        pool.evict();
923                    }
924                    
925                    // Total instances in pool
926                    int totalInstances = zeroLength + oneLength + twoLength;
927                    
928                    // Number of times evictor should have cycled through pools
929                    int cycleCount = (runs * pool.getNumTestsPerEvictionRun())
930                        / totalInstances;
931                    
932                    // Look at elements and make sure they are visited cycleCount
933                    // or cycleCount + 1 times
934                    VisitTracker tracker = null;
935                    int visitCount = 0;
936                    for (int k = 0; k < zeroLength; k++) {
937                        tracker = (VisitTracker) pool.borrowObject(zero); 
938                        visitCount = tracker.getValidateCount();
939                        if (visitCount < cycleCount || visitCount > cycleCount + 1){
940                            fail(formatSettings("ZERO", "runs", runs, "lifo", lifo, "i", i, "j", j,
941                                    "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
942                                    "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
943                        }
944                    }
945                    for (int k = 0; k < oneLength; k++) {
946                        tracker = (VisitTracker) pool.borrowObject(one); 
947                        visitCount = tracker.getValidateCount();
948                        if (visitCount < cycleCount || visitCount > cycleCount + 1){
949                            fail(formatSettings("ONE", "runs", runs, "lifo", lifo, "i", i, "j", j,
950                                    "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
951                                    "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
952                        }
953                    }
954                    int visits[] = new int[twoLength];
955                    for (int k = 0; k < twoLength; k++) {
956                        tracker = (VisitTracker) pool.borrowObject(two); 
957                        visitCount = tracker.getValidateCount();
958                        visits[k] = visitCount;
959                        if (visitCount < cycleCount || visitCount > cycleCount + 1){
960                            StringBuffer sb = new StringBuffer("Visits:");
961                            for (int l = 0; l <= k; l++){
962                                sb.append(visits[l]).append(' ');
963                            }
964                            fail(formatSettings("TWO "+sb.toString(), "runs", runs, "lifo", lifo, "i", i, "j", j,
965                                    "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
966                                    "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
967                        }
968                    }
969                }
970            }
971        }
972        
973        public void testConstructors() {
974            
975            // Make constructor arguments all different from defaults
976            int maxActive = 1;
977            int maxIdle = 2;
978            long maxWait = 3;
979            int minIdle = 4;
980            int maxTotal = 5;
981            long minEvictableIdleTimeMillis = 6;
982            int numTestsPerEvictionRun = 7;
983            boolean testOnBorrow = true;
984            boolean testOnReturn = true;
985            boolean testWhileIdle = true;
986            long timeBetweenEvictionRunsMillis = 8;
987            byte whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
988            boolean lifo = false;
989            
990            GenericKeyedObjectPool pool = new GenericKeyedObjectPool();
991            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE, pool.getMaxActive());
992            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
993            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_WAIT, pool.getMaxWait());
994            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
995            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
996            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
997                    pool.getMinEvictableIdleTimeMillis());
998            assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
999                    pool.getNumTestsPerEvictionRun());
1000            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
1001                    pool.getTestOnBorrow());
1002            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
1003                    pool.getTestOnReturn());
1004            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
1005                    pool.getTestWhileIdle());
1006            assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
1007                    pool.getTimeBetweenEvictionRunsMillis());
1008            assertEquals(GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,
1009                    pool.getWhenExhaustedAction());
1010            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1011            
1012            GenericKeyedObjectPool.Config config = new GenericKeyedObjectPool.Config();
1013            config.lifo = lifo;
1014            config.maxActive = maxActive;
1015            config.maxIdle = maxIdle;
1016            config.minIdle = minIdle;
1017            config.maxTotal = maxTotal;
1018            config.maxWait = maxWait;
1019            config.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
1020            config.numTestsPerEvictionRun = numTestsPerEvictionRun;
1021            config.testOnBorrow = testOnBorrow;
1022            config.testOnReturn = testOnReturn;
1023            config.testWhileIdle = testWhileIdle;
1024            config.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
1025            config.whenExhaustedAction = whenExhaustedAction;
1026            pool = new GenericKeyedObjectPool(null, config);
1027            assertEquals(maxActive, pool.getMaxActive());
1028            assertEquals(maxIdle, pool.getMaxIdle());
1029            assertEquals(maxWait, pool.getMaxWait());
1030            assertEquals(minIdle, pool.getMinIdle());
1031            assertEquals(maxTotal, pool.getMaxTotal());
1032            assertEquals(minEvictableIdleTimeMillis,
1033                    pool.getMinEvictableIdleTimeMillis());
1034            assertEquals(numTestsPerEvictionRun, pool.getNumTestsPerEvictionRun());
1035            assertEquals(testOnBorrow,pool.getTestOnBorrow());
1036            assertEquals(testOnReturn,pool.getTestOnReturn());
1037            assertEquals(testWhileIdle,pool.getTestWhileIdle());
1038            assertEquals(timeBetweenEvictionRunsMillis,
1039                    pool.getTimeBetweenEvictionRunsMillis());
1040            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1041            assertEquals(lifo, pool.getLifo());
1042            
1043            pool = new GenericKeyedObjectPool(null, maxActive);
1044            assertEquals(maxActive, pool.getMaxActive());
1045            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
1046            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_WAIT, pool.getMaxWait());
1047            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
1048            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
1049            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
1050                    pool.getMinEvictableIdleTimeMillis());
1051            assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
1052                    pool.getNumTestsPerEvictionRun());
1053            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
1054                    pool.getTestOnBorrow());
1055            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
1056                    pool.getTestOnReturn());
1057            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
1058                    pool.getTestWhileIdle());
1059            assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
1060                    pool.getTimeBetweenEvictionRunsMillis());
1061            assertEquals(GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,
1062                    pool.getWhenExhaustedAction());
1063            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1064            
1065            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction, maxWait);
1066            assertEquals(maxActive, pool.getMaxActive());
1067            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
1068            assertEquals(maxWait, pool.getMaxWait());
1069            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
1070            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
1071            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
1072                    pool.getMinEvictableIdleTimeMillis());
1073            assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
1074                    pool.getNumTestsPerEvictionRun());
1075            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
1076                    pool.getTestOnBorrow());
1077            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
1078                    pool.getTestOnReturn());
1079            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
1080                    pool.getTestWhileIdle());
1081            assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
1082                    pool.getTimeBetweenEvictionRunsMillis());
1083            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1084            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1085            
1086            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction,
1087                       maxWait, testOnBorrow, testOnReturn);
1088            assertEquals(maxActive, pool.getMaxActive());
1089            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
1090            assertEquals(maxWait, pool.getMaxWait());
1091            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
1092            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
1093            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
1094                    pool.getMinEvictableIdleTimeMillis());
1095            assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
1096                    pool.getNumTestsPerEvictionRun());
1097            assertEquals(testOnBorrow,pool.getTestOnBorrow());
1098            assertEquals(testOnReturn,pool.getTestOnReturn());
1099            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
1100                    pool.getTestWhileIdle());
1101            assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
1102                    pool.getTimeBetweenEvictionRunsMillis());
1103            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1104            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1105            
1106            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction,
1107                    maxWait, maxIdle);
1108            assertEquals(maxActive, pool.getMaxActive());
1109            assertEquals(maxIdle, pool.getMaxIdle());
1110            assertEquals(maxWait, pool.getMaxWait());
1111            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
1112            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
1113            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
1114                    pool.getMinEvictableIdleTimeMillis());
1115            assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
1116                    pool.getNumTestsPerEvictionRun());
1117            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
1118                    pool.getTestOnBorrow());
1119            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
1120                    pool.getTestOnReturn());
1121            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
1122                    pool.getTestWhileIdle());
1123            assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
1124                    pool.getTimeBetweenEvictionRunsMillis());
1125            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1126            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1127    
1128            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction,
1129                    maxWait, maxIdle, testOnBorrow, testOnReturn);
1130            assertEquals(maxActive, pool.getMaxActive());
1131            assertEquals(maxIdle, pool.getMaxIdle());
1132            assertEquals(maxWait, pool.getMaxWait());
1133            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
1134            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
1135            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
1136                    pool.getMinEvictableIdleTimeMillis());
1137            assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
1138                    pool.getNumTestsPerEvictionRun());
1139            assertEquals(testOnBorrow, pool.getTestOnBorrow());
1140            assertEquals(testOnReturn, pool.getTestOnReturn());
1141            assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
1142                    pool.getTestWhileIdle());
1143            assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
1144                    pool.getTimeBetweenEvictionRunsMillis());
1145            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1146            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1147    
1148            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction,
1149                    maxWait, maxIdle, testOnBorrow, testOnReturn,
1150                    timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
1151                    minEvictableIdleTimeMillis, testWhileIdle);
1152            assertEquals(maxActive, pool.getMaxActive());
1153            assertEquals(maxIdle, pool.getMaxIdle());
1154            assertEquals(maxWait, pool.getMaxWait());
1155            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
1156            assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
1157            assertEquals(minEvictableIdleTimeMillis,
1158                    pool.getMinEvictableIdleTimeMillis());
1159            assertEquals(numTestsPerEvictionRun,
1160                    pool.getNumTestsPerEvictionRun());
1161            assertEquals(testOnBorrow, pool.getTestOnBorrow());
1162            assertEquals(testOnReturn, pool.getTestOnReturn());
1163            assertEquals(testWhileIdle,
1164                    pool.getTestWhileIdle());
1165            assertEquals(timeBetweenEvictionRunsMillis,
1166                    pool.getTimeBetweenEvictionRunsMillis());
1167            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1168            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1169            
1170            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction,
1171                    maxWait, maxIdle, maxTotal, testOnBorrow, testOnReturn,
1172                    timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
1173                    minEvictableIdleTimeMillis, testWhileIdle);
1174            assertEquals(maxActive, pool.getMaxActive());
1175            assertEquals(maxIdle, pool.getMaxIdle());
1176            assertEquals(maxWait, pool.getMaxWait());
1177            assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
1178            assertEquals(maxTotal, pool.getMaxTotal());
1179            assertEquals(minEvictableIdleTimeMillis,
1180                    pool.getMinEvictableIdleTimeMillis());
1181            assertEquals(numTestsPerEvictionRun,
1182                    pool.getNumTestsPerEvictionRun());
1183            assertEquals(testOnBorrow, pool.getTestOnBorrow());
1184            assertEquals(testOnReturn, pool.getTestOnReturn());
1185            assertEquals(testWhileIdle,
1186                    pool.getTestWhileIdle());
1187            assertEquals(timeBetweenEvictionRunsMillis,
1188                    pool.getTimeBetweenEvictionRunsMillis());
1189            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1190            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1191            
1192            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction,
1193                    maxWait, maxIdle, maxTotal, minIdle, testOnBorrow, testOnReturn,
1194                    timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
1195                    minEvictableIdleTimeMillis, testWhileIdle);
1196            assertEquals(maxActive, pool.getMaxActive());
1197            assertEquals(maxIdle, pool.getMaxIdle());
1198            assertEquals(maxWait, pool.getMaxWait());
1199            assertEquals(minIdle, pool.getMinIdle());
1200            assertEquals(maxTotal, pool.getMaxTotal());
1201            assertEquals(minEvictableIdleTimeMillis,
1202                    pool.getMinEvictableIdleTimeMillis());
1203            assertEquals(numTestsPerEvictionRun,
1204                    pool.getNumTestsPerEvictionRun());
1205            assertEquals(testOnBorrow, pool.getTestOnBorrow());
1206            assertEquals(testOnReturn, pool.getTestOnReturn());
1207            assertEquals(testWhileIdle,
1208                    pool.getTestWhileIdle());
1209            assertEquals(timeBetweenEvictionRunsMillis,
1210                    pool.getTimeBetweenEvictionRunsMillis());
1211            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1212            assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
1213            
1214            pool = new GenericKeyedObjectPool(null, maxActive, whenExhaustedAction,
1215                    maxWait, maxIdle, maxTotal, minIdle, testOnBorrow, testOnReturn,
1216                    timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
1217                    minEvictableIdleTimeMillis, testWhileIdle, lifo);
1218            assertEquals(maxActive, pool.getMaxActive());
1219            assertEquals(maxIdle, pool.getMaxIdle());
1220            assertEquals(maxWait, pool.getMaxWait());
1221            assertEquals(minIdle, pool.getMinIdle());
1222            assertEquals(maxTotal, pool.getMaxTotal());
1223            assertEquals(minEvictableIdleTimeMillis,
1224                    pool.getMinEvictableIdleTimeMillis());
1225            assertEquals(numTestsPerEvictionRun,
1226                    pool.getNumTestsPerEvictionRun());
1227            assertEquals(testOnBorrow, pool.getTestOnBorrow());
1228            assertEquals(testOnReturn, pool.getTestOnReturn());
1229            assertEquals(testWhileIdle,
1230                    pool.getTestWhileIdle());
1231            assertEquals(timeBetweenEvictionRunsMillis,
1232                    pool.getTimeBetweenEvictionRunsMillis());
1233            assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
1234            assertEquals(lifo, pool.getLifo());  
1235        }
1236        
1237        public void testExceptionOnPassivateDuringReturn() throws Exception {
1238            SimpleFactory factory = new SimpleFactory();        
1239            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(factory);
1240            Object obj = pool.borrowObject("one");
1241            factory.setThrowExceptionOnPassivate(true);
1242            pool.returnObject("one", obj);
1243            assertEquals(0,pool.getNumIdle());
1244            pool.close();
1245        }
1246        
1247        public void testExceptionOnDestroyDuringBorrow() throws Exception {
1248            SimpleFactory factory = new SimpleFactory(); 
1249            factory.setThrowExceptionOnDestroy(true);
1250            factory.setValidationEnabled(true);
1251            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(factory);
1252            pool.setTestOnBorrow(true);
1253            pool.borrowObject("one");
1254            factory.setValid(false); // Make validation fail on next borrow attempt
1255            try {
1256                pool.borrowObject("one");
1257                fail("Expecting NoSuchElementException");
1258            } catch (NoSuchElementException ex) {
1259                // expected
1260            }
1261            assertEquals(1, pool.getNumActive("one"));
1262            assertEquals(0, pool.getNumIdle("one"));
1263            assertEquals(1, pool.getNumActive());
1264            assertEquals(0, pool.getNumIdle());
1265        }
1266        
1267        public void testExceptionOnDestroyDuringReturn() throws Exception {
1268            SimpleFactory factory = new SimpleFactory(); 
1269            factory.setThrowExceptionOnDestroy(true);
1270            factory.setValidationEnabled(true);
1271            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(factory);
1272            pool.setTestOnReturn(true);
1273            Object obj1 = pool.borrowObject("one");
1274            pool.borrowObject("one");
1275            factory.setValid(false); // Make validation fail
1276            pool.returnObject("one", obj1);
1277            assertEquals(1, pool.getNumActive("one"));
1278            assertEquals(0, pool.getNumIdle("one"));
1279            assertEquals(1, pool.getNumActive());
1280            assertEquals(0, pool.getNumIdle());
1281        }
1282        
1283        public void testExceptionOnActivateDuringBorrow() throws Exception {
1284            SimpleFactory factory = new SimpleFactory(); 
1285            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(factory);
1286            Object obj1 = pool.borrowObject("one");
1287            Object obj2 = pool.borrowObject("one");
1288            pool.returnObject("one", obj1);
1289            pool.returnObject("one", obj2);
1290            factory.setThrowExceptionOnActivate(true);
1291            factory.setEvenValid(false);  
1292            // Activation will now throw every other time
1293            // First attempt throws, but loop continues and second succeeds
1294            Object obj = pool.borrowObject("one");
1295            assertEquals(1, pool.getNumActive("one"));
1296            assertEquals(0, pool.getNumIdle("one"));
1297            assertEquals(1, pool.getNumActive());
1298            assertEquals(0, pool.getNumIdle());
1299            
1300            pool.returnObject("one", obj);
1301            factory.setValid(false);
1302            // Validation will now fail on activation when borrowObject returns
1303            // an idle instance, and then when attempting to create a new instance
1304            try {
1305                pool.borrowObject("one");
1306                fail("Expecting NoSuchElementException");
1307            } catch (NoSuchElementException ex) {
1308                // expected
1309            }
1310            assertEquals(0, pool.getNumActive("one"));
1311            assertEquals(0, pool.getNumIdle("one"));
1312            assertEquals(0, pool.getNumActive());
1313            assertEquals(0, pool.getNumIdle());
1314        }
1315        
1316        public void testBlockedKeyDoesNotBlockPool() throws Exception {
1317            SimpleFactory factory = new SimpleFactory();
1318            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(factory);
1319            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
1320            pool.setMaxWait(5000);
1321            pool.setMaxActive(1);
1322            pool.setMaxTotal(-1);
1323            pool.borrowObject("one");
1324            long start = System.currentTimeMillis();
1325            // Needs to be in a separate thread as this will block
1326            Runnable simple = new SimpleTestThread(pool, "one");
1327            (new Thread(simple)).start();
1328            // This should be almost instant. If it isn't it means this thread got
1329            // stuck behind the thread created above which is bad.
1330            // Give other thread a chance to start
1331            Thread.sleep(1000);
1332            pool.borrowObject("two");
1333            long end = System.currentTimeMillis();
1334            // If it fails it will be more than 4000ms (5000 less the 1000 sleep)
1335            // If it passes it should be almost instant
1336            // Use 3000ms as the threshold - should avoid timing issues on most
1337            // (all? platforms)
1338            assertTrue ((end-start) < 4000);
1339            
1340        }
1341    
1342        /*
1343         * Very simple test thread that just tries to borrow an object from
1344         * the provided pool with the specified key and returns it
1345         */
1346        static class SimpleTestThread implements Runnable {
1347            private final KeyedObjectPool _pool;
1348            private final String _key;
1349            
1350            public SimpleTestThread(KeyedObjectPool pool, String key) {
1351                _pool = pool;
1352                _key = key;
1353            }
1354    
1355            public void run() {
1356                try {
1357                    Object obj = _pool.borrowObject(_key);
1358                    _pool.returnObject(_key, obj);
1359                } catch (Exception e) {
1360                    // Ignore
1361                }
1362            }
1363        }
1364        
1365        static class TestThread implements Runnable {
1366            private final java.util.Random _random = new java.util.Random();
1367            
1368            // Thread config items
1369            private final KeyedObjectPool _pool;
1370            private final int _iter;
1371            private final int _delay;
1372    
1373            private volatile boolean _complete = false;
1374            private volatile boolean _failed = false;
1375            private volatile Exception _exception;
1376    
1377            public TestThread(KeyedObjectPool pool) {
1378                this(pool, 100, 50);
1379            }
1380    
1381            public TestThread(KeyedObjectPool pool, int iter) {
1382                this(pool, iter, 50);
1383            }
1384    
1385            public TestThread(KeyedObjectPool pool, int iter, int delay) {
1386                _pool = pool;
1387                _iter = iter;
1388                _delay = delay;
1389            }
1390    
1391            public boolean complete() {
1392                return _complete;
1393            }
1394    
1395            public boolean failed() {
1396                return _failed;
1397            }
1398    
1399            public void run() {
1400                for(int i=0;i<_iter;i++) {
1401                    String key = String.valueOf(_random.nextInt(3));
1402                    try {
1403                        Thread.sleep(_random.nextInt(_delay));
1404                    } catch(InterruptedException e) {
1405                        // ignored
1406                    }
1407                    Object obj = null;
1408                    try {
1409                        obj = _pool.borrowObject(key);
1410                    } catch(Exception e) {
1411                        _exception = e;
1412                        _failed = true;
1413                        _complete = true;
1414                        break;
1415                    }
1416    
1417                    try {
1418                        Thread.sleep(_random.nextInt(_delay));
1419                    } catch(InterruptedException e) {
1420                        // ignored
1421                    }
1422                    try {
1423                        _pool.returnObject(key,obj);
1424                    } catch(Exception e) {
1425                        _exception = e;
1426                        _failed = true;
1427                        _complete = true;
1428                        break;
1429                    }
1430                }
1431                _complete = true;
1432            }
1433        }
1434    
1435        static class SimpleFactory implements KeyedPoolableObjectFactory {
1436            public SimpleFactory() {
1437                this(true);
1438            }
1439            public SimpleFactory(boolean valid) {
1440                this.valid = valid;
1441            }
1442            public Object makeObject(Object key) {
1443                synchronized(this) {
1444                    activeCount++;
1445                    if (activeCount > maxActive) {
1446                        throw new IllegalStateException(
1447                            "Too many active instances: " + activeCount);
1448                    }
1449                }
1450                return String.valueOf(key) + String.valueOf(counter++);
1451            }
1452            public void destroyObject(Object key, Object obj) throws Exception {
1453                doWait(destroyLatency);
1454                synchronized(this) {
1455                    activeCount--;
1456                }
1457                if (exceptionOnDestroy) {
1458                    throw new Exception();
1459                }
1460            }
1461            public boolean validateObject(Object key, Object obj) {
1462                if (enableValidation) { 
1463                    return validateCounter++%2 == 0 ? evenValid : oddValid; 
1464                } else {
1465                    return valid;
1466                }
1467            }
1468            public void activateObject(Object key, Object obj) throws Exception {
1469                if (exceptionOnActivate) {
1470                    if (!(validateCounter++%2 == 0 ? evenValid : oddValid)) {
1471                        throw new Exception();
1472                    }
1473                }
1474            }
1475            public void passivateObject(Object key, Object obj) throws Exception {
1476                if (exceptionOnPassivate) {
1477                    throw new Exception();
1478                }
1479            }
1480            
1481            public void setMaxActive(int maxActive) {
1482                this.maxActive = maxActive;
1483            }
1484            public void setDestroyLatency(long destroyLatency) {
1485                this.destroyLatency = destroyLatency;
1486            }
1487            public void setValidationEnabled(boolean b) {
1488                enableValidation = b;
1489            }
1490            void setEvenValid(boolean valid) {
1491                evenValid = valid;
1492            }
1493            void setValid(boolean valid) {
1494                evenValid = valid;
1495                oddValid = valid;
1496            }
1497            
1498            public void setThrowExceptionOnActivate(boolean b) {
1499                exceptionOnActivate = b;
1500            }
1501            
1502            public void setThrowExceptionOnDestroy(boolean b) {
1503                exceptionOnDestroy = b;
1504            }
1505            
1506            public void setThrowExceptionOnPassivate(boolean b) {
1507                exceptionOnPassivate = b;
1508            }
1509            
1510            int counter = 0;
1511            boolean valid;
1512            
1513            int activeCount = 0;
1514            int validateCounter = 0;
1515            boolean evenValid = true;
1516            boolean oddValid = true;
1517            boolean enableValidation = false;
1518            long destroyLatency = 0;
1519            int maxActive = Integer.MAX_VALUE;
1520            boolean exceptionOnPassivate = false;
1521            boolean exceptionOnActivate = false;
1522            boolean exceptionOnDestroy = false;
1523            
1524            private void doWait(long latency) {
1525                try {
1526                    Thread.sleep(latency);
1527                } catch (InterruptedException ex) {
1528                    // ignore
1529                }
1530            }
1531        }
1532    
1533        protected boolean isLifo() {
1534            return true;
1535        }
1536    
1537        protected boolean isFifo() {
1538            return false;
1539        }
1540    
1541        private String getExceptionTrace(Throwable t){
1542            StringWriter sw = new StringWriter();
1543            t.printStackTrace(new PrintWriter(sw));
1544            return sw.toString();
1545        }
1546        
1547        private String formatSettings(String title, String s, int i, String s0, boolean b0, String s1, int i1, String s2, int i2, String s3, int i3,
1548                String s4, int i4, String s5, int i5, String s6, int i6, int zeroLength, int oneLength, int twoLength){
1549            StringBuffer sb = new StringBuffer(80);
1550            sb.append(title).append(' ');
1551            sb.append(s).append('=').append(i).append(' ');
1552            sb.append(s0).append('=').append(b0).append(' ');
1553            sb.append(s1).append('=').append(i1).append(' ');
1554            sb.append(s2).append('=').append(i2).append(' ');
1555            sb.append(s3).append('=').append(i3).append(' ');
1556            sb.append(s4).append('=').append(i4).append(' ');
1557            sb.append(s5).append('=').append(i5).append(' ');
1558            sb.append(s6).append('=').append(i6).append(' ');
1559            sb.append("Lengths=").append(zeroLength).append(',').append(oneLength).append(',').append(twoLength).append(' ');
1560            return sb.toString();
1561        }
1562        
1563    }
1564    
1565