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.Arrays;
021    import java.util.LinkedList;
022    import java.util.List;
023    
024    import junit.framework.TestCase;
025    
026    import org.apache.commons.pool.BasePoolableObjectFactory;
027    import org.apache.commons.pool.PoolableObjectFactory;
028    
029    /**
030     * @author Dirk Verbeeck
031     * @author Sandy McArthur
032     * @version $Revision: 1084639 $ $Date: 2011-03-23 10:02:42 -0700 (Wed, 23 Mar 2011) $
033     */
034    public class TestSoftRefOutOfMemory extends TestCase {
035        private SoftReferenceObjectPool pool;
036    
037        public TestSoftRefOutOfMemory(String testName) {
038            super(testName);
039        }
040    
041        public void tearDown() throws Exception {
042            if (pool != null) {
043                pool.close();
044                pool = null;
045            }
046            System.gc();
047        }
048    
049        public void testOutOfMemory() throws Exception {
050            pool = new SoftReferenceObjectPool(new SmallPoolableObjectFactory());
051    
052            Object obj = pool.borrowObject();
053            assertEquals("1", obj);
054            pool.returnObject(obj);
055            obj = null;
056    
057            assertEquals(1, pool.getNumIdle());
058    
059            final List garbage = new LinkedList();
060            final Runtime runtime = Runtime.getRuntime();
061            while (pool.getNumIdle() > 0) {
062                try {
063                    long freeMemory = runtime.freeMemory();
064                    if (freeMemory > Integer.MAX_VALUE) {
065                        freeMemory = Integer.MAX_VALUE;
066                    }
067                    garbage.add(new byte[Math.min(1024 * 1024, (int)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                    long freeMemory = runtime.freeMemory();
103                    if (freeMemory > Integer.MAX_VALUE) {
104                        freeMemory = Integer.MAX_VALUE;
105                    }
106                    garbage.add(new byte[Math.min(1024 * 1024, (int)freeMemory/2)]);
107                } catch (OutOfMemoryError oome) {
108                    System.gc();
109                }
110                System.gc();
111            }
112            garbage.clear();
113            System.gc();
114    
115            obj = pool.borrowObject();
116            assertEquals("1001", obj);
117            pool.returnObject(obj);
118            obj = null;
119    
120            assertEquals(1, pool.getNumIdle());
121        }
122    
123        public void testOutOfMemoryLarge() throws Exception {
124            pool = new SoftReferenceObjectPool(new LargePoolableObjectFactory(1000000));
125    
126            Object obj = pool.borrowObject();
127            assertTrue(((String)obj).startsWith("1."));
128            pool.returnObject(obj);
129            obj = null;
130    
131            assertEquals(1, pool.getNumIdle());
132    
133            final List garbage = new LinkedList();
134            final Runtime runtime = Runtime.getRuntime();
135            while (pool.getNumIdle() > 0) {
136                try {
137                    long freeMemory = runtime.freeMemory();
138                    if (freeMemory > Integer.MAX_VALUE) {
139                        freeMemory = Integer.MAX_VALUE;
140                    }
141                    garbage.add(new byte[Math.min(1024 * 1024, (int)freeMemory/2)]);
142                } catch (OutOfMemoryError oome) {
143                    System.gc();
144                }
145                System.gc();
146            }
147            garbage.clear();
148            System.gc();
149    
150            obj = pool.borrowObject();
151            assertTrue(((String)obj).startsWith("2."));
152            pool.returnObject(obj);
153            obj = null;
154    
155            assertEquals(1, pool.getNumIdle());
156        }
157    
158        /**
159         * Makes sure an {@link OutOfMemoryError} isn't swallowed.
160         */
161        public void testOutOfMemoryError() throws Exception {
162            pool = new SoftReferenceObjectPool(new BasePoolableObjectFactory() {
163                public Object makeObject() throws Exception {
164                    throw new OutOfMemoryError();
165                }
166            });
167    
168            try {
169                pool.borrowObject();
170                fail("Expected out of memory.");
171            }
172            catch (OutOfMemoryError ex) {
173                // expected
174            }
175            pool.close();
176    
177            pool = new SoftReferenceObjectPool(new BasePoolableObjectFactory() {
178                public Object makeObject() throws Exception {
179                    return new Object();
180                }
181    
182                public boolean validateObject(Object obj) {
183                    throw new OutOfMemoryError();
184                }
185            });
186    
187            try {
188                pool.borrowObject();
189                fail("Expected out of memory.");
190            }
191            catch (OutOfMemoryError ex) {
192                // expected
193            }
194            pool.close();
195            
196            pool = new SoftReferenceObjectPool(new BasePoolableObjectFactory() {
197                public Object makeObject() throws Exception {
198                    return new Object();
199                }
200    
201                public boolean validateObject(Object obj) {
202                    throw new IllegalAccessError();
203                }
204    
205                public void destroyObject(Object obj) throws Exception {
206                    throw new OutOfMemoryError();
207                }
208            });
209    
210            try {
211                pool.borrowObject();
212                fail("Expected out of memory.");
213            }
214            catch (OutOfMemoryError ex) {
215                // expected
216            }
217            pool.close();
218    
219        }
220    
221    
222        public static class SmallPoolableObjectFactory implements PoolableObjectFactory {
223            private int counter = 0;
224    
225            public Object makeObject() {
226                counter++;
227                // It seems that as of Java 1.4 String.valueOf may return an
228                // intern()'ed String this may cause problems when the tests
229                // depend on the returned object to be eventually garbaged
230                // collected. Either way, making sure a new String instance
231                // is returned eliminated false failures.
232                return new String(String.valueOf(counter));
233            }
234            public boolean validateObject(Object obj) {
235                return true;
236            }
237            public void activateObject(Object obj) { }
238            public void passivateObject(Object obj) { }
239            public void destroyObject(Object obj) { }
240        }
241    
242        public static class LargePoolableObjectFactory implements PoolableObjectFactory {
243            private String buffer;
244            private int counter = 0;
245    
246            public LargePoolableObjectFactory(int size) {
247                char[] data = new char[size];
248                Arrays.fill(data, '.');
249                buffer = new String(data);
250            }
251    
252            public Object makeObject() {
253                counter++;
254                return String.valueOf(counter) + buffer;
255            }
256            public boolean validateObject(Object obj) {
257                return true;
258            }
259            public void activateObject(Object obj) { }
260            public void passivateObject(Object obj) { }
261            public void destroyObject(Object obj) { }
262        }
263    }