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 org.apache.commons.pool.BaseKeyedObjectPool; 021 import org.apache.commons.pool.KeyedObjectPool; 022 import org.apache.commons.pool.KeyedPoolableObjectFactory; 023 import org.apache.commons.pool.PoolUtils; 024 025 import java.util.HashMap; 026 import java.util.Iterator; 027 import java.util.Map; 028 import java.util.NoSuchElementException; 029 import java.util.Stack; 030 031 /** 032 * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> implementation. 033 * <p> 034 * Given a {@link KeyedPoolableObjectFactory}, this class will maintain 035 * a simple pool of instances. A finite number of "sleeping" 036 * or inactive instances is enforced, but when the pool is 037 * empty, new instances are created to support the new load. 038 * Hence this class places no limit on the number of "active" 039 * instances created by the pool, but is quite useful for 040 * re-using <code>Object</code>s without introducing 041 * artificial limits. 042 * </p> 043 * 044 * @author Rodney Waldhoff 045 * @author Sandy McArthur 046 * @version $Revision: 990437 $ $Date: 2010-08-28 13:17:57 -0700 (Sat, 28 Aug 2010) $ 047 * @see Stack 048 * @since Pool 1.0 049 */ 050 public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool { 051 /** 052 * Create a new pool using no factory. 053 * Clients must first set the {@link #setFactory factory} or 054 * may populate the pool using {@link #returnObject returnObject} 055 * before they can be {@link #borrowObject borrowed}. 056 * 057 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory) 058 * @see #setFactory(KeyedPoolableObjectFactory) 059 */ 060 public StackKeyedObjectPool() { 061 this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY); 062 } 063 064 /** 065 * Create a new pool using no factory. 066 * Clients must first set the {@link #setFactory factory} or 067 * may populate the pool using {@link #returnObject returnObject} 068 * before they can be {@link #borrowObject borrowed}. 069 * 070 * @param max cap on the number of "sleeping" instances in the pool 071 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int) 072 * @see #setFactory(KeyedPoolableObjectFactory) 073 */ 074 public StackKeyedObjectPool(int max) { 075 this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY); 076 } 077 078 /** 079 * Create a new pool using no factory. 080 * Clients must first set the {@link #setFactory factory} or 081 * may populate the pool using {@link #returnObject returnObject} 082 * before they can be {@link #borrowObject borrowed}. 083 * 084 * @param max cap on the number of "sleeping" instances in the pool 085 * @param init initial size of the pool (this specifies the size of the container, 086 * it does not cause the pool to be pre-populated.) 087 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int) 088 * @see #setFactory(KeyedPoolableObjectFactory) 089 */ 090 public StackKeyedObjectPool(int max, int init) { 091 this((KeyedPoolableObjectFactory)null,max,init); 092 } 093 094 /** 095 * Create a new <code>SimpleKeyedObjectPool</code> using 096 * the specified <code>factory</code> to create new instances. 097 * 098 * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool 099 */ 100 public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) { 101 this(factory,DEFAULT_MAX_SLEEPING); 102 } 103 104 /** 105 * Create a new <code>SimpleKeyedObjectPool</code> using 106 * the specified <code>factory</code> to create new instances. 107 * capping the number of "sleeping" instances to <code>max</code> 108 * 109 * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool 110 * @param max cap on the number of "sleeping" instances in the pool 111 */ 112 public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max) { 113 this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY); 114 } 115 116 /** 117 * Create a new <code>SimpleKeyedObjectPool</code> using 118 * the specified <code>factory</code> to create new instances. 119 * capping the number of "sleeping" instances to <code>max</code>, 120 * and initially allocating a container capable of containing 121 * at least <code>init</code> instances. 122 * 123 * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool 124 * @param max cap on the number of "sleeping" instances in the pool 125 * @param init initial size of the pool (this specifies the size of the container, 126 * it does not cause the pool to be pre-populated.) 127 */ 128 public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) { 129 _factory = factory; 130 _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max); 131 _initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init); 132 _pools = new HashMap(); 133 _activeCount = new HashMap(); 134 } 135 136 /** 137 * Borrows an object with the given key. If there are no idle instances under the 138 * given key, a new one is created. 139 * 140 * @param key the pool key 141 * @return keyed poolable object instance 142 */ 143 public synchronized Object borrowObject(Object key) throws Exception { 144 assertOpen(); 145 Stack stack = (Stack)(_pools.get(key)); 146 if(null == stack) { 147 stack = new Stack(); 148 stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); 149 _pools.put(key,stack); 150 } 151 Object obj = null; 152 do { 153 boolean newlyMade = false; 154 if (!stack.empty()) { 155 obj = stack.pop(); 156 _totIdle--; 157 } else { 158 if(null == _factory) { 159 throw new NoSuchElementException("pools without a factory cannot create new objects as needed."); 160 } else { 161 obj = _factory.makeObject(key); 162 newlyMade = true; 163 } 164 } 165 if (null != _factory && null != obj) { 166 try { 167 _factory.activateObject(key, obj); 168 if (!_factory.validateObject(key, obj)) { 169 throw new Exception("ValidateObject failed"); 170 } 171 } catch (Throwable t) { 172 PoolUtils.checkRethrow(t); 173 try { 174 _factory.destroyObject(key,obj); 175 } catch (Throwable t2) { 176 PoolUtils.checkRethrow(t2); 177 // swallowed 178 } finally { 179 obj = null; 180 } 181 if (newlyMade) { 182 throw new NoSuchElementException( 183 "Could not create a validated object, cause: " + 184 t.getMessage()); 185 } 186 } 187 } 188 } while (obj == null); 189 incrementActiveCount(key); 190 return obj; 191 } 192 193 /** 194 * Returns <code>obj</code> to the pool under <code>key</code>. If adding the 195 * returning instance to the pool results in {@link #_maxSleeping maxSleeping} 196 * exceeded for the given key, the oldest instance in the idle object pool 197 * is destroyed to make room for the returning instance. 198 * 199 * @param key the pool key 200 * @param obj returning instance 201 */ 202 public synchronized void returnObject(Object key, Object obj) throws Exception { 203 decrementActiveCount(key); 204 if (null != _factory) { 205 if (_factory.validateObject(key, obj)) { 206 try { 207 _factory.passivateObject(key, obj); 208 } catch (Exception ex) { 209 _factory.destroyObject(key, obj); 210 return; 211 } 212 } else { 213 return; 214 } 215 } 216 217 if (isClosed()) { 218 if (null != _factory) { 219 try { 220 _factory.destroyObject(key, obj); 221 } catch (Exception e) { 222 // swallowed 223 } 224 } 225 return; 226 } 227 228 Stack stack = (Stack)_pools.get(key); 229 if(null == stack) { 230 stack = new Stack(); 231 stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); 232 _pools.put(key,stack); 233 } 234 final int stackSize = stack.size(); 235 if (stackSize >= _maxSleeping) { 236 final Object staleObj; 237 if (stackSize > 0) { 238 staleObj = stack.remove(0); 239 _totIdle--; 240 } else { 241 staleObj = obj; 242 } 243 if(null != _factory) { 244 try { 245 _factory.destroyObject(key, staleObj); 246 } catch (Exception e) { 247 // swallowed 248 } 249 } 250 } 251 stack.push(obj); 252 _totIdle++; 253 } 254 255 /** 256 * {@inheritDoc} 257 */ 258 public synchronized void invalidateObject(Object key, Object obj) throws Exception { 259 decrementActiveCount(key); 260 if(null != _factory) { 261 _factory.destroyObject(key,obj); 262 } 263 notifyAll(); // _totalActive has changed 264 } 265 266 /** 267 * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory}, 268 * passivate it, and then placed in the idle object pool. 269 * <code>addObject</code> is useful for "pre-loading" a pool with idle objects. 270 * 271 * @param key the key a new instance should be added to 272 * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails. 273 * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been called on this pool. 274 */ 275 public synchronized void addObject(Object key) throws Exception { 276 assertOpen(); 277 if (_factory == null) { 278 throw new IllegalStateException("Cannot add objects without a factory."); 279 } 280 Object obj = _factory.makeObject(key); 281 try { 282 if (!_factory.validateObject(key, obj)) { 283 return; 284 } 285 } catch (Exception e) { 286 try { 287 _factory.destroyObject(key, obj); 288 } catch (Exception e2) { 289 // swallowed 290 } 291 return; 292 } 293 _factory.passivateObject(key, obj); 294 295 Stack stack = (Stack)_pools.get(key); 296 if(null == stack) { 297 stack = new Stack(); 298 stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); 299 _pools.put(key,stack); 300 } 301 302 final int stackSize = stack.size(); 303 if (stackSize >= _maxSleeping) { 304 final Object staleObj; 305 if (stackSize > 0) { 306 staleObj = stack.remove(0); 307 _totIdle--; 308 } else { 309 staleObj = obj; 310 } 311 try { 312 _factory.destroyObject(key, staleObj); 313 } catch (Exception e) { 314 // Don't swallow destroying the newly created object. 315 if (obj == staleObj) { 316 throw e; 317 } 318 } 319 } else { 320 stack.push(obj); 321 _totIdle++; 322 } 323 } 324 325 /** 326 * Returns the total number of instances currently idle in this pool. 327 * 328 * @return the total number of instances currently idle in this pool 329 */ 330 public synchronized int getNumIdle() { 331 return _totIdle; 332 } 333 334 /** 335 * Returns the total number of instances current borrowed from this pool but not yet returned. 336 * 337 * @return the total number of instances currently borrowed from this pool 338 */ 339 public synchronized int getNumActive() { 340 return _totActive; 341 } 342 343 /** 344 * Returns the number of instances currently borrowed from but not yet returned 345 * to the pool corresponding to the given <code>key</code>. 346 * 347 * @param key the key to query 348 * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool 349 */ 350 public synchronized int getNumActive(Object key) { 351 return getActiveCount(key); 352 } 353 354 /** 355 * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool. 356 * 357 * @param key the key to query 358 * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool 359 */ 360 public synchronized int getNumIdle(Object key) { 361 try { 362 return((Stack)(_pools.get(key))).size(); 363 } catch(Exception e) { 364 return 0; 365 } 366 } 367 368 /** 369 * Clears the pool, removing all pooled instances. 370 */ 371 public synchronized void clear() { 372 Iterator it = _pools.keySet().iterator(); 373 while(it.hasNext()) { 374 Object key = it.next(); 375 Stack stack = (Stack)(_pools.get(key)); 376 destroyStack(key,stack); 377 } 378 _totIdle = 0; 379 _pools.clear(); 380 _activeCount.clear(); 381 } 382 383 /** 384 * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>. 385 * 386 * @param key the key to clear 387 */ 388 public synchronized void clear(Object key) { 389 Stack stack = (Stack)(_pools.remove(key)); 390 destroyStack(key,stack); 391 } 392 393 /** 394 * Destroys all instances in the stack and clears the stack. 395 * 396 * @param key key passed to factory when destroying instances 397 * @param stack stack to destroy 398 */ 399 private synchronized void destroyStack(Object key, Stack stack) { 400 if(null == stack) { 401 return; 402 } else { 403 if(null != _factory) { 404 Iterator it = stack.iterator(); 405 while(it.hasNext()) { 406 try { 407 _factory.destroyObject(key,it.next()); 408 } catch(Exception e) { 409 // ignore error, keep destroying the rest 410 } 411 } 412 } 413 _totIdle -= stack.size(); 414 _activeCount.remove(key); 415 stack.clear(); 416 } 417 } 418 419 /** 420 * Returns a string representation of this StackKeyedObjectPool, including 421 * the number of pools, the keys and the size of each keyed pool. 422 * 423 * @return Keys and pool sizes 424 */ 425 public synchronized String toString() { 426 StringBuffer buf = new StringBuffer(); 427 buf.append(getClass().getName()); 428 buf.append(" contains ").append(_pools.size()).append(" distinct pools: "); 429 Iterator it = _pools.keySet().iterator(); 430 while(it.hasNext()) { 431 Object key = it.next(); 432 buf.append(" |").append(key).append("|="); 433 Stack s = (Stack)(_pools.get(key)); 434 buf.append(s.size()); 435 } 436 return buf.toString(); 437 } 438 439 /** 440 * Close this pool, and free any resources associated with it. 441 * <p> 442 * Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking 443 * this method on a pool will cause them to throw an {@link IllegalStateException}. 444 * </p> 445 * 446 * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed. 447 */ 448 public void close() throws Exception { 449 super.close(); 450 clear(); 451 } 452 453 /** 454 * Sets the {@link KeyedPoolableObjectFactory factory} the pool uses 455 * to create new instances. 456 * Trying to change the <code>factory</code> after a pool has been used will frequently 457 * throw an {@link UnsupportedOperationException}. 458 * 459 * @param factory the {@link KeyedPoolableObjectFactory} used to manage object instances 460 * @throws IllegalStateException when the factory cannot be set at this time 461 * @deprecated to be removed in pool 2.0 462 */ 463 public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException { 464 if(0 < getNumActive()) { 465 throw new IllegalStateException("Objects are already active"); 466 } else { 467 clear(); 468 _factory = factory; 469 } 470 } 471 472 /** 473 * @return the {@link KeyedPoolableObjectFactory} used by this pool to manage object instances. 474 * @since 1.5.5 475 */ 476 public synchronized KeyedPoolableObjectFactory getFactory() { 477 return _factory; 478 } 479 480 /** 481 * Returns the active instance count for the given key. 482 * 483 * @param key pool key 484 * @return active count 485 */ 486 private int getActiveCount(Object key) { 487 try { 488 return ((Integer)_activeCount.get(key)).intValue(); 489 } catch(NoSuchElementException e) { 490 return 0; 491 } catch(NullPointerException e) { 492 return 0; 493 } 494 } 495 496 /** 497 * Increment the active count for the given key. Also 498 * increments the total active count. 499 * 500 * @param key pool key 501 */ 502 private void incrementActiveCount(Object key) { 503 _totActive++; 504 Integer old = (Integer)(_activeCount.get(key)); 505 if(null == old) { 506 _activeCount.put(key,new Integer(1)); 507 } else { 508 _activeCount.put(key,new Integer(old.intValue() + 1)); 509 } 510 } 511 512 /** 513 * Decrements the active count for the given key. 514 * Also decrements the total active count. 515 * 516 * @param key pool key 517 */ 518 private void decrementActiveCount(Object key) { 519 _totActive--; 520 Integer active = (Integer)(_activeCount.get(key)); 521 if(null == active) { 522 // do nothing, either null or zero is OK 523 } else if(active.intValue() <= 1) { 524 _activeCount.remove(key); 525 } else { 526 _activeCount.put(key, new Integer(active.intValue() - 1)); 527 } 528 } 529 530 531 /** 532 * @return map of keyed pools 533 * @since 1.5.5 534 */ 535 public Map getPools() { 536 return _pools; 537 } 538 539 /** 540 * @return the cap on the number of "sleeping" instances in <code>each</code> pool. 541 * @since 1.5.5 542 */ 543 public int getMaxSleeping() { 544 return _maxSleeping; 545 } 546 547 /** 548 * @return the initial capacity of each pool. 549 * @since 1.5.5 550 */ 551 public int getInitSleepingCapacity() { 552 return _initSleepingCapacity; 553 } 554 555 /** 556 * @return the _totActive 557 */ 558 public int getTotActive() { 559 return _totActive; 560 } 561 562 /** 563 * @return the _totIdle 564 */ 565 public int getTotIdle() { 566 return _totIdle; 567 } 568 569 /** 570 * @return the _activeCount 571 * @since 1.5.5 572 */ 573 public Map getActiveCount() { 574 return _activeCount; 575 } 576 577 578 /** The default cap on the number of "sleeping" instances in the pool. */ 579 protected static final int DEFAULT_MAX_SLEEPING = 8; 580 581 /** 582 * The default initial size of the pool 583 * (this specifies the size of the container, it does not 584 * cause the pool to be pre-populated.) 585 */ 586 protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4; 587 588 /** 589 * My named-set of pools. 590 * @deprecated to be removed in pool 2.0. Use {@link #getPools()} 591 */ 592 protected HashMap _pools = null; 593 594 /** 595 * My {@link KeyedPoolableObjectFactory}. 596 * @deprecated to be removed in pool 2.0. Use {@link #getFactory()} 597 */ 598 protected KeyedPoolableObjectFactory _factory = null; 599 600 /** 601 * The cap on the number of "sleeping" instances in <code>each</code> pool. 602 * @deprecated to be removed in pool 2.0. Use {@link #getMaxSleeping()} 603 */ 604 protected int _maxSleeping = DEFAULT_MAX_SLEEPING; 605 606 /** 607 * The initial capacity of each pool. 608 * @deprecated to be removed in pool 2.0. Use {@link #getInitSleepingCapacity()}. 609 */ 610 protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY; 611 612 /** 613 * Total number of object borrowed and not yet returned for all pools. 614 * @deprecated to be removed in pool 2.0. Use {@link #getTotActive()}. 615 */ 616 protected int _totActive = 0; 617 618 /** 619 * Total number of objects "sleeping" for all pools 620 * @deprecated to be removed in pool 2.0. Use {@link #getTotIdle()}. 621 */ 622 protected int _totIdle = 0; 623 624 /** 625 * Number of active objects borrowed and not yet returned by pool 626 * @deprecated to be removed in pool 2.0. Use {@link #getActiveCount()}. 627 */ 628 protected HashMap _activeCount = null; 629 630 }