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 org.apache.commons.pool.BaseKeyedObjectPool;
21  import org.apache.commons.pool.KeyedObjectPool;
22  import org.apache.commons.pool.KeyedPoolableObjectFactory;
23  import org.apache.commons.pool.PoolUtils;
24  
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.Map;
28  import java.util.NoSuchElementException;
29  import java.util.Stack;
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   * @author Rodney Waldhoff
45   * @author Sandy McArthur
46   * @version $Revision: 990437 $ $Date: 2010-08-28 13:17:57 -0700 (Sat, 28 Aug 2010) $
47   * @see Stack
48   * @since Pool 1.0
49   */
50  public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
51      /**
52       * Create a new pool using no factory.
53       * Clients must first set the {@link #setFactory factory} or
54       * may populate the pool using {@link #returnObject returnObject}
55       * before they can be {@link #borrowObject borrowed}.
56       *
57       * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
58       * @see #setFactory(KeyedPoolableObjectFactory)
59       */
60      public StackKeyedObjectPool() {
61          this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
62      }
63  
64      /**
65       * Create a new pool using no factory.
66       * Clients must first set the {@link #setFactory factory} or
67       * may populate the pool using {@link #returnObject returnObject}
68       * before they can be {@link #borrowObject borrowed}.
69       *
70       * @param max cap on the number of "sleeping" instances in the pool
71       * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
72       * @see #setFactory(KeyedPoolableObjectFactory)
73       */
74      public StackKeyedObjectPool(int max) {
75          this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
76      }
77  
78      /**
79       * Create a new pool using no factory.
80       * Clients must first set the {@link #setFactory factory} or
81       * may populate the pool using {@link #returnObject returnObject}
82       * before they can be {@link #borrowObject borrowed}.
83       *
84       * @param max cap on the number of "sleeping" instances in the pool
85       * @param init initial size of the pool (this specifies the size of the container,
86       *             it does not cause the pool to be pre-populated.)
87       * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
88       * @see #setFactory(KeyedPoolableObjectFactory)
89       */
90      public StackKeyedObjectPool(int max, int init) {
91          this((KeyedPoolableObjectFactory)null,max,init);
92      }
93  
94      /**
95       * Create a new <code>SimpleKeyedObjectPool</code> using
96       * the specified <code>factory</code> to create new instances.
97       *
98       * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
99       */
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 }