View Javadoc

1   /*
2    * Copyright 1999-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.pool.impl;
18  
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.NoSuchElementException;
22  import java.util.Stack;
23  
24  import org.apache.commons.pool.BaseKeyedObjectPool;
25  import org.apache.commons.pool.KeyedObjectPool;
26  import org.apache.commons.pool.KeyedPoolableObjectFactory;
27  
28  /***
29   * A simple, {@link java.util.Stack Stack}-based {@link KeyedObjectPool} implementation.
30   * <p>
31   * Given a {@link KeyedPoolableObjectFactory}, this class will maintain
32   * a simple pool of instances.  A finite number of "sleeping"
33   * or inactive instances is enforced, but when the pool is
34   * empty, new instances are created to support the new load.
35   * Hence this class places no limit on the number of "active"
36   * instances created by the pool, but is quite useful for
37   * re-using <tt>Object</tt>s without introducing
38   * artificial limits.
39   *
40   * @author Rodney Waldhoff
41   * @version $Revision: 328937 $ $Date: 2005-10-27 15:23:53 -0400 (Thu, 27 Oct 2005) $ 
42   */
43  public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
44      /***
45       * Create a new pool using
46       * no factory. Clients must first populate the pool
47       * using {@link #returnObject(java.lang.Object,java.lang.Object)}
48       * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
49       */
50      public StackKeyedObjectPool() {
51          this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
52      }
53  
54      /***
55       * Create a new pool using
56       * no factory. Clients must first populate the pool
57       * using {@link #returnObject(java.lang.Object,java.lang.Object)}
58       * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
59       *
60       * @param max cap on the number of "sleeping" instances in the pool
61       */
62      public StackKeyedObjectPool(int max) {
63          this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
64      }
65  
66      /***
67       * Create a new pool using
68       * no factory. Clients must first populate the pool
69       * using {@link #returnObject(java.lang.Object,java.lang.Object)}
70       * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
71       *
72       * @param max cap on the number of "sleeping" instances in the pool
73       * @param init initial size of the pool (this specifies the size of the container,
74       *             it does not cause the pool to be pre-populated.)
75       */
76      public StackKeyedObjectPool(int max, int init) {
77          this((KeyedPoolableObjectFactory)null,max,init);
78      }
79  
80      /***
81       * Create a new <tt>SimpleKeyedObjectPool</tt> using
82       * the specified <i>factory</i> to create new instances.
83       *
84       * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
85       */
86      public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) {
87          this(factory,DEFAULT_MAX_SLEEPING);
88      }
89  
90      /***
91       * Create a new <tt>SimpleKeyedObjectPool</tt> using
92       * the specified <i>factory</i> to create new instances.
93       * capping the number of "sleeping" instances to <i>max</i>
94       *
95       * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
96       * @param max cap on the number of "sleeping" instances in the pool
97       */
98      public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max) {
99          this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
100     }
101 
102     /***
103      * Create a new <tt>SimpleKeyedObjectPool</tt> using
104      * the specified <i>factory</i> to create new instances.
105      * capping the number of "sleeping" instances to <i>max</i>,
106      * and initially allocating a container capable of containing
107      * at least <i>init</i> instances.
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      * @param init initial size of the pool (this specifies the size of the container,
112      *             it does not cause the pool to be pre-populated.)
113      */
114     public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) {
115         _factory = factory;
116         _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max);
117         _initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
118         _pools = new HashMap();
119         _activeCount = new HashMap();
120     }
121 
122     public synchronized Object borrowObject(Object key) throws Exception {
123         Object obj = null;
124         Stack stack = (Stack)(_pools.get(key));
125         if(null == stack) {
126             stack = new Stack();
127             stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
128             _pools.put(key,stack);
129         }
130         try {
131             obj = stack.pop();
132             _totIdle--;
133         } catch(Exception e) {
134             if(null == _factory) {
135                 throw new NoSuchElementException();
136             } else {
137                 obj = _factory.makeObject(key);
138             }
139         }
140         if(null != obj && null != _factory) {
141             _factory.activateObject(key,obj);
142         }
143         incrementActiveCount(key);
144         return obj;
145     }
146 
147     public synchronized void returnObject(Object key, Object obj) throws Exception {
148         decrementActiveCount(key);
149         if(null == _factory || _factory.validateObject(key,obj)) {
150             Stack stack = (Stack)(_pools.get(key));
151             if(null == stack) {
152                 stack = new Stack();
153                 stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
154                 _pools.put(key,stack);
155             }
156             if(null != _factory) {
157                 try {
158                     _factory.passivateObject(key,obj);
159                 } catch(Exception e) {
160                     _factory.destroyObject(key,obj);
161                     return;
162                 }
163             }
164             if(stack.size() < _maxSleeping) {
165                 stack.push(obj);
166                 _totIdle++;
167             } else {
168                 if(null != _factory) {
169                     _factory.destroyObject(key,obj);
170                 }
171             }
172         } else {
173             if(null != _factory) {
174                 _factory.destroyObject(key,obj);
175             }
176         }
177     }
178 
179     public synchronized void invalidateObject(Object key, Object obj) throws Exception {
180         decrementActiveCount(key);
181         if(null != _factory) {
182             _factory.destroyObject(key,obj);
183         }
184         notifyAll(); // _totalActive has changed
185     }
186 
187     public synchronized void addObject(Object key) throws Exception {
188         Object obj = _factory.makeObject(key);
189         incrementActiveCount(key); // returnObject will decrement this
190         returnObject(key,obj);
191     }
192 
193     public int getNumIdle() {
194         return _totIdle;
195     }
196 
197     public int getNumActive() {
198         return _totActive;
199     }
200 
201     public synchronized int getNumActive(Object key) {
202         return getActiveCount(key);
203     }
204 
205     public synchronized int getNumIdle(Object key) {
206         try {
207             return((Stack)(_pools.get(key))).size();
208         } catch(Exception e) {
209             return 0;
210         }
211     }
212 
213     public synchronized void clear() {
214         Iterator it = _pools.keySet().iterator();
215         while(it.hasNext()) {
216             Object key = it.next();
217             Stack stack = (Stack)(_pools.get(key));
218             destroyStack(key,stack);
219         }
220         _totIdle = 0;
221         _pools.clear();
222         _activeCount.clear();
223     }
224 
225     public synchronized void clear(Object key) {
226         Stack stack = (Stack)(_pools.remove(key));
227         destroyStack(key,stack);
228     }
229 
230     private synchronized void destroyStack(Object key, Stack stack) {
231         if(null == stack) {
232             return;
233         } else {
234             if(null != _factory) {
235                 Iterator it = stack.iterator();
236                 while(it.hasNext()) {
237                     try {
238                         _factory.destroyObject(key,it.next());
239                     } catch(Exception e) {
240                         // ignore error, keep destroying the rest
241                     }
242                 }
243             }
244             _totIdle -= stack.size();
245             _activeCount.remove(key);
246             stack.clear();
247         }
248     }
249 
250     public synchronized String toString() {
251         StringBuffer buf = new StringBuffer();
252         buf.append(getClass().getName());
253         buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
254         Iterator it = _pools.keySet().iterator();
255         while(it.hasNext()) {
256             Object key = it.next();
257             buf.append(" |").append(key).append("|=");
258             Stack s = (Stack)(_pools.get(key));
259             buf.append(s.size());
260         }
261         return buf.toString();
262     }
263 
264     public synchronized void close() throws Exception {
265         clear();
266         _pools = null;
267         _factory = null;
268         _activeCount = null;
269     }
270 
271     public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException {
272         if(0 < getNumActive()) {
273             throw new IllegalStateException("Objects are already active");
274         } else {
275             clear();
276             _factory = factory;
277         }
278     }
279 
280     private int getActiveCount(Object key) {
281         try {
282             return ((Integer)_activeCount.get(key)).intValue();
283         } catch(NoSuchElementException e) {
284             return 0;
285         } catch(NullPointerException e) {
286             return 0;
287         }
288     }
289 
290     private void incrementActiveCount(Object key) {
291         _totActive++;
292         Integer old = (Integer)(_activeCount.get(key));
293         if(null == old) { 
294             _activeCount.put(key,new Integer(1));
295         } else {
296             _activeCount.put(key,new Integer(old.intValue() + 1));
297         }
298     }
299 
300     private void decrementActiveCount(Object key) {
301         _totActive--;
302         Integer active = (Integer)(_activeCount.get(key));
303         if(null == active) {
304             // do nothing, either null or zero is OK
305         } else if(active.intValue() <= 1) {
306             _activeCount.remove(key);
307         } else {
308             _activeCount.put(key, new Integer(active.intValue() - 1));
309         }
310     }
311 
312     /*** The default cap on the number of "sleeping" instances in the pool. */
313     protected static final int DEFAULT_MAX_SLEEPING  = 8;
314 
315     /***
316      * The default initial size of the pool
317      * (this specifies the size of the container, it does not
318      * cause the pool to be pre-populated.)
319      */
320     protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
321 
322     /*** My named-set of pools. */
323     protected HashMap _pools = null;
324 
325     /*** My {@link KeyedPoolableObjectFactory}. */
326     protected KeyedPoolableObjectFactory _factory = null;
327 
328     /*** The cap on the number of "sleeping" instances in <i>each</i> pool. */
329     protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
330 
331     /*** The initial capacity of each pool. */
332     protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
333 
334     /*** Total number of object borrowed and not yet retuened for all pools */
335     protected int _totActive = 0;
336 
337     /*** Total number of objects "sleeping" for all pools */
338     protected int _totIdle = 0;
339 
340     /*** Number of active objects borrowed and not yet returned by pool */
341     protected HashMap _activeCount = null;
342 
343 }