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  
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: 778880 $ $Date: 2009-05-26 16:46:22 -0400 (Tue, 26 May 2009) $
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                         // 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 }