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 package org.apache.commons.pool;
018
019 import junit.framework.TestCase;
020 import org.apache.commons.pool.impl.GenericObjectPool;
021 import org.apache.commons.pool.impl.StackObjectPool;
022 import org.apache.commons.pool.impl.SoftReferenceObjectPool;
023
024 import java.util.List;
025 import java.util.ArrayList;
026 import java.util.Iterator;
027 import java.util.NoSuchElementException;
028
029 /**
030 * Abstract {@link TestCase} for {@link ObjectPool} implementations.
031 * @author Rodney Waldhoff
032 * @author Sandy McArthur
033 * @version $Revision: 775703 $ $Date: 2009-05-17 12:39:51 -0400 (Sun, 17 May 2009) $
034 */
035 public abstract class TestObjectPool extends TestCase {
036 public TestObjectPool(String testName) {
037 super(testName);
038 }
039
040 /**
041 * Create an <code>ObjectPool</code> with the specified factory.
042 * The pool should be in a default configuration and conform to the expected
043 * behaviors described in {@link ObjectPool}.
044 * Generally speaking there should be no limits on the various object counts.
045 * @throws UnsupportedOperationException if the pool being tested does not follow pool contracts.
046 */
047 protected abstract ObjectPool makeEmptyPool(PoolableObjectFactory factory) throws UnsupportedOperationException;
048
049 public void testClosedPoolBehavior() throws Exception {
050 final ObjectPool pool;
051 try {
052 pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
053 } catch (UnsupportedOperationException uoe) {
054 return; // test not supported
055 }
056 Object o1 = pool.borrowObject();
057 Object o2 = pool.borrowObject();
058
059 pool.close();
060
061 try {
062 pool.addObject();
063 fail("A closed pool must throw an IllegalStateException when addObject is called.");
064 } catch (IllegalStateException ise) {
065 // expected
066 }
067
068 try {
069 pool.borrowObject();
070 fail("A closed pool must throw an IllegalStateException when borrowObject is called.");
071 } catch (IllegalStateException ise) {
072 // expected
073 }
074
075 // The following should not throw exceptions just because the pool is closed.
076 if (pool.getNumIdle() >= 0) {
077 assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle());
078 }
079 if (pool.getNumActive() >= 0) {
080 assertEquals("A closed pool should still keep count of active objects.", 2, pool.getNumActive());
081 }
082 pool.returnObject(o1);
083 if (pool.getNumIdle() >= 0) {
084 assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle());
085 }
086 if (pool.getNumActive() >= 0) {
087 assertEquals("A closed pool should still keep count of active objects.", 1, pool.getNumActive());
088 }
089 pool.invalidateObject(o2);
090 if (pool.getNumIdle() >= 0) {
091 assertEquals("invalidateObject must not add items back into the idle object pool.", 0, pool.getNumIdle());
092 }
093 if (pool.getNumActive() >= 0) {
094 assertEquals("A closed pool should still keep count of active objects.", 0, pool.getNumActive());
095 }
096 pool.clear();
097 pool.close();
098 }
099
100 private final Integer ZERO = new Integer(0);
101 private final Integer ONE = new Integer(1);
102
103 public void testPOFAddObjectUsage() throws Exception {
104 final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
105 final ObjectPool pool;
106 try {
107 pool = makeEmptyPool(factory);
108 } catch(UnsupportedOperationException uoe) {
109 return; // test not supported
110 }
111 final List expectedMethods = new ArrayList();
112
113 assertEquals(0, pool.getNumActive());
114 assertEquals(0, pool.getNumIdle());
115 // addObject should make a new object, pasivate it and put it in the pool
116 pool.addObject();
117 assertEquals(0, pool.getNumActive());
118 assertEquals(1, pool.getNumIdle());
119 expectedMethods.add(new MethodCall("makeObject").returned(ZERO));
120 // StackObjectPool, SoftReferenceObjectPool also validate on add
121 if (pool instanceof StackObjectPool ||
122 pool instanceof SoftReferenceObjectPool) {
123 expectedMethods.add(new MethodCall(
124 "validateObject", ZERO).returned(Boolean.TRUE));
125 }
126 expectedMethods.add(new MethodCall("passivateObject", ZERO));
127 assertEquals(expectedMethods, factory.getMethodCalls());
128
129 //// Test exception handling of addObject
130 reset(pool, factory, expectedMethods);
131
132 // makeObject Exceptions should be propagated to client code from addObject
133 factory.setMakeObjectFail(true);
134 try {
135 pool.addObject();
136 fail("Expected addObject to propagate makeObject exception.");
137 } catch (PrivateException pe) {
138 // expected
139 }
140 expectedMethods.add(new MethodCall("makeObject"));
141 assertEquals(expectedMethods, factory.getMethodCalls());
142
143 clear(factory, expectedMethods);
144
145 // passivateObject Exceptions should be propagated to client code from addObject
146 factory.setMakeObjectFail(false);
147 factory.setPassivateObjectFail(true);
148 try {
149 pool.addObject();
150 fail("Expected addObject to propagate passivateObject exception.");
151 } catch (PrivateException pe) {
152 // expected
153 }
154 expectedMethods.add(new MethodCall("makeObject").returned(ONE));
155 // StackObjectPool, SofReferenceObjectPool also validate on add
156 if (pool instanceof StackObjectPool ||
157 pool instanceof SoftReferenceObjectPool) {
158 expectedMethods.add(new MethodCall(
159 "validateObject", ONE).returned(Boolean.TRUE));
160 }
161 expectedMethods.add(new MethodCall("passivateObject", ONE));
162 assertEquals(expectedMethods, factory.getMethodCalls());
163 }
164
165 public void testPOFBorrowObjectUsages() throws Exception {
166 final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
167 final ObjectPool pool;
168 try {
169 pool = makeEmptyPool(factory);
170 } catch (UnsupportedOperationException uoe) {
171 return; // test not supported
172 }
173 if (pool instanceof GenericObjectPool) {
174 ((GenericObjectPool) pool).setTestOnBorrow(true);
175 }
176 final List expectedMethods = new ArrayList();
177 Object obj;
178
179 /// Test correct behavior code paths
180
181 // existing idle object should be activated and validated
182 pool.addObject();
183 clear(factory, expectedMethods);
184 obj = pool.borrowObject();
185 expectedMethods.add(new MethodCall("activateObject", ZERO));
186 expectedMethods.add(new MethodCall("validateObject", ZERO).returned(Boolean.TRUE));
187 assertEquals(expectedMethods, factory.getMethodCalls());
188 pool.returnObject(obj);
189
190 //// Test exception handling of borrowObject
191 reset(pool, factory, expectedMethods);
192
193 // makeObject Exceptions should be propagated to client code from borrowObject
194 factory.setMakeObjectFail(true);
195 try {
196 obj = pool.borrowObject();
197 fail("Expected borrowObject to propagate makeObject exception.");
198 } catch (PrivateException pe) {
199 // expected
200 }
201 expectedMethods.add(new MethodCall("makeObject"));
202 assertEquals(expectedMethods, factory.getMethodCalls());
203
204
205 // when activateObject fails in borrowObject, a new object should be borrowed/created
206 reset(pool, factory, expectedMethods);
207 pool.addObject();
208 clear(factory, expectedMethods);
209
210 factory.setActivateObjectFail(true);
211 expectedMethods.add(new MethodCall("activateObject", obj));
212 try {
213 obj = pool.borrowObject();
214 fail("Expecting NoSuchElementException");
215 } catch (NoSuchElementException ex) {
216 // Expected - newly created object will also fail to activate
217 }
218 // Idle object fails activation, new one created, also fails
219 expectedMethods.add(new MethodCall("makeObject").returned(ONE));
220 expectedMethods.add(new MethodCall("activateObject", ONE));
221 removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
222 assertEquals(expectedMethods, factory.getMethodCalls());
223
224 // when validateObject fails in borrowObject, a new object should be borrowed/created
225 reset(pool, factory, expectedMethods);
226 pool.addObject();
227 clear(factory, expectedMethods);
228
229 factory.setValidateObjectFail(true);
230 expectedMethods.add(new MethodCall("activateObject", ZERO));
231 expectedMethods.add(new MethodCall("validateObject", ZERO));
232 try {
233 obj = pool.borrowObject();
234 } catch (NoSuchElementException ex) {
235 // Expected - newly created object will also fail to validate
236 }
237 // Idle object is activated, but fails validation.
238 // New instance is created, activated and then fails validation
239 expectedMethods.add(new MethodCall("makeObject").returned(ONE));
240 expectedMethods.add(new MethodCall("activateObject", ONE));
241 expectedMethods.add(new MethodCall("validateObject", ONE));
242 removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
243 // Second activate and validate are missing from expectedMethods
244 assertTrue(factory.getMethodCalls().containsAll(expectedMethods));
245 }
246
247 public void testPOFReturnObjectUsages() throws Exception {
248 final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
249 final ObjectPool pool;
250 try {
251 pool = makeEmptyPool(factory);
252 } catch (UnsupportedOperationException uoe) {
253 return; // test not supported
254 }
255 final List expectedMethods = new ArrayList();
256 Object obj;
257
258 /// Test correct behavior code paths
259 obj = pool.borrowObject();
260 clear(factory, expectedMethods);
261
262 // returned object should be passivated
263 pool.returnObject(obj);
264 // StackObjectPool, SoftReferenceObjectPool also validate on return
265 if (pool instanceof StackObjectPool ||
266 pool instanceof SoftReferenceObjectPool) {
267 expectedMethods.add(new MethodCall(
268 "validateObject", obj).returned(Boolean.TRUE));
269 }
270 expectedMethods.add(new MethodCall("passivateObject", obj));
271 assertEquals(expectedMethods, factory.getMethodCalls());
272
273 //// Test exception handling of returnObject
274 reset(pool, factory, expectedMethods);
275 pool.addObject();
276 pool.addObject();
277 pool.addObject();
278 assertEquals(3, pool.getNumIdle());
279 // passivateObject should swallow exceptions and not add the object to the pool
280 obj = pool.borrowObject();
281 pool.borrowObject();
282 assertEquals(1, pool.getNumIdle());
283 assertEquals(2, pool.getNumActive());
284 clear(factory, expectedMethods);
285 factory.setPassivateObjectFail(true);
286 pool.returnObject(obj);
287 // StackObjectPool, SoftReferenceObjectPool also validate on return
288 if (pool instanceof StackObjectPool ||
289 pool instanceof SoftReferenceObjectPool) {
290 expectedMethods.add(new MethodCall(
291 "validateObject", obj).returned(Boolean.TRUE));
292 }
293 expectedMethods.add(new MethodCall("passivateObject", obj));
294 removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
295 assertEquals(expectedMethods, factory.getMethodCalls());
296 assertEquals(1, pool.getNumIdle()); // Not returned
297 assertEquals(1, pool.getNumActive()); // But not in active count
298
299 // destroyObject should swallow exceptions too
300 reset(pool, factory, expectedMethods);
301 obj = pool.borrowObject();
302 clear(factory, expectedMethods);
303 factory.setPassivateObjectFail(true);
304 factory.setDestroyObjectFail(true);
305 pool.returnObject(obj);
306 }
307
308 public void testPOFInvalidateObjectUsages() throws Exception {
309 final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
310 final ObjectPool pool;
311 try {
312 pool = makeEmptyPool(factory);
313 } catch (UnsupportedOperationException uoe) {
314 return; // test not supported
315 }
316 final List expectedMethods = new ArrayList();
317 Object obj;
318
319 /// Test correct behavior code paths
320
321 obj = pool.borrowObject();
322 clear(factory, expectedMethods);
323
324 // invalidated object should be destroyed
325 pool.invalidateObject(obj);
326 expectedMethods.add(new MethodCall("destroyObject", obj));
327 assertEquals(expectedMethods, factory.getMethodCalls());
328
329 //// Test exception handling of invalidateObject
330 reset(pool, factory, expectedMethods);
331 obj = pool.borrowObject();
332 clear(factory, expectedMethods);
333 factory.setDestroyObjectFail(true);
334 try {
335 pool.invalidateObject(obj);
336 fail("Expecting destroy exception to propagate");
337 } catch (PrivateException ex) {
338 // Expected
339 }
340 Thread.sleep(250); // could be defered
341 removeDestroyObjectCall(factory.getMethodCalls());
342 assertEquals(expectedMethods, factory.getMethodCalls());
343 }
344
345 public void testPOFClearUsages() throws Exception {
346 final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
347 final ObjectPool pool;
348 try {
349 pool = makeEmptyPool(factory);
350 } catch (UnsupportedOperationException uoe) {
351 return; // test not supported
352 }
353 final List expectedMethods = new ArrayList();
354
355 /// Test correct behavior code paths
356 PoolUtils.prefill(pool, 5);
357 pool.clear();
358
359 //// Test exception handling clear should swallow destory object failures
360 reset(pool, factory, expectedMethods);
361 factory.setDestroyObjectFail(true);
362 PoolUtils.prefill(pool, 5);
363 pool.clear();
364 }
365
366 public void testPOFCloseUsages() throws Exception {
367 final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
368 ObjectPool pool;
369 try {
370 pool = makeEmptyPool(factory);
371 } catch (UnsupportedOperationException uoe) {
372 return; // test not supported
373 }
374 final List expectedMethods = new ArrayList();
375
376 /// Test correct behavior code paths
377 PoolUtils.prefill(pool, 5);
378 pool.close();
379
380
381 //// Test exception handling close should swallow failures
382 try {
383 pool = makeEmptyPool(factory);
384 } catch (UnsupportedOperationException uoe) {
385 return; // test not supported
386 }
387 reset(pool, factory, expectedMethods);
388 factory.setDestroyObjectFail(true);
389 PoolUtils.prefill(pool, 5);
390 pool.close();
391 }
392
393 public void testSetFactory() throws Exception {
394 ObjectPool pool;
395 try {
396 pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
397 } catch (UnsupportedOperationException uoe) {
398 return; // test not supported
399 }
400 final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
401 try {
402 pool.setFactory(factory);
403 } catch (UnsupportedOperationException uoe) {
404 return;
405 }
406 }
407
408 public void testToString() {
409 ObjectPool pool;
410 try {
411 pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
412 } catch (UnsupportedOperationException uoe) {
413 return; // test not supported
414 }
415 pool.toString();
416 }
417
418 static void removeDestroyObjectCall(List calls) {
419 Iterator iter = calls.iterator();
420 while (iter.hasNext()) {
421 MethodCall call = (MethodCall)iter.next();
422 if ("destroyObject".equals(call.getName())) {
423 iter.remove();
424 }
425 }
426 }
427
428 private static void reset(final ObjectPool pool, final MethodCallPoolableObjectFactory factory, final List expectedMethods) throws Exception {
429 pool.clear();
430 clear(factory, expectedMethods);
431 factory.reset();
432 }
433
434 private static void clear(final MethodCallPoolableObjectFactory factory, final List expectedMethods) {
435 factory.getMethodCalls().clear();
436 expectedMethods.clear();
437 }
438 }