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 }