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 021 import java.util.List; 022 import java.util.ArrayList; 023 import java.util.NoSuchElementException; 024 025 import org.apache.commons.pool.impl.GenericKeyedObjectPool; 026 import org.apache.commons.pool.impl.StackKeyedObjectPool; 027 028 /** 029 * Abstract {@link TestCase} for {@link ObjectPool} implementations. 030 * @author Rodney Waldhoff 031 * @author Sandy McArthur 032 * @version $Revision: 774099 $ $Date: 2009-05-12 17:29:02 -0400 (Tue, 12 May 2009) $ 033 */ 034 public abstract class TestKeyedObjectPool extends TestCase { 035 public TestKeyedObjectPool(String testName) { 036 super(testName); 037 } 038 039 /** 040 * Create an <code>KeyedObjectPool</code> with the specified factory. 041 * The pool should be in a default configuration and conform to the expected 042 * behaviors described in {@link KeyedObjectPool}. 043 * Generally speaking there should be no limits on the various object counts. 044 */ 045 protected abstract KeyedObjectPool makeEmptyPool(KeyedPoolableObjectFactory factory); 046 047 protected final String KEY = "key"; 048 049 public void testClosedPoolBehavior() throws Exception { 050 final KeyedObjectPool pool; 051 try { 052 pool = makeEmptyPool(new BaseKeyedPoolableObjectFactory() { 053 public Object makeObject(final Object key) throws Exception { 054 return new Object(); 055 } 056 }); 057 } catch(UnsupportedOperationException uoe) { 058 return; // test not supported 059 } 060 061 Object o1 = pool.borrowObject(KEY); 062 Object o2 = pool.borrowObject(KEY); 063 064 pool.close(); 065 066 try { 067 pool.addObject(KEY); 068 fail("A closed pool must throw an IllegalStateException when addObject is called."); 069 } catch (IllegalStateException ise) { 070 // expected 071 } 072 073 try { 074 pool.borrowObject(KEY); 075 fail("A closed pool must throw an IllegalStateException when borrowObject is called."); 076 } catch (IllegalStateException ise) { 077 // expected 078 } 079 080 // The following should not throw exceptions just because the pool is closed. 081 assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle(KEY)); 082 assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle()); 083 pool.getNumActive(); 084 pool.getNumActive(KEY); 085 pool.returnObject(KEY, o1); 086 assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle(KEY)); 087 assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle()); 088 pool.invalidateObject(KEY, o2); 089 pool.clear(KEY); 090 pool.clear(); 091 pool.close(); 092 } 093 094 private final Integer ZERO = new Integer(0); 095 private final Integer ONE = new Integer(1); 096 097 public void testKPOFAddObjectUsage() throws Exception { 098 final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory(); 099 final KeyedObjectPool pool; 100 try { 101 pool = makeEmptyPool(factory); 102 } catch(UnsupportedOperationException uoe) { 103 return; // test not supported 104 } 105 final List expectedMethods = new ArrayList(); 106 107 // addObject should make a new object, pasivate it and put it in the pool 108 pool.addObject(KEY); 109 expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO)); 110 if (pool instanceof StackKeyedObjectPool) { 111 expectedMethods.add(new MethodCall( 112 "validateObject", KEY, ZERO).returned(Boolean.TRUE)); 113 } 114 expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO)); 115 assertEquals(expectedMethods, factory.getMethodCalls()); 116 117 //// Test exception handling of addObject 118 reset(pool, factory, expectedMethods); 119 120 // makeObject Exceptions should be propagated to client code from addObject 121 factory.setMakeObjectFail(true); 122 try { 123 pool.addObject(KEY); 124 fail("Expected addObject to propagate makeObject exception."); 125 } catch (PrivateException pe) { 126 // expected 127 } 128 expectedMethods.add(new MethodCall("makeObject", KEY)); 129 assertEquals(expectedMethods, factory.getMethodCalls()); 130 131 clear(factory, expectedMethods); 132 133 // passivateObject Exceptions should be propagated to client code from addObject 134 factory.setMakeObjectFail(false); 135 factory.setPassivateObjectFail(true); 136 try { 137 pool.addObject(KEY); 138 fail("Expected addObject to propagate passivateObject exception."); 139 } catch (PrivateException pe) { 140 // expected 141 } 142 expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); 143 if (pool instanceof StackKeyedObjectPool) { 144 expectedMethods.add(new MethodCall( 145 "validateObject", KEY, ONE).returned(Boolean.TRUE)); 146 } 147 expectedMethods.add(new MethodCall("passivateObject", KEY, ONE)); 148 assertEquals(expectedMethods, factory.getMethodCalls()); 149 } 150 151 public void testKPOFBorrowObjectUsages() throws Exception { 152 final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory(); 153 final KeyedObjectPool pool; 154 try { 155 pool = makeEmptyPool(factory); 156 } catch(UnsupportedOperationException uoe) { 157 return; // test not supported 158 } 159 final List expectedMethods = new ArrayList(); 160 Object obj; 161 162 if (pool instanceof GenericKeyedObjectPool) { 163 ((GenericKeyedObjectPool) pool).setTestOnBorrow(true); 164 } 165 166 /// Test correct behavior code paths 167 168 // existing idle object should be activated and validated 169 pool.addObject(KEY); 170 clear(factory, expectedMethods); 171 obj = pool.borrowObject(KEY); 172 expectedMethods.add(new MethodCall("activateObject", KEY, ZERO)); 173 expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE)); 174 assertEquals(expectedMethods, factory.getMethodCalls()); 175 pool.returnObject(KEY, obj); 176 177 //// Test exception handling of borrowObject 178 reset(pool, factory, expectedMethods); 179 180 // makeObject Exceptions should be propagated to client code from borrowObject 181 factory.setMakeObjectFail(true); 182 try { 183 obj = pool.borrowObject(KEY); 184 fail("Expected borrowObject to propagate makeObject exception."); 185 } catch (PrivateException pe) { 186 // expected 187 } 188 expectedMethods.add(new MethodCall("makeObject", KEY)); 189 assertEquals(expectedMethods, factory.getMethodCalls()); 190 191 192 // when activateObject fails in borrowObject, a new object should be borrowed/created 193 reset(pool, factory, expectedMethods); 194 pool.addObject(KEY); 195 clear(factory, expectedMethods); 196 197 factory.setActivateObjectFail(true); 198 expectedMethods.add(new MethodCall("activateObject", KEY, obj)); 199 try { 200 obj = pool.borrowObject(KEY); 201 fail("Expecting NoSuchElementException"); 202 } catch (NoSuchElementException e) { 203 //Activate should fail 204 } 205 // After idle object fails validation, new on is created and activation 206 // fails again for the new one. 207 expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); 208 expectedMethods.add(new MethodCall("activateObject", KEY, ONE)); 209 TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. 210 assertEquals(expectedMethods, factory.getMethodCalls()); 211 212 // when validateObject fails in borrowObject, a new object should be borrowed/created 213 reset(pool, factory, expectedMethods); 214 pool.addObject(KEY); 215 clear(factory, expectedMethods); 216 217 factory.setValidateObjectFail(true); 218 // testOnBorrow is on, so this will throw when the newly created instance 219 // fails validation 220 try { 221 obj = pool.borrowObject(KEY); 222 fail("Expecting NoSuchElementException"); 223 } catch (NoSuchElementException ex) { 224 // expected 225 } 226 // Activate, then validate for idle instance 227 expectedMethods.add(new MethodCall("activateObject", KEY, ZERO)); 228 expectedMethods.add(new MethodCall("validateObject", KEY, ZERO)); 229 // Make new instance, activate succeeds, validate fails 230 expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE)); 231 expectedMethods.add(new MethodCall("activateObject", KEY, ONE)); 232 expectedMethods.add(new MethodCall("validateObject", KEY, ONE)); 233 TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); 234 assertEquals(expectedMethods, factory.getMethodCalls()); 235 } 236 237 public void testKPOFReturnObjectUsages() throws Exception { 238 final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory(); 239 final KeyedObjectPool pool; 240 try { 241 pool = makeEmptyPool(factory); 242 } catch(UnsupportedOperationException uoe) { 243 return; // test not supported 244 } 245 final List expectedMethods = new ArrayList(); 246 Object obj; 247 248 /// Test correct behavior code paths 249 obj = pool.borrowObject(KEY); 250 clear(factory, expectedMethods); 251 252 // returned object should be passivated 253 pool.returnObject(KEY, obj); 254 if (pool instanceof StackKeyedObjectPool) { 255 expectedMethods.add(new MethodCall( 256 "validateObject", KEY, obj).returned(Boolean.TRUE)); 257 } 258 expectedMethods.add(new MethodCall("passivateObject", KEY, obj)); 259 assertEquals(expectedMethods, factory.getMethodCalls()); 260 261 //// Test exception handling of returnObject 262 reset(pool, factory, expectedMethods); 263 264 // passivateObject should swallow exceptions and not add the object to the pool 265 pool.addObject(KEY); 266 pool.addObject(KEY); 267 pool.addObject(KEY); 268 assertEquals(3, pool.getNumIdle(KEY)); 269 obj = pool.borrowObject(KEY); 270 obj = pool.borrowObject(KEY); 271 assertEquals(1, pool.getNumIdle(KEY)); 272 assertEquals(2, pool.getNumActive(KEY)); 273 clear(factory, expectedMethods); 274 factory.setPassivateObjectFail(true); 275 pool.returnObject(KEY, obj); 276 if (pool instanceof StackKeyedObjectPool) { 277 expectedMethods.add(new MethodCall( 278 "validateObject", KEY, obj).returned(Boolean.TRUE)); 279 } 280 expectedMethods.add(new MethodCall("passivateObject", KEY, obj)); 281 TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here. 282 assertEquals(expectedMethods, factory.getMethodCalls()); 283 assertEquals(1, pool.getNumIdle(KEY)); // Not added 284 assertEquals(1, pool.getNumActive(KEY)); // But not active 285 286 reset(pool, factory, expectedMethods); 287 obj = pool.borrowObject(KEY); 288 clear(factory, expectedMethods); 289 factory.setPassivateObjectFail(true); 290 factory.setDestroyObjectFail(true); 291 try { 292 pool.returnObject(KEY, obj); 293 if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat 294 fail("Expecting destroyObject exception to be propagated"); 295 } 296 } catch (PrivateException ex) { 297 // Expected 298 } 299 } 300 301 public void testKPOFInvalidateObjectUsages() throws Exception { 302 final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory(); 303 final KeyedObjectPool pool; 304 try { 305 pool = makeEmptyPool(factory); 306 } catch(UnsupportedOperationException uoe) { 307 return; // test not supported 308 } 309 final List expectedMethods = new ArrayList(); 310 Object obj; 311 312 /// Test correct behavior code paths 313 314 obj = pool.borrowObject(KEY); 315 clear(factory, expectedMethods); 316 317 // invalidated object should be destroyed 318 pool.invalidateObject(KEY, obj); 319 expectedMethods.add(new MethodCall("destroyObject", KEY, obj)); 320 assertEquals(expectedMethods, factory.getMethodCalls()); 321 322 //// Test exception handling of invalidateObject 323 reset(pool, factory, expectedMethods); 324 obj = pool.borrowObject(KEY); 325 clear(factory, expectedMethods); 326 factory.setDestroyObjectFail(true); 327 try { 328 pool.invalidateObject(KEY, obj); 329 fail("Expecting destroy exception to propagate"); 330 } catch (PrivateException ex) { 331 // Expected 332 } 333 Thread.sleep(250); // could be defered 334 TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); 335 assertEquals(expectedMethods, factory.getMethodCalls()); 336 } 337 338 public void testKPOFClearUsages() throws Exception { 339 final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory(); 340 final KeyedObjectPool pool; 341 try { 342 pool = makeEmptyPool(factory); 343 } catch(UnsupportedOperationException uoe) { 344 return; // test not supported 345 } 346 final List expectedMethods = new ArrayList(); 347 348 /// Test correct behavior code paths 349 PoolUtils.prefill(pool, KEY, 5); 350 pool.clear(); 351 352 //// Test exception handling clear should swallow destory object failures 353 reset(pool, factory, expectedMethods); 354 factory.setDestroyObjectFail(true); 355 PoolUtils.prefill(pool, KEY, 5); 356 pool.clear(); 357 } 358 359 public void testKPOFCloseUsages() throws Exception { 360 final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory(); 361 KeyedObjectPool pool; 362 try { 363 pool = makeEmptyPool(factory); 364 } catch(UnsupportedOperationException uoe) { 365 return; // test not supported 366 } 367 final List expectedMethods = new ArrayList(); 368 369 /// Test correct behavior code paths 370 PoolUtils.prefill(pool, KEY, 5); 371 pool.close(); 372 373 374 //// Test exception handling close should swallow failures 375 pool = makeEmptyPool(factory); 376 reset(pool, factory, expectedMethods); 377 factory.setDestroyObjectFail(true); 378 PoolUtils.prefill(pool, KEY, 5); 379 pool.close(); 380 } 381 382 public void testToString() throws Exception { 383 final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory(); 384 try { 385 makeEmptyPool(factory).toString(); 386 } catch(UnsupportedOperationException uoe) { 387 return; // test not supported 388 } 389 } 390 391 private void reset(final KeyedObjectPool pool, final FailingKeyedPoolableObjectFactory factory, final List expectedMethods) throws Exception { 392 pool.clear(); 393 clear(factory, expectedMethods); 394 factory.reset(); 395 } 396 397 private void clear(final FailingKeyedPoolableObjectFactory factory, final List expectedMethods) { 398 factory.getMethodCalls().clear(); 399 expectedMethods.clear(); 400 } 401 402 protected static class FailingKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory { 403 private final List methodCalls = new ArrayList(); 404 private int count = 0; 405 private boolean makeObjectFail; 406 private boolean activateObjectFail; 407 private boolean validateObjectFail; 408 private boolean passivateObjectFail; 409 private boolean destroyObjectFail; 410 411 public FailingKeyedPoolableObjectFactory() { 412 } 413 414 public void reset() { 415 count = 0; 416 getMethodCalls().clear(); 417 setMakeObjectFail(false); 418 setActivateObjectFail(false); 419 setValidateObjectFail(false); 420 setPassivateObjectFail(false); 421 setDestroyObjectFail(false); 422 } 423 424 public List getMethodCalls() { 425 return methodCalls; 426 } 427 428 public int getCurrentCount() { 429 return count; 430 } 431 432 public void setCurrentCount(final int count) { 433 this.count = count; 434 } 435 436 public boolean isMakeObjectFail() { 437 return makeObjectFail; 438 } 439 440 public void setMakeObjectFail(boolean makeObjectFail) { 441 this.makeObjectFail = makeObjectFail; 442 } 443 444 public boolean isDestroyObjectFail() { 445 return destroyObjectFail; 446 } 447 448 public void setDestroyObjectFail(boolean destroyObjectFail) { 449 this.destroyObjectFail = destroyObjectFail; 450 } 451 452 public boolean isValidateObjectFail() { 453 return validateObjectFail; 454 } 455 456 public void setValidateObjectFail(boolean validateObjectFail) { 457 this.validateObjectFail = validateObjectFail; 458 } 459 460 public boolean isActivateObjectFail() { 461 return activateObjectFail; 462 } 463 464 public void setActivateObjectFail(boolean activateObjectFail) { 465 this.activateObjectFail = activateObjectFail; 466 } 467 468 public boolean isPassivateObjectFail() { 469 return passivateObjectFail; 470 } 471 472 public void setPassivateObjectFail(boolean passivateObjectFail) { 473 this.passivateObjectFail = passivateObjectFail; 474 } 475 476 public Object makeObject(final Object key) throws Exception { 477 final MethodCall call = new MethodCall("makeObject", key); 478 methodCalls.add(call); 479 int count = this.count++; 480 if (makeObjectFail) { 481 throw new PrivateException("makeObject"); 482 } 483 final Integer obj = new Integer(count); 484 call.setReturned(obj); 485 return obj; 486 } 487 488 public void activateObject(final Object key, final Object obj) throws Exception { 489 methodCalls.add(new MethodCall("activateObject", key, obj)); 490 if (activateObjectFail) { 491 throw new PrivateException("activateObject"); 492 } 493 } 494 495 public boolean validateObject(final Object key, final Object obj) { 496 final MethodCall call = new MethodCall("validateObject", key, obj); 497 methodCalls.add(call); 498 if (validateObjectFail) { 499 throw new PrivateException("validateObject"); 500 } 501 final boolean r = true; 502 call.returned(new Boolean(r)); 503 return r; 504 } 505 506 public void passivateObject(final Object key, final Object obj) throws Exception { 507 methodCalls.add(new MethodCall("passivateObject", key, obj)); 508 if (passivateObjectFail) { 509 throw new PrivateException("passivateObject"); 510 } 511 } 512 513 public void destroyObject(final Object key, final Object obj) throws Exception { 514 methodCalls.add(new MethodCall("destroyObject", key, obj)); 515 if (destroyObjectFail) { 516 throw new PrivateException("destroyObject"); 517 } 518 } 519 } 520 }