View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.pool.impl;
19  
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.Map;
23  import java.util.NoSuchElementException;
24  import java.util.Stack;
25  
26  import org.apache.commons.pool.BaseKeyedObjectPool;
27  import org.apache.commons.pool.KeyedObjectPool;
28  import org.apache.commons.pool.KeyedPoolableObjectFactory;
29  import org.apache.commons.pool.PoolUtils;
30  
31  /**
32   * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> implementation.
33   * <p>
34   * Given a {@link KeyedPoolableObjectFactory}, this class will maintain
35   * a simple pool of instances.  A finite number of "sleeping"
36   * or inactive instances is enforced, but when the pool is
37   * empty, new instances are created to support the new load.
38   * Hence this class places no limit on the number of "active"
39   * instances created by the pool, but is quite useful for
40   * re-using <code>Object</code>s without introducing
41   * artificial limits.
42   * </p>
43   *
44   * @param <K> the type of keys in this pool
45   * @param <V> the type of objects held in this pool
46   * 
47   * @author Rodney Waldhoff
48   * @author Sandy McArthur
49   * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
50   * @see Stack
51   * @since Pool 1.0
52   */
53  public class StackKeyedObjectPool<K, V> extends BaseKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
54      /**
55       * Create a new pool using no factory.
56       * Clients must first set the {@link #setFactory factory} or
57       * may populate the pool using {@link #returnObject returnObject}
58       * before they can be {@link #borrowObject borrowed}.
59       *
60       * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
61       * @see #setFactory(KeyedPoolableObjectFactory)
62       */
63      public StackKeyedObjectPool() {
64          this(null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
65      }
66  
67      /**
68       * Create a new pool using no factory.
69       * Clients must first set the {@link #setFactory factory} or
70       * may populate the pool using {@link #returnObject returnObject}
71       * before they can be {@link #borrowObject borrowed}.
72       *
73       * @param max cap on the number of "sleeping" instances in the pool
74       * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
75       * @see #setFactory(KeyedPoolableObjectFactory)
76       */
77      public StackKeyedObjectPool(int max) {
78          this(null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
79      }
80  
81      /**
82       * Create a new pool using no factory.
83       * Clients must first set the {@link #setFactory factory} or
84       * may populate the pool using {@link #returnObject returnObject}
85       * before they can be {@link #borrowObject borrowed}.
86       *
87       * @param max cap on the number of "sleeping" instances in the pool
88       * @param init initial size of the pool (this specifies the size of the container,
89       *             it does not cause the pool to be pre-populated.)
90       * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
91       * @see #setFactory(KeyedPoolableObjectFactory)
92       */
93      public StackKeyedObjectPool(int max, int init) {
94          this(null,max,init);
95      }
96  
97      /**
98       * Create a new <code>SimpleKeyedObjectPool</code> using
99       * the specified <code>factory</code> to create new instances.
100      *
101      * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
102      */
103     public StackKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory) {
104         this(factory,DEFAULT_MAX_SLEEPING);
105     }
106 
107     /**
108      * Create a new <code>SimpleKeyedObjectPool</code> using
109      * the specified <code>factory</code> to create new instances.
110      * capping the number of "sleeping" instances to <code>max</code>
111      *
112      * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
113      * @param max cap on the number of "sleeping" instances in the pool
114      */
115     public StackKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory, int max) {
116         this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
117     }
118 
119     /**
120      * Create a new <code>SimpleKeyedObjectPool</code> using
121      * the specified <code>factory</code> to create new instances.
122      * capping the number of "sleeping" instances to <code>max</code>,
123      * and initially allocating a container capable of containing
124      * at least <code>init</code> instances.
125      *
126      * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
127      * @param max cap on the number of "sleeping" instances in the pool
128      * @param init initial size of the pool (this specifies the size of the container,
129      *             it does not cause the pool to be pre-populated.)
130      */
131     public StackKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory, int max, int init) {
132         _factory = factory;
133         _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max);
134         _initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
135         _pools = new HashMap<K, Stack<V>>();
136         _activeCount = new HashMap<K, Integer>();
137     }
138 
139     /**
140      * Borrows an object with the given key.  If there are no idle instances under the
141      * given key, a new one is created.
142      * 
143      * @param key the pool key
144      * @return keyed poolable object instance
145      */
146     @Override
147     public synchronized V borrowObject(K key) throws Exception {
148         assertOpen();
149         Stack<V> stack = (_pools.get(key));
150         if(null == stack) {
151             stack = new Stack<V>();
152             stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
153             _pools.put(key,stack);
154         }
155         V obj = null;
156         do {
157             boolean newlyMade = false;
158             if (!stack.empty()) {
159                 obj = stack.pop();
160                 _totIdle--;
161             } else {
162                 if(null == _factory) {
163                     throw new NoSuchElementException("pools without a factory cannot create new objects as needed.");
164                 } else {
165                     obj = _factory.makeObject(key);
166                     newlyMade = true;
167                 }
168             }
169             if (null != _factory && null != obj) {
170                 try {
171                     _factory.activateObject(key, obj);
172                     if (!_factory.validateObject(key, obj)) {
173                         throw new Exception("ValidateObject failed");
174                     }
175                 } catch (Throwable t) {
176                     PoolUtils.checkRethrow(t);
177                     try {
178                         _factory.destroyObject(key,obj);
179                     } catch (Throwable t2) {
180                         PoolUtils.checkRethrow(t2);
181                         // swallowed
182                     } finally {
183                         obj = null;
184                     }
185                     if (newlyMade) {
186                         throw new NoSuchElementException(
187                             "Could not create a validated object, cause: " +
188                             t.getMessage());
189                     }
190                 }
191             }
192         } while (obj == null);
193         incrementActiveCount(key);
194         return obj;
195     }
196 
197     /**
198      * Returns <code>obj</code> to the pool under <code>key</code>.  If adding the
199      * returning instance to the pool results in {@link #_maxSleeping maxSleeping}
200      * exceeded for the given key, the oldest instance in the idle object pool
201      * is destroyed to make room for the returning instance.
202      * 
203      * @param key the pool key
204      * @param obj returning instance
205      */
206     @Override
207     public synchronized void returnObject(K key, V obj) throws Exception {
208         decrementActiveCount(key);
209         if (null != _factory) {
210             if (_factory.validateObject(key, obj)) {
211                 try {
212                     _factory.passivateObject(key, obj);
213                 } catch (Exception ex) {
214                     _factory.destroyObject(key, obj);
215                     return;
216                 }
217             } else {
218                 return;
219             }
220         }
221 
222         if (isClosed()) {
223             if (null != _factory) {
224                 try {
225                     _factory.destroyObject(key, obj);
226                 } catch (Exception e) {
227                     // swallowed
228                 }
229             }
230             return;
231         }
232 
233         Stack<V> stack = _pools.get(key);
234         if(null == stack) {
235             stack = new Stack<V>();
236             stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
237             _pools.put(key,stack);
238         }
239         final int stackSize = stack.size();
240         if (stackSize >= _maxSleeping) {
241             final V staleObj;
242             if (stackSize > 0) {
243                 staleObj = stack.remove(0);
244                 _totIdle--;
245             } else {
246                 staleObj = obj;
247             }
248             if(null != _factory) {
249                 try {
250                     _factory.destroyObject(key, staleObj);
251                 } catch (Exception e) {
252                     // swallowed
253                 }
254             }
255         }
256         stack.push(obj);
257         _totIdle++;
258     }
259 
260     /**
261      * {@inheritDoc}
262      */
263     @Override
264     public synchronized void invalidateObject(K key, V obj) throws Exception {
265         decrementActiveCount(key);
266         if(null != _factory) {
267             _factory.destroyObject(key,obj);
268         }
269         notifyAll(); // _totalActive has changed
270     }
271 
272     /**
273      * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
274      * passivate it, and then placed in the idle object pool.
275      * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
276      *
277      * @param key the key a new instance should be added to
278      * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
279      * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been called on this pool.
280      */
281     @Override
282     public synchronized void addObject(K key) throws Exception {
283         assertOpen();
284         if (_factory == null) {
285             throw new IllegalStateException("Cannot add objects without a factory.");
286         }
287         V obj = _factory.makeObject(key);
288         try {
289             if (!_factory.validateObject(key, obj)) {
290                return;
291             }
292         } catch (Exception e) {
293             try {
294                 _factory.destroyObject(key, obj);
295             } catch (Exception e2) {
296                 // swallowed
297             }
298             return;
299         }
300         _factory.passivateObject(key, obj);
301 
302         Stack<V> stack = _pools.get(key);
303         if(null == stack) {
304             stack = new Stack<V>();
305             stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
306             _pools.put(key,stack);
307         }
308 
309         final int stackSize = stack.size();
310         if (stackSize >= _maxSleeping) {
311             final V staleObj;
312             if (stackSize > 0) {
313                 staleObj = stack.remove(0);
314                 _totIdle--;
315             } else {
316                 staleObj = obj;
317             }
318             try {
319                 _factory.destroyObject(key, staleObj);
320             } catch (Exception e) {
321                 // Don't swallow destroying the newly created object.
322                 if (obj == staleObj) {
323                     throw e;
324                 }
325             }
326         } else {
327             stack.push(obj);
328             _totIdle++;
329         }
330     }
331 
332     /**
333      * Returns the total number of instances currently idle in this pool.
334      *
335      * @return the total number of instances currently idle in this pool
336      */
337     @Override
338     public synchronized int getNumIdle() {
339         return _totIdle;
340     }
341 
342     /**
343      * Returns the total number of instances current borrowed from this pool but not yet returned.
344      *
345      * @return the total number of instances currently borrowed from this pool
346      */
347     @Override
348     public synchronized int getNumActive() {
349         return _totActive;
350     }
351 
352     /**
353      * Returns the number of instances currently borrowed from but not yet returned
354      * to the pool corresponding to the given <code>key</code>.
355      *
356      * @param key the key to query
357      * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
358      */
359     @Override
360     public synchronized int getNumActive(K key) {
361         return getActiveCount(key);
362     }
363 
364     /**
365      * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
366      *
367      * @param key the key to query
368      * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
369      */
370     @Override
371     public synchronized int getNumIdle(K key) {
372         try {
373             return(_pools.get(key)).size();
374         } catch(Exception e) {
375             return 0;
376         }
377     }
378 
379     /**
380      * Clears the pool, removing all pooled instances.
381      */
382     @Override
383     public synchronized void clear() {
384         Iterator<K> it = _pools.keySet().iterator();
385         while(it.hasNext()) {
386             K key = it.next();
387             Stack<V> stack = _pools.get(key);
388             destroyStack(key,stack);
389         }
390         _totIdle = 0;
391         _pools.clear();
392         _activeCount.clear();
393     }
394 
395     /**
396      * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
397      *
398      * @param key the key to clear
399      */
400     @Override
401     public synchronized void clear(K key) {
402         Stack<V> stack = _pools.remove(key);
403         destroyStack(key,stack);
404     }
405 
406     /**
407      * Destroys all instances in the stack and clears the stack.
408      * 
409      * @param key key passed to factory when destroying instances
410      * @param stack stack to destroy
411      */
412     private synchronized void destroyStack(K key, Stack<V> stack) {
413         if(null == stack) {
414             return;
415         } else {
416             if(null != _factory) {
417                 Iterator<V> it = stack.iterator();
418                 while(it.hasNext()) {
419                     try {
420                         _factory.destroyObject(key,it.next());
421                     } catch(Exception e) {
422                         // ignore error, keep destroying the rest
423                     }
424                 }
425             }
426             _totIdle -= stack.size();
427             _activeCount.remove(key);
428             stack.clear();
429         }
430     }
431 
432     /**
433      * Returns a string representation of this StackKeyedObjectPool, including
434      * the number of pools, the keys and the size of each keyed pool.
435      * 
436      * @return Keys and pool sizes
437      */
438     @Override
439     public synchronized String toString() {
440         StringBuffer buf = new StringBuffer();
441         buf.append(getClass().getName());
442         buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
443         Iterator<K> it = _pools.keySet().iterator();
444         while(it.hasNext()) {
445             K key = it.next();
446             buf.append(" |").append(key).append("|=");
447             Stack<V> s = _pools.get(key);
448             buf.append(s.size());
449         }
450         return buf.toString();
451     }
452 
453     /**
454      * Close this pool, and free any resources associated with it.
455      * <p>
456      * Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking
457      * this method on a pool will cause them to throw an {@link IllegalStateException}.
458      * </p>
459      *
460      * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed.
461      */
462     @Override
463     public void close() throws Exception {
464         super.close();
465         clear();
466     }
467 
468     /**
469      * Sets the {@link KeyedPoolableObjectFactory factory} the pool uses
470      * to create new instances.
471      * Trying to change the <code>factory</code> after a pool has been used will frequently
472      * throw an {@link UnsupportedOperationException}.
473      *
474      * @param factory the {@link KeyedPoolableObjectFactory} used to manage object instances
475      * @throws IllegalStateException when the factory cannot be set at this time
476      * @deprecated to be removed in pool 2.0
477      */
478     @Deprecated
479     @Override
480     public synchronized void setFactory(KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException {
481         if(0 < getNumActive()) {
482             throw new IllegalStateException("Objects are already active");
483         } else {
484             clear();
485             _factory = factory;
486         }
487     }
488     
489     /**
490      * @return the {@link KeyedPoolableObjectFactory} used by this pool to manage object instances.
491      * @since 1.5.5
492      */
493     public synchronized KeyedPoolableObjectFactory<K, V> getFactory() {
494         return _factory;
495     }
496 
497     /**
498      * Returns the active instance count for the given key.
499      * 
500      * @param key pool key
501      * @return active count
502      */
503     private int getActiveCount(K key) {
504         try {
505             return _activeCount.get(key).intValue();
506         } catch(NoSuchElementException e) {
507             return 0;
508         } catch(NullPointerException e) {
509             return 0;
510         }
511     }
512 
513     /**
514      * Increment the active count for the given key. Also
515      * increments the total active count.
516      * 
517      * @param key pool key
518      */
519     private void incrementActiveCount(K key) {
520         _totActive++;
521         Integer old = _activeCount.get(key);
522         if(null == old) {
523             _activeCount.put(key,new Integer(1));
524         } else {
525             _activeCount.put(key,new Integer(old.intValue() + 1));
526         }
527     }
528 
529     /**
530      * Decrements the active count for the given key.
531      * Also decrements the total active count.
532      * 
533      * @param key pool key
534      */
535     private void decrementActiveCount(K key) {
536         _totActive--;
537         Integer active = _activeCount.get(key);
538         if(null == active) {
539             // do nothing, either null or zero is OK
540         } else if(active.intValue() <= 1) {
541             _activeCount.remove(key);
542         } else {
543             _activeCount.put(key, new Integer(active.intValue() - 1));
544         }
545     }
546 
547     
548     /**
549      * @return map of keyed pools
550      * @since 1.5.5
551      */
552     public Map<K, Stack<V>> getPools() {
553         return _pools;
554     }
555 
556     /**
557      * @return the cap on the number of "sleeping" instances in <code>each</code> pool.
558      * @since 1.5.5
559      */
560     public int getMaxSleeping() {
561         return _maxSleeping;
562     }
563 
564     /**
565      * @return the initial capacity of each pool.
566      * @since 1.5.5
567      */
568     public int getInitSleepingCapacity() {
569         return _initSleepingCapacity;
570     }
571 
572     /**
573      * @return the _totActive
574      */
575     public int getTotActive() {
576         return _totActive;
577     }
578 
579     /**
580      * @return the _totIdle
581      */
582     public int getTotIdle() {
583         return _totIdle;
584     }
585 
586     /**
587      * @return the _activeCount
588      * @since 1.5.5
589      */
590     public Map<K, Integer> getActiveCount() {
591         return _activeCount;
592     }
593 
594 
595     /** The default cap on the number of "sleeping" instances in the pool. */
596     protected static final int DEFAULT_MAX_SLEEPING  = 8;
597 
598     /**
599      * The default initial size of the pool
600      * (this specifies the size of the container, it does not
601      * cause the pool to be pre-populated.)
602      */
603     protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
604 
605     /**
606      *  My named-set of pools.
607      *  @deprecated to be removed in pool 2.0.  Use {@link #getPools()}
608      */
609     @Deprecated
610     protected HashMap<K, Stack<V>> _pools = null;
611 
612     /**
613      * My {@link KeyedPoolableObjectFactory}.
614      * @deprecated to be removed in pool 2.0.  Use {@link #getFactory()}
615      */
616     @Deprecated
617     protected KeyedPoolableObjectFactory<K, V> _factory = null;
618 
619     /**
620      *  The cap on the number of "sleeping" instances in <code>each</code> pool.
621      *  @deprecated to be removed in pool 2.0.  Use {@link #getMaxSleeping()}
622      */
623     @Deprecated
624     protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
625 
626     /**
627      * The initial capacity of each pool.
628      * @deprecated to be removed in pool 2.0.  Use {@link #getInitSleepingCapacity()}.
629      */
630     @Deprecated
631     protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
632 
633     /**
634      * Total number of object borrowed and not yet returned for all pools.
635      * @deprecated to be removed in pool 2.0.  Use {@link #getTotActive()}.
636      */
637     @Deprecated
638     protected int _totActive = 0;
639 
640     /**
641      * Total number of objects "sleeping" for all pools
642      * @deprecated to be removed in pool 2.0.  Use {@link #getTotIdle()}.
643      */
644     @Deprecated
645     protected int _totIdle = 0;
646 
647     /**
648      * Number of active objects borrowed and not yet returned by pool
649      * @deprecated to be removed in pool 2.0.  Use {@link #getActiveCount()}.
650      */
651     @Deprecated
652     protected HashMap<K, Integer> _activeCount = null;
653 
654 }