1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.pool.impl;
19  
20  import junit.framework.TestCase;
21  import junit.framework.TestSuite;
22  import org.apache.commons.pool.BasePoolableObjectFactory;
23  import org.apache.commons.pool.PoolableObjectFactory;
24  
25  import java.util.Arrays;
26  import java.util.LinkedList;
27  import java.util.List;
28  
29  /**
30   * @author Dirk Verbeeck
31   * @author Sandy McArthur
32   * @version $Revision: 480413 $ $Date: 2006-11-29 00:16:05 -0500 (Wed, 29 Nov 2006) $
33   */
34  public class TestSoftRefOutOfMemory extends TestCase {
35      private SoftReferenceObjectPool pool;
36  
37      public TestSoftRefOutOfMemory(String testName) {
38          super(testName);
39      }
40  
41      public static TestSuite suite() {
42          return new TestSuite(TestSoftRefOutOfMemory.class);
43      }
44  
45      public void tearDown() throws Exception {
46          if (pool != null) {
47              pool.close();
48              pool = null;
49          }
50          System.gc();
51      }
52  
53      public void testOutOfMemory() throws Exception {
54          pool = new SoftReferenceObjectPool(new SmallPoolableObjectFactory());
55  
56          Object obj = pool.borrowObject();
57          assertEquals("1", obj);
58          pool.returnObject(obj);
59          obj = null;
60  
61          assertEquals(1, pool.getNumIdle());
62  
63          final List garbage = new LinkedList();
64          final Runtime runtime = Runtime.getRuntime();
65          while (pool.getNumIdle() > 0) {
66              try {
67                  garbage.add(new byte[Math.min(1024 * 1024, (int)runtime.freeMemory()/2)]);
68              } catch (OutOfMemoryError oome) {
69                  System.gc();
70              }
71              System.gc();
72          }
73          garbage.clear();
74          System.gc();
75  
76          obj = pool.borrowObject();
77          assertEquals("2", obj);
78          pool.returnObject(obj);
79          obj = null;
80  
81          assertEquals(1, pool.getNumIdle());
82      }
83  
84      public void testOutOfMemory1000() throws Exception {
85          pool = new SoftReferenceObjectPool(new SmallPoolableObjectFactory());
86  
87          for (int i = 0 ; i < 1000 ; i++) {
88              pool.addObject();
89          }
90  
91          Object obj = pool.borrowObject();
92          assertEquals("1000", obj);
93          pool.returnObject(obj);
94          obj = null;
95  
96          assertEquals(1000, pool.getNumIdle());
97  
98          final List garbage = new LinkedList();
99          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 }