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 024 import java.util.HashMap; 025 import java.util.Iterator; 026 import java.util.NoSuchElementException; 027 import java.util.Stack; 028 029 /** 030 * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> implementation. 031 * <p> 032 * Given a {@link KeyedPoolableObjectFactory}, this class will maintain 033 * a simple pool of instances. A finite number of "sleeping" 034 * or inactive instances is enforced, but when the pool is 035 * empty, new instances are created to support the new load. 036 * Hence this class places no limit on the number of "active" 037 * instances created by the pool, but is quite useful for 038 * re-using <code>Object</code>s without introducing 039 * artificial limits. 040 * </p> 041 * 042 * @author Rodney Waldhoff 043 * @author Sandy McArthur 044 * @version $Revision: 778880 $ $Date: 2009-05-26 16:46:22 -0400 (Tue, 26 May 2009) $ 045 * @see Stack 046 * @since Pool 1.0 047 */ 048 public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool { 049 /** 050 * Create a new pool using no factory. 051 * Clients must first set the {@link #setFactory factory} or 052 * may populate the pool using {@link #returnObject returnObject} 053 * before they can be {@link #borrowObject borrowed}. 054 * 055 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory) 056 * @see #setFactory(KeyedPoolableObjectFactory) 057 */ 058 public StackKeyedObjectPool() { 059 this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY); 060 } 061 062 /** 063 * Create a new pool using no factory. 064 * Clients must first set the {@link #setFactory factory} or 065 * may populate the pool using {@link #returnObject returnObject} 066 * before they can be {@link #borrowObject borrowed}. 067 * 068 * @param max cap on the number of "sleeping" instances in the pool 069 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int) 070 * @see #setFactory(KeyedPoolableObjectFactory) 071 */ 072 public StackKeyedObjectPool(int max) { 073 this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY); 074 } 075 076 /** 077 * Create a new pool using no factory. 078 * Clients must first set the {@link #setFactory factory} or 079 * may populate the pool using {@link #returnObject returnObject} 080 * before they can be {@link #borrowObject borrowed}. 081 * 082 * @param max cap on the number of "sleeping" instances in the pool 083 * @param init initial size of the pool (this specifies the size of the container, 084 * it does not cause the pool to be pre-populated.) 085 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int) 086 * @see #setFactory(KeyedPoolableObjectFactory) 087 */ 088 public StackKeyedObjectPool(int max, int init) { 089 this((KeyedPoolableObjectFactory)null,max,init); 090 } 091 092 /** 093 * Create a new <code>SimpleKeyedObjectPool</code> using 094 * the specified <code>factory</code> to create new instances. 095 * 096 * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool 097 */ 098 public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) { 099 this(factory,DEFAULT_MAX_SLEEPING); 100 } 101 102 /** 103 * Create a new <code>SimpleKeyedObjectPool</code> using 104 * the specified <code>factory</code> to create new instances. 105 * capping the number of "sleeping" instances to <code>max</code> 106 * 107 * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool 108 * @param max cap on the number of "sleeping" instances in the pool 109 */ 110 public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max) { 111 this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY); 112 } 113 114 /** 115 * Create a new <code>SimpleKeyedObjectPool</code> using 116 * the specified <code>factory</code> to create new instances. 117 * capping the number of "sleeping" instances to <code>max</code>, 118 * and initially allocating a container capable of containing 119 * at least <code>init</code> instances. 120 * 121 * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool 122 * @param max cap on the number of "sleeping" instances in the pool 123 * @param init initial size of the pool (this specifies the size of the container, 124 * it does not cause the pool to be pre-populated.) 125 */ 126 public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) { 127 _factory = factory; 128 _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max); 129 _initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init); 130 _pools = new HashMap(); 131 _activeCount = new HashMap(); 132 } 133 134 public synchronized Object borrowObject(Object key) throws Exception { 135 assertOpen(); 136 Stack stack = (Stack)(_pools.get(key)); 137 if(null == stack) { 138 stack = new Stack(); 139 stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); 140 _pools.put(key,stack); 141 } 142 Object obj = null; 143 do { 144 boolean newlyMade = false; 145 if (!stack.empty()) { 146 obj = stack.pop(); 147 _totIdle--; 148 } else { 149 if(null == _factory) { 150 throw new NoSuchElementException("pools without a factory cannot create new objects as needed."); 151 } else { 152 obj = _factory.makeObject(key); 153 newlyMade = true; 154 } 155 } 156 if (null != _factory && null != obj) { 157 try { 158 _factory.activateObject(key, obj); 159 if (!_factory.validateObject(key, obj)) { 160 throw new Exception("ValidateObject failed"); 161 } 162 } catch (Throwable t) { 163 try { 164 _factory.destroyObject(key,obj); 165 } catch (Throwable t2) { 166 // swallowed 167 } finally { 168 obj = null; 169 } 170 if (newlyMade) { 171 throw new NoSuchElementException( 172 "Could not create a validated object, cause: " + 173 t.getMessage()); 174 } 175 } 176 } 177 } while (obj == null); 178 incrementActiveCount(key); 179 return obj; 180 } 181 182 public synchronized void returnObject(Object key, Object obj) throws Exception { 183 decrementActiveCount(key); 184 if (null != _factory) { 185 if (_factory.validateObject(key, obj)) { 186 try { 187 _factory.passivateObject(key, obj); 188 } catch (Exception ex) { 189 _factory.destroyObject(key, obj); 190 return; 191 } 192 } else { 193 return; 194 } 195 } 196 197 if (isClosed()) { 198 if (null != _factory) { 199 try { 200 _factory.destroyObject(key, obj); 201 } catch (Exception e) { 202 // swallowed 203 } 204 } 205 return; 206 } 207 208 Stack stack = (Stack)_pools.get(key); 209 if(null == stack) { 210 stack = new Stack(); 211 stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); 212 _pools.put(key,stack); 213 } 214 final int stackSize = stack.size(); 215 if (stackSize >= _maxSleeping) { 216 final Object staleObj; 217 if (stackSize > 0) { 218 staleObj = stack.remove(0); 219 _totIdle--; 220 } else { 221 staleObj = obj; 222 } 223 if(null != _factory) { 224 try { 225 _factory.destroyObject(key, staleObj); 226 } catch (Exception e) { 227 // swallowed 228 } 229 } 230 } 231 stack.push(obj); 232 _totIdle++; 233 } 234 235 public synchronized void invalidateObject(Object key, Object obj) throws Exception { 236 decrementActiveCount(key); 237 if(null != _factory) { 238 _factory.destroyObject(key,obj); 239 } 240 notifyAll(); // _totalActive has changed 241 } 242 243 /** 244 * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory}, 245 * passivate it, and then placed in the idle object pool. 246 * <code>addObject</code> is useful for "pre-loading" a pool with idle objects. 247 * 248 * @param key the key a new instance should be added to 249 * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails. 250 * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been called on this pool. 251 */ 252 public synchronized void addObject(Object key) throws Exception { 253 assertOpen(); 254 if (_factory == null) { 255 throw new IllegalStateException("Cannot add objects without a factory."); 256 } 257 Object obj = _factory.makeObject(key); 258 try { 259 if (!_factory.validateObject(key, obj)) { 260 return; 261 } 262 } catch (Exception e) { 263 try { 264 _factory.destroyObject(key, obj); 265 } catch (Exception e2) { 266 // swallowed 267 } 268 return; 269 } 270 _factory.passivateObject(key, obj); 271 272 Stack stack = (Stack)_pools.get(key); 273 if(null == stack) { 274 stack = new Stack(); 275 stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); 276 _pools.put(key,stack); 277 } 278 279 final int stackSize = stack.size(); 280 if (stackSize >= _maxSleeping) { 281 final Object staleObj; 282 if (stackSize > 0) { 283 staleObj = stack.remove(0); 284 _totIdle--; 285 } else { 286 staleObj = obj; 287 } 288 try { 289 _factory.destroyObject(key, staleObj); 290 } catch (Exception e) { 291 // Don't swallow destroying the newly created object. 292 if (obj == staleObj) { 293 throw e; 294 } 295 } 296 } else { 297 stack.push(obj); 298 _totIdle++; 299 } 300 } 301 302 /** 303 * Returns the total number of instances currently idle in this pool. 304 * 305 * @return the total number of instances currently idle in this pool 306 */ 307 public synchronized int getNumIdle() { 308 return _totIdle; 309 } 310 311 /** 312 * Returns the total number of instances current borrowed from this pool but not yet returned. 313 * 314 * @return the total number of instances currently borrowed from this pool 315 */ 316 public synchronized int getNumActive() { 317 return _totActive; 318 } 319 320 /** 321 * Returns the number of instances currently borrowed from but not yet returned 322 * to the pool corresponding to the given <code>key</code>. 323 * 324 * @param key the key to query 325 * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool 326 */ 327 public synchronized int getNumActive(Object key) { 328 return getActiveCount(key); 329 } 330 331 /** 332 * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool. 333 * 334 * @param key the key to query 335 * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool 336 */ 337 public synchronized int getNumIdle(Object key) { 338 try { 339 return((Stack)(_pools.get(key))).size(); 340 } catch(Exception e) { 341 return 0; 342 } 343 } 344 345 /** 346 * Clears the pool, removing all pooled instances. 347 */ 348 public synchronized void clear() { 349 Iterator it = _pools.keySet().iterator(); 350 while(it.hasNext()) { 351 Object key = it.next(); 352 Stack stack = (Stack)(_pools.get(key)); 353 destroyStack(key,stack); 354 } 355 _totIdle = 0; 356 _pools.clear(); 357 _activeCount.clear(); 358 } 359 360 /** 361 * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>. 362 * 363 * @param key the key to clear 364 */ 365 public synchronized void clear(Object key) { 366 Stack stack = (Stack)(_pools.remove(key)); 367 destroyStack(key,stack); 368 } 369 370 private synchronized void destroyStack(Object key, Stack stack) { 371 if(null == stack) { 372 return; 373 } else { 374 if(null != _factory) { 375 Iterator it = stack.iterator(); 376 while(it.hasNext()) { 377 try { 378 _factory.destroyObject(key,it.next()); 379 } catch(Exception e) { 380 // ignore error, keep destroying the rest 381 } 382 } 383 } 384 _totIdle -= stack.size(); 385 _activeCount.remove(key); 386 stack.clear(); 387 } 388 } 389 390 public synchronized String toString() { 391 StringBuffer buf = new StringBuffer(); 392 buf.append(getClass().getName()); 393 buf.append(" contains ").append(_pools.size()).append(" distinct pools: "); 394 Iterator it = _pools.keySet().iterator(); 395 while(it.hasNext()) { 396 Object key = it.next(); 397 buf.append(" |").append(key).append("|="); 398 Stack s = (Stack)(_pools.get(key)); 399 buf.append(s.size()); 400 } 401 return buf.toString(); 402 } 403 404 /** 405 * Close this pool, and free any resources associated with it. 406 * <p> 407 * Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking 408 * this method on a pool will cause them to throw an {@link IllegalStateException}. 409 * </p> 410 * 411 * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed. 412 */ 413 public void close() throws Exception { 414 super.close(); 415 clear(); 416 } 417 418 /** 419 * Sets the {@link KeyedPoolableObjectFactory factory} the pool uses 420 * to create new instances. 421 * Trying to change the <code>factory</code> after a pool has been used will frequently 422 * throw an {@link UnsupportedOperationException}. 423 * 424 * @param factory the {@link KeyedPoolableObjectFactory} used to create new instances. 425 * @throws IllegalStateException when the factory cannot be set at this time 426 */ 427 public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException { 428 if(0 < getNumActive()) { 429 throw new IllegalStateException("Objects are already active"); 430 } else { 431 clear(); 432 _factory = factory; 433 } 434 } 435 436 private int getActiveCount(Object key) { 437 try { 438 return ((Integer)_activeCount.get(key)).intValue(); 439 } catch(NoSuchElementException e) { 440 return 0; 441 } catch(NullPointerException e) { 442 return 0; 443 } 444 } 445 446 private void incrementActiveCount(Object key) { 447 _totActive++; 448 Integer old = (Integer)(_activeCount.get(key)); 449 if(null == old) { 450 _activeCount.put(key,new Integer(1)); 451 } else { 452 _activeCount.put(key,new Integer(old.intValue() + 1)); 453 } 454 } 455 456 private void decrementActiveCount(Object key) { 457 _totActive--; 458 Integer active = (Integer)(_activeCount.get(key)); 459 if(null == active) { 460 // do nothing, either null or zero is OK 461 } else if(active.intValue() <= 1) { 462 _activeCount.remove(key); 463 } else { 464 _activeCount.put(key, new Integer(active.intValue() - 1)); 465 } 466 } 467 468 /** The default cap on the number of "sleeping" instances in the pool. */ 469 protected static final int DEFAULT_MAX_SLEEPING = 8; 470 471 /** 472 * The default initial size of the pool 473 * (this specifies the size of the container, it does not 474 * cause the pool to be pre-populated.) 475 */ 476 protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4; 477 478 /** My named-set of pools. */ 479 protected HashMap _pools = null; 480 481 /** My {@link KeyedPoolableObjectFactory}. */ 482 protected KeyedPoolableObjectFactory _factory = null; 483 484 /** The cap on the number of "sleeping" instances in <code>each</code> pool. */ 485 protected int _maxSleeping = DEFAULT_MAX_SLEEPING; 486 487 /** The initial capacity of each pool. */ 488 protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY; 489 490 /** Total number of object borrowed and not yet retuened for all pools */ 491 protected int _totActive = 0; 492 493 /** Total number of objects "sleeping" for all pools */ 494 protected int _totIdle = 0; 495 496 /** Number of active objects borrowed and not yet returned by pool */ 497 protected HashMap _activeCount = null; 498 499 }