001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.pool.impl;
019    
020    import java.util.ArrayList;
021    import java.util.BitSet;
022    import java.util.List;
023    import java.util.NoSuchElementException;
024    
025    import org.apache.commons.pool.ObjectPool;
026    import org.apache.commons.pool.PoolableObjectFactory;
027    import org.apache.commons.pool.TestBaseObjectPool;
028    
029    /**
030     * @author Rodney Waldhoff
031     * @author Dirk Verbeeck
032     * @author Sandy McArthur
033     * @version $Revision: 960644 $ $Date: 2010-07-05 10:15:07 -0700 (Mon, 05 Jul 2010) $
034     */
035    public class TestStackObjectPool extends TestBaseObjectPool {
036        public TestStackObjectPool(String testName) {
037            super(testName);
038        }
039    
040        protected ObjectPool makeEmptyPool(int mincap) {
041            return new StackObjectPool(new SimpleFactory());
042        }
043    
044        protected ObjectPool makeEmptyPool(final PoolableObjectFactory factory) {
045            return new StackObjectPool(factory);
046        }
047    
048        protected Object getNthObject(int n) {
049            return String.valueOf(n);
050        }
051    
052        public void testIdleCap() throws Exception {
053            ObjectPool pool = makeEmptyPool(8);
054            Object[] active = new Object[100];
055            for(int i=0;i<100;i++) {
056                active[i] = pool.borrowObject();
057            }
058            assertEquals(100,pool.getNumActive());
059            assertEquals(0,pool.getNumIdle());
060            for(int i=0;i<100;i++) {
061                pool.returnObject(active[i]);
062                assertEquals(99 - i,pool.getNumActive());
063                assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle());
064            }
065        }
066    
067        /**
068         * @deprecated - to be removed in pool 2.0
069         */
070        public void testPoolWithNullFactory() throws Exception {
071            ObjectPool pool = new StackObjectPool(10);
072            for(int i=0;i<10;i++) {
073                pool.returnObject(new Integer(i));
074            }
075            for(int j=0;j<3;j++) {
076                Integer[] borrowed = new Integer[10];
077                BitSet found = new BitSet();
078                for(int i=0;i<10;i++) {
079                    borrowed[i] = (Integer)(pool.borrowObject());
080                    assertNotNull(borrowed);
081                    assertTrue(!found.get(borrowed[i].intValue()));
082                    found.set(borrowed[i].intValue());
083                }
084                for(int i=0;i<10;i++) {
085                    pool.returnObject(borrowed[i]);
086                }
087            }
088            pool.invalidateObject(pool.borrowObject());
089            pool.invalidateObject(pool.borrowObject());
090            pool.clear();        
091        }
092        
093        /**
094         * @deprecated - to be removed in pool 2.0
095         */
096        public void testBorrowFromEmptyPoolWithNullFactory() throws Exception {
097            ObjectPool pool = new StackObjectPool();
098            try {
099                pool.borrowObject();
100                fail("Expected NoSuchElementException");
101            } catch(NoSuchElementException e) {
102                // expected
103            }
104        }
105        
106        /**
107         * @deprecated - to be removed in pool 2.0
108         */
109        public void testSetFactory() throws Exception {
110            ObjectPool pool = new StackObjectPool();
111            try {
112                pool.borrowObject();
113                fail("Expected NoSuchElementException");
114            } catch(NoSuchElementException e) {
115                // expected
116            }
117            pool.setFactory(new SimpleFactory());
118            Object obj = pool.borrowObject();
119            assertNotNull(obj);
120            pool.returnObject(obj);
121        }
122    
123        /**
124         * @deprecated - to be removed in pool 2.0
125         */
126        public void testCantResetFactoryWithActiveObjects() throws Exception {
127            ObjectPool pool = new StackObjectPool();
128            pool.setFactory(new SimpleFactory());
129            Object obj = pool.borrowObject();
130            assertNotNull(obj);
131    
132            try {
133                pool.setFactory(new SimpleFactory());
134                fail("Expected IllegalStateException");
135            } catch(IllegalStateException e) {
136                // expected
137            }        
138        }
139        
140        /**
141         * @deprecated - to be removed in pool 2.0
142         */
143        public void testCanResetFactoryWithoutActiveObjects() throws Exception {
144            ObjectPool pool = new StackObjectPool();
145            {
146                pool.setFactory(new SimpleFactory());
147                Object obj = pool.borrowObject();        
148                assertNotNull(obj);
149                pool.returnObject(obj);
150            }
151            {
152                pool.setFactory(new SimpleFactory());
153                Object obj = pool.borrowObject();        
154                assertNotNull(obj);
155                pool.returnObject(obj);
156            }
157        }
158    
159        /**
160         * Verifies that validation failures when borrowing newly created instances
161         * from the pool result in NoSuchElementExceptions and passivation failures
162         * result in instances not being returned to the pool.
163         */
164        public void testBorrowWithSometimesInvalidObjects() throws Exception {
165            SelectiveFactory factory = new SelectiveFactory();
166            factory.setValidateSelectively(true);  // Even numbers fail validation
167            factory.setPassivateSelectively(true); // Multiples of 3 fail passivation
168            ObjectPool pool = new StackObjectPool(factory, 20);
169            Object[] obj = new Object[10];
170            for(int i=0;i<10;i++) {
171                Object object = null;
172                int k = 0;
173                while (object == null && k < 100) { // bound not really needed
174                    try {
175                        k++;
176                        object = pool.borrowObject();
177                        if (((Integer) object).intValue() % 2 == 0) {
178                            fail("Expecting NoSuchElementException");
179                        } else {
180                            obj[i] = object; 
181                        }
182                    } catch (NoSuchElementException ex) {
183                        // Should fail for evens
184                    }
185                }
186                assertEquals("Each time we borrow, get one more active.", i+1, pool.getNumActive());
187            }
188            // 1,3,5,...,19 pass validation, get checked out
189            for(int i=0;i<10;i++) {
190                pool.returnObject(obj[i]);
191                assertEquals("Each time we return, get one less active.", 9-i, pool.getNumActive());
192            }
193            // 3, 9, 15 fail passivation.  
194            assertEquals(7,pool.getNumIdle());
195            assertEquals(new Integer(19), pool.borrowObject());
196            assertEquals(new Integer(17), pool.borrowObject());
197            assertEquals(new Integer(13), pool.borrowObject());
198            assertEquals(new Integer(11), pool.borrowObject());
199            assertEquals(new Integer(7), pool.borrowObject());
200            assertEquals(new Integer(5), pool.borrowObject());
201            assertEquals(new Integer(1), pool.borrowObject());     
202        }
203        
204        /**
205         * Verifies that validation and passivation failures returning objects are handled
206         * properly - instances destroyed and not returned to the pool, but no exceptions propagated.
207         */
208        public void testBorrowReturnWithSometimesInvalidObjects() throws Exception {
209            SelectiveFactory factory = new SelectiveFactory();
210            ObjectPool pool = new StackObjectPool(factory, 20);
211    
212            Object[] obj = new Object[10];
213            for(int i=0;i<10;i++) {
214                obj[i] = pool.borrowObject();
215                assertEquals("Each time we borrow, get one more active.", i+1, pool.getNumActive());
216                
217            }
218            
219            factory.setValidateSelectively(true);  // Even numbers fail validation
220            factory.setPassivateSelectively(true); // Multiples of 3 fail passivation
221    
222            for(int i=0;i<10;i++) {
223                pool.returnObject(obj[i]);
224                assertEquals("Each time we return, get one less active.", 9-i, pool.getNumActive());
225            }
226            // 0,2,4,6,8 fail validation, 3, 9 fail passivation - 3 left.
227            assertEquals(3,pool.getNumIdle());
228        }
229         
230        public void testVariousConstructors() throws Exception {
231            {
232                StackObjectPool pool = new StackObjectPool();
233                assertNotNull(pool);
234            }
235            {
236                StackObjectPool pool = new StackObjectPool(10);
237                assertNotNull(pool);
238            }
239            {
240                StackObjectPool pool = new StackObjectPool(10,5);
241                assertNotNull(pool);
242            }
243            {
244                StackObjectPool pool = new StackObjectPool(null);
245                assertNotNull(pool);
246            }
247            {
248                StackObjectPool pool = new StackObjectPool(null,10);
249                assertNotNull(pool);
250            }
251            {
252                StackObjectPool pool = new StackObjectPool(null,10,5);
253                assertNotNull(pool);
254            }
255        }
256        
257        /**
258         * Verify that out of range constructor arguments are ignored.
259         */
260        public void testMaxIdleInitCapacityOutOfRange() throws Exception {
261            SimpleFactory factory = new SimpleFactory();
262            StackObjectPool pool = new StackObjectPool(factory, -1, 0);
263            assertEquals(pool.getMaxSleeping(), StackObjectPool.DEFAULT_MAX_SLEEPING);
264            pool.addObject();
265            pool.close();
266        }
267    
268        /**
269         * Verifies that when returning objects cause maxSleeping exceeded, oldest instances
270         * are destroyed to make room for returning objects.
271         */
272        public void testReturnObjectDiscardOrder() throws Exception {
273            SelectiveFactory factory = new SelectiveFactory();
274            ObjectPool pool = new StackObjectPool(factory, 3);
275    
276            // borrow more objects than the pool can hold
277            Integer i0 = (Integer)pool.borrowObject();
278            Integer i1 = (Integer)pool.borrowObject();
279            Integer i2 = (Integer)pool.borrowObject();
280            Integer i3 = (Integer)pool.borrowObject();
281    
282            // tests
283            // return as many as the pool will hold.
284            pool.returnObject(i0);
285            pool.returnObject(i1);
286            pool.returnObject(i2);
287    
288            // the pool should now be full.
289            assertEquals("No returned objects should have been destroyed yet.", 0,  factory.getDestroyed().size());
290    
291            // cause the pool to discard a stale object.
292            pool.returnObject(i3);
293            assertEquals("One object should have been destroyed.", 1, factory.getDestroyed().size());
294    
295            // check to see what object was destroyed
296            Integer d = (Integer)factory.getDestroyed().get(0);
297            assertEquals("Destoryed object should be the stalest object.", i0, d);
298        }
299        
300        /**
301         * Verifies that exceptions thrown by factory activate method are not propagated to
302         * the caller.  Objects that throw on activate are destroyed and if none succeed,
303         * the caller gets NoSuchElementException.
304         */
305        public void testExceptionOnActivate() throws Exception {
306            SelectiveFactory factory = new SelectiveFactory();
307            ObjectPool pool = new StackObjectPool(factory);
308            pool.addObject();
309            pool.addObject();
310            factory.setThrowOnActivate(true);
311            try {
312                pool.borrowObject();
313                fail("Expecting NoSuchElementException");
314            } catch (NoSuchElementException ex) {
315                // expected
316            }
317            assertEquals(0, pool.getNumIdle());
318            assertEquals(0, pool.getNumActive());
319        }
320        
321        /**
322         * Verifies that exceptions thrown by factory destroy are swallowed
323         * by both addObject and returnObject.
324         */
325        public void testExceptionOnDestroy() throws Exception {
326            SelectiveFactory factory = new SelectiveFactory();
327            ObjectPool pool = new StackObjectPool(factory, 2);
328            factory.setThrowOnDestroy(true);
329            for (int i = 0; i < 3; i++) {
330                pool.addObject(); // Third one will destroy, exception should be swallowed
331            }
332            assertEquals(2, pool.getNumIdle());
333            
334            Object[] objects = new Object[3];
335            for (int i = 0; i < 3; i++) {
336                objects[i] = pool.borrowObject();
337            }
338            for (int i = 0; i < 3; i++) {
339                pool.returnObject(objects[i]); // Third triggers destroy
340            } 
341            assertEquals(2, pool.getNumIdle());
342        }
343        
344        /**
345         * Verifies that addObject propagates exceptions thrown by
346         * factory passivate, but returnObject swallows these.
347         */
348        public void testExceptionOnPassivate() throws Exception {
349            SelectiveFactory factory = new SelectiveFactory();
350            ObjectPool pool = new StackObjectPool(factory, 2);
351            factory.setThrowOnPassivate(true);
352            
353            // addObject propagates
354            try {
355                pool.addObject();
356                fail("Expecting IntegerFactoryException");
357            } catch (IntegerFactoryException ex) {
358                assertEquals("passivateObject", ex.getType());
359                assertEquals(0, ex.getValue());
360            }
361            assertEquals(0, pool.getNumIdle());
362            
363            // returnObject swallows 
364            Object obj = pool.borrowObject();
365            pool.returnObject(obj);
366            assertEquals(0, pool.getNumIdle());
367        }
368        
369        /**
370         * Verifies that validation exceptions always propagate
371         */
372        public void testExceptionOnValidate() throws Exception {
373            SelectiveFactory factory = new SelectiveFactory();
374            ObjectPool pool = new StackObjectPool(factory, 2);
375            factory.setThrowOnValidate(true);
376            
377            // addObject
378            try {
379                pool.addObject();
380                fail("Expecting IntegerFactoryException");
381            } catch (IntegerFactoryException ex) {
382                assertEquals("validateObject", ex.getType());
383            }
384            assertEquals(0, pool.getNumIdle());
385            
386            // returnObject 
387            factory.setThrowOnValidate(false);
388            Object obj = pool.borrowObject();
389            factory.setThrowOnValidate(true);
390            try {
391                pool.returnObject(obj);
392                fail("Expecting IntegerFactoryException");
393            } catch (IntegerFactoryException ex) {
394                assertEquals("validateObject", ex.getType());
395            }
396            assertEquals(0, pool.getNumIdle());
397            
398            // borrowObject - throws NoSuchElementException
399            try {
400                pool.borrowObject();
401                fail("Expecting NoSuchElementException");
402            } catch (NoSuchElementException ex) {
403                // Expected
404            }
405        }
406        
407        /**
408         * Verifies that exceptions thrown by makeObject are propagated.
409         */
410        public void testExceptionOnMake() throws Exception {
411            SelectiveFactory factory = new SelectiveFactory();
412            factory.setThrowOnMake(true);
413            ObjectPool pool = new StackObjectPool(factory);
414            try {
415                pool.borrowObject();
416                fail("Expecting IntegerFactoryException");
417            } catch (IntegerFactoryException ex) {
418                assertEquals("makeObject", ex.getType());
419            }
420            try {
421                pool.addObject();
422                fail("Expecting IntegerFactoryException");
423            } catch (IntegerFactoryException ex) {
424                assertEquals("makeObject", ex.getType());
425            }
426        }
427        
428        /**
429         * Verifies NoSuchElementException when the factory returns a null object in borrowObject
430         */
431        public void testMakeNull() throws Exception {
432            SelectiveFactory factory = new SelectiveFactory();
433            ObjectPool pool = new StackObjectPool(factory);
434            factory.setMakeNull(true);
435            try {
436                pool.borrowObject();
437                fail("Expecting NoSuchElementException");
438            } catch (NoSuchElementException ex) {
439                // Expected
440            }
441        }
442        
443        /**
444         * Verifies that initIdleCapacity is not a hard limit, but maxIdle is.
445         */
446        public void testInitIdleCapacityExceeded() throws Exception {
447            PoolableObjectFactory factory = new SimpleFactory();
448            ObjectPool pool = new StackObjectPool(factory, 2, 1);
449            pool.addObject();
450            pool.addObject();
451            assertEquals(2, pool.getNumIdle());
452            pool.close();
453            pool = new StackObjectPool(factory, 1, 2);
454            pool.addObject();
455            pool.addObject();
456            assertEquals(1, pool.getNumIdle());
457        }
458        
459        /**
460         * Verifies close contract - idle instances are destroyed, returning instances
461         * are destroyed, add/borrowObject throw IllegalStateException.
462         */
463        public void testClose() throws Exception {
464            SelectiveFactory factory = new SelectiveFactory();
465            ObjectPool pool = new StackObjectPool(factory);
466            pool.addObject(); // 0
467            pool.addObject(); // 1
468            pool.addObject(); // 2
469            Integer two = (Integer) pool.borrowObject();
470            assertEquals(2, two.intValue());
471            pool.close();
472            assertEquals(0, pool.getNumIdle());
473            assertEquals(1, pool.getNumActive());
474            List destroyed = factory.getDestroyed();
475            assertEquals(2, destroyed.size());
476            assertTrue(destroyed.contains(new Integer(0)));
477            assertTrue(destroyed.contains(new Integer(0)));
478            pool.returnObject(two);
479            assertTrue(destroyed.contains(two));
480            try {
481                pool.addObject();
482                fail("Expecting IllegalStateException");
483            } catch (IllegalStateException ex) {
484                // Expected
485            }
486            try {
487                pool.borrowObject();
488                fail("Expecting IllegalStateException");
489            } catch (IllegalStateException ex) {
490                // Expected
491            }
492        }
493    
494        /**
495         * Simple factory that creates Integers. Validation and other factory methods
496         * always succeed.
497         */
498        static class SimpleFactory implements PoolableObjectFactory {
499            int counter = 0;
500            public Object makeObject() { return String.valueOf(counter++); }
501            public void destroyObject(Object obj) { }
502            public boolean validateObject(Object obj) { return true; }
503            public void activateObject(Object obj) { }
504            public void passivateObject(Object obj) { }
505        }
506        
507        /**
508         * Integer factory that fails validation and other factory methods "selectively" and
509         * tracks object destruction.
510         */
511        static class SelectiveFactory implements PoolableObjectFactory {
512            private List destroyed = new ArrayList();
513            private int counter = 0;
514            private boolean validateSelectively = false;  // true <-> validate returns false for even Integers
515            private boolean passivateSelectively = false; // true <-> passivate throws RTE if Integer = 0 mod 3
516            private boolean throwOnDestroy = false;       // true <-> destroy throws RTE (always)
517            private boolean throwOnActivate = false;      // true <-> activate throws RTE (always)
518            private boolean throwOnMake = false;          // true <-> make throws RTE (always)
519            private boolean throwOnValidate= false;       // true <-> validate throws RTE (always)
520            private boolean throwOnPassivate = false;     // true <-> passivate throws RTE (always)
521            private boolean makeNull = false;             // true <-> make returns null
522            public Object makeObject() {
523                if (throwOnMake) {
524                    final int next = counter + 1;
525                    throw new IntegerFactoryException("makeObject", next);
526                } else {
527                    return makeNull? null : new Integer(counter++);
528                }
529            }
530            public void destroyObject(Object obj) {
531                if (throwOnDestroy) {
532                    final Integer integer = (Integer)obj;
533                    throw new IntegerFactoryException("destroyObject", integer.intValue());
534                }
535                destroyed.add(obj);
536            }
537            public boolean validateObject(Object obj) {
538                if (throwOnValidate) {
539                    final Integer integer = (Integer)obj;
540                    throw new IntegerFactoryException("validateObject", integer.intValue());
541                }
542                if (validateSelectively) {
543                    // only odd objects are valid
544                    if(obj instanceof Integer) {
545                        return ((((Integer)obj).intValue() % 2) == 1);
546                    } else {
547                        return false;
548                    }
549                }
550                return true;
551            }
552            public void activateObject(Object obj) {
553                if (throwOnActivate) {
554                    final Integer integer = (Integer)obj;
555                    throw new IntegerFactoryException("activateObject", integer.intValue());
556                }
557            }
558            public void passivateObject(Object obj) { 
559                if (throwOnPassivate) {
560                    final Integer integer = (Integer)obj;
561                    throw new IntegerFactoryException("passivateObject", integer.intValue());
562                }
563                if (passivateSelectively) {
564                    final Integer integer = (Integer)obj;
565                    if (integer.intValue() % 3 == 0) {
566                        throw new IntegerFactoryException("passivateObject", integer.intValue());
567                    }
568                }
569            }
570            public List getDestroyed() {
571                return destroyed;
572            }
573            public void setCounter(int counter) {
574                this.counter = counter;
575            }
576            public void setValidateSelectively(boolean validateSelectively) {
577                this.validateSelectively = validateSelectively;
578            }
579            public void setPassivateSelectively(boolean passivateSelectively) {
580                this.passivateSelectively = passivateSelectively;
581            }
582            public void setThrowOnDestroy(boolean throwOnDestroy) {
583                this.throwOnDestroy = throwOnDestroy;
584            }
585            public void setThrowOnActivate(boolean throwOnActivate) {
586                this.throwOnActivate = throwOnActivate;
587            }
588            public void setThrowOnMake(boolean throwOnMake) {
589                this.throwOnMake = throwOnMake;
590            }
591            public void setThrowOnPassivate(boolean throwOnPassivate) {
592                this.throwOnPassivate = throwOnPassivate;
593            }
594            public void setThrowOnValidate(boolean throwOnValidate) {
595                this.throwOnValidate = throwOnValidate;
596            }
597            public void setMakeNull(boolean makeNull) {
598                this.makeNull = makeNull;
599            }
600        }
601        
602        static class IntegerFactoryException extends RuntimeException {
603            private String type;
604            private int value;
605            public IntegerFactoryException(String type, int value) {
606                super(type + " failed. Value: " + value);
607                this.type = type;
608                this.value = value;
609            }
610            public String getType() {
611                return type;
612            }
613            public int getValue() {
614                return value;
615            }
616        }
617    
618        protected boolean isLifo() {
619            return true;
620        }
621    
622        protected boolean isFifo() {
623            return false;
624        }
625    }
626