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.TestCase;
021    import junit.framework.TestSuite;
022    import org.apache.commons.pool.BasePoolableObjectFactory;
023    import org.apache.commons.pool.PoolableObjectFactory;
024    
025    import java.util.Arrays;
026    import java.util.LinkedList;
027    import java.util.List;
028    
029    /**
030     * @author Dirk Verbeeck
031     * @author Sandy McArthur
032     * @version $Revision: 480413 $ $Date: 2006-11-29 00:16:05 -0500 (Wed, 29 Nov 2006) $
033     */
034    public class TestSoftRefOutOfMemory extends TestCase {
035        private SoftReferenceObjectPool pool;
036    
037        public TestSoftRefOutOfMemory(String testName) {
038            super(testName);
039        }
040    
041        public static TestSuite suite() {
042            return new TestSuite(TestSoftRefOutOfMemory.class);
043        }
044    
045        public void tearDown() throws Exception {
046            if (pool != null) {
047                pool.close();
048                pool = null;
049            }
050            System.gc();
051        }
052    
053        public void testOutOfMemory() throws Exception {
054            pool = new SoftReferenceObjectPool(new SmallPoolableObjectFactory());
055    
056            Object obj = pool.borrowObject();
057            assertEquals("1", obj);
058            pool.returnObject(obj);
059            obj = null;
060    
061            assertEquals(1, pool.getNumIdle());
062    
063            final List garbage = new LinkedList();
064            final Runtime runtime = Runtime.getRuntime();
065            while (pool.getNumIdle() > 0) {
066                try {
067                    garbage.add(new byte[Math.min(1024 * 1024, (int)runtime.freeMemory()/2)]);
068                } catch (OutOfMemoryError oome) {
069                    System.gc();
070                }
071                System.gc();
072            }
073            garbage.clear();
074            System.gc();
075    
076            obj = pool.borrowObject();
077            assertEquals("2", obj);
078            pool.returnObject(obj);
079            obj = null;
080    
081            assertEquals(1, pool.getNumIdle());
082        }
083    
084        public void testOutOfMemory1000() throws Exception {
085            pool = new SoftReferenceObjectPool(new SmallPoolableObjectFactory());
086    
087            for (int i = 0 ; i < 1000 ; i++) {
088                pool.addObject();
089            }
090    
091            Object obj = pool.borrowObject();
092            assertEquals("1000", obj);
093            pool.returnObject(obj);
094            obj = null;
095    
096            assertEquals(1000, pool.getNumIdle());
097    
098            final List garbage = new LinkedList();
099            final Runtime runtime = Runtime.getRuntime();
100            while (pool.getNumIdle() > 0) {
101                try {
102                    garbage.add(new byte[Math.min(1024 * 1024, (int)runtime.freeMemory()/2)]);
103                } catch (OutOfMemoryError oome) {
104                    System.gc();
105                }
106                System.gc();
107            }
108            garbage.clear();
109            System.gc();
110    
111            obj = pool.borrowObject();
112            assertEquals("1001", obj);
113            pool.returnObject(obj);
114            obj = null;
115    
116            assertEquals(1, pool.getNumIdle());
117        }
118    
119        public void testOutOfMemoryLarge() throws Exception {
120            pool = new SoftReferenceObjectPool(new LargePoolableObjectFactory(1000000));
121    
122            Object obj = pool.borrowObject();
123            assertTrue(((String)obj).startsWith("1."));
124            pool.returnObject(obj);
125            obj = null;
126    
127            assertEquals(1, pool.getNumIdle());
128    
129            final List garbage = new LinkedList();
130            final Runtime runtime = Runtime.getRuntime();
131            while (pool.getNumIdle() > 0) {
132                try {
133                    garbage.add(new byte[Math.min(1024 * 1024, (int)runtime.freeMemory()/2)]);
134                } catch (OutOfMemoryError oome) {
135                    System.gc();
136                }
137                System.gc();
138            }
139            garbage.clear();
140            System.gc();
141    
142            obj = pool.borrowObject();
143            assertTrue(((String)obj).startsWith("2."));
144            pool.returnObject(obj);
145            obj = null;
146    
147            assertEquals(1, pool.getNumIdle());
148        }
149    
150        /**
151         * Makes sure an {@link OutOfMemoryError} isn't swallowed.
152         */
153        public void testOutOfMemoryError() throws Exception {
154            pool = new SoftReferenceObjectPool(new BasePoolableObjectFactory() {
155                public Object makeObject() throws Exception {
156                    throw new OutOfMemoryError();
157                }
158            });
159    
160            try {
161                pool.borrowObject();
162                fail("Expected out of memory.");
163            }
164            catch (OutOfMemoryError ex) {
165                // expected
166            }
167            pool.close();
168    
169            pool = new SoftReferenceObjectPool(new BasePoolableObjectFactory() {
170                public Object makeObject() throws Exception {
171                    return new Object();
172                }
173    
174                public boolean validateObject(Object obj) {
175                    throw new OutOfMemoryError();
176                }
177            });
178    
179            pool.returnObject(pool.borrowObject());
180    
181            try {
182                pool.borrowObject();
183                fail("Expected out of memory.");
184            }
185            catch (OutOfMemoryError ex) {
186                // expected
187            }
188            pool.close();
189        }
190    
191    
192        public static class SmallPoolableObjectFactory implements PoolableObjectFactory {
193            private int counter = 0;
194    
195            public Object makeObject() {
196                counter++;
197                // It seems that as of Java 1.4 String.valueOf may return an
198                // intern()'ed String this may cause problems when the tests
199                // depend on the returned object to be eventually garbaged
200                // collected. Either way, making sure a new String instance
201                // is returned eliminated false failures.
202                return new String(String.valueOf(counter));
203            }
204            public boolean validateObject(Object obj) {
205                return true;
206            }
207            public void activateObject(Object obj) { }
208            public void passivateObject(Object obj) { }
209            public void destroyObject(Object obj) { }
210        }
211    
212        public static class LargePoolableObjectFactory implements PoolableObjectFactory {
213            private String buffer;
214            private int counter = 0;
215    
216            public LargePoolableObjectFactory(int size) {
217                char[] data = new char[size];
218                Arrays.fill(data, '.');
219                buffer = new String(data);
220            }
221    
222            public Object makeObject() {
223                counter++;
224                return String.valueOf(counter) + buffer;
225            }
226            public boolean validateObject(Object obj) {
227                return true;
228            }
229            public void activateObject(Object obj) { }
230            public void passivateObject(Object obj) { }
231            public void destroyObject(Object obj) { }
232        }
233    }