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 }