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 }