1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.pool.impl;
19
20 import org.apache.commons.pool.BaseKeyedObjectPool;
21 import org.apache.commons.pool.KeyedObjectPool;
22 import org.apache.commons.pool.KeyedPoolableObjectFactory;
23
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.NoSuchElementException;
27 import java.util.Stack;
28
29 /***
30 * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> implementation.
31 * <p>
32 * Given a {@link KeyedPoolableObjectFactory}, this class will maintain
33 * a simple pool of instances. A finite number of "sleeping"
34 * or inactive instances is enforced, but when the pool is
35 * empty, new instances are created to support the new load.
36 * Hence this class places no limit on the number of "active"
37 * instances created by the pool, but is quite useful for
38 * re-using <code>Object</code>s without introducing
39 * artificial limits.
40 * </p>
41 *
42 * @author Rodney Waldhoff
43 * @author Sandy McArthur
44 * @version $Revision: 607154 $ $Date: 2007-12-27 18:37:53 -0700 (Thu, 27 Dec 2007) $
45 * @see Stack
46 * @since Pool 1.0
47 */
48 public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
49 /***
50 * Create a new pool using no factory.
51 * Clients must first set the {@link #setFactory factory} or
52 * may populate the pool using {@link #returnObject returnObject}
53 * before they can be {@link #borrowObject borrowed}.
54 *
55 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
56 * @see #setFactory(KeyedPoolableObjectFactory)
57 */
58 public StackKeyedObjectPool() {
59 this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
60 }
61
62 /***
63 * Create a new pool using no factory.
64 * Clients must first set the {@link #setFactory factory} or
65 * may populate the pool using {@link #returnObject returnObject}
66 * before they can be {@link #borrowObject borrowed}.
67 *
68 * @param max cap on the number of "sleeping" instances in the pool
69 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
70 * @see #setFactory(KeyedPoolableObjectFactory)
71 */
72 public StackKeyedObjectPool(int max) {
73 this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
74 }
75
76 /***
77 * Create a new pool using no factory.
78 * Clients must first set the {@link #setFactory factory} or
79 * may populate the pool using {@link #returnObject returnObject}
80 * before they can be {@link #borrowObject borrowed}.
81 *
82 * @param max cap on the number of "sleeping" instances in the pool
83 * @param init initial size of the pool (this specifies the size of the container,
84 * it does not cause the pool to be pre-populated.)
85 * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
86 * @see #setFactory(KeyedPoolableObjectFactory)
87 */
88 public StackKeyedObjectPool(int max, int init) {
89 this((KeyedPoolableObjectFactory)null,max,init);
90 }
91
92 /***
93 * Create a new <code>SimpleKeyedObjectPool</code> using
94 * the specified <code>factory</code> to create new instances.
95 *
96 * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
97 */
98 public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) {
99 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
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
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
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();
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
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
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 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
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
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 }