001 /*
002  * Copyright 1999-2004 The Apache Software Foundation.
003  
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  
008  *      http://www.apache.org/licenses/LICENSE-2.0
009  
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 
017 package org.apache.commons.pool.impl;
018 
019 import java.util.Enumeration;
020 import java.util.HashMap;
021 import java.util.Iterator;
022 import java.util.NoSuchElementException;
023 import java.util.Stack;
024 
025 import org.apache.commons.pool.BaseKeyedObjectPool;
026 import org.apache.commons.pool.KeyedObjectPool;
027 import org.apache.commons.pool.KeyedPoolableObjectFactory;
028 
029 /**
030  * A simple, {@link java.util.Stack Stack}-based {@link KeyedObjectPool} 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 <tt>Object</tt>s without introducing
039  * artificial limits.
040  *
041  @author Rodney Waldhoff
042  @version $Revision: 1.14 $ $Date: 2004/02/28 12:16:21 $ 
043  */
044 public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
045     /**
046      * Create a new pool using
047      * no factory. Clients must first populate the pool
048      * using {@link #returnObject(java.lang.Object,java.lang.Object)}
049      * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
050      */
051     public StackKeyedObjectPool() {
052         this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
053     }
054 
055     /**
056      * Create a new pool using
057      * no factory. Clients must first populate the pool
058      * using {@link #returnObject(java.lang.Object,java.lang.Object)}
059      * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
060      *
061      @param max cap on the number of "sleeping" instances in the pool
062      */
063     public StackKeyedObjectPool(int max) {
064         this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
065     }
066 
067     /**
068      * Create a new pool using
069      * no factory. Clients must first populate the pool
070      * using {@link #returnObject(java.lang.Object,java.lang.Object)}
071      * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
072      *
073      @param max cap on the number of "sleeping" instances in the pool
074      @param init initial size of the pool (this specifies the size of the container,
075      *             it does not cause the pool to be pre-populated.)
076      */
077     public StackKeyedObjectPool(int max, int init) {
078         this((KeyedPoolableObjectFactory)null,max,init);
079     }
080 
081     /**
082      * Create a new <tt>SimpleKeyedObjectPool</tt> using
083      * the specified <i>factory</i> to create new instances.
084      *
085      @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
086      */
087     public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) {
088         this(factory,DEFAULT_MAX_SLEEPING);
089     }
090 
091     /**
092      * Create a new <tt>SimpleKeyedObjectPool</tt> using
093      * the specified <i>factory</i> to create new instances.
094      * capping the number of "sleeping" instances to <i>max</i>
095      *
096      @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
097      @param max cap on the number of "sleeping" instances in the pool
098      */
099     public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max) {
100         this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
101     }
102 
103     /**
104      * Create a new <tt>SimpleKeyedObjectPool</tt> using
105      * the specified <i>factory</i> to create new instances.
106      * capping the number of "sleeping" instances to <i>max</i>,
107      * and initially allocating a container capable of containing
108      * at least <i>init</i> instances.
109      *
110      @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
111      @param max cap on the number of "sleeping" instances in the pool
112      @param init initial size of the pool (this specifies the size of the container,
113      *             it does not cause the pool to be pre-populated.)
114      */
115     public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) {
116         _factory = factory;
117         _maxSleeping = (max < ? DEFAULT_MAX_SLEEPING : max);
118         _initSleepingCapacity = (init < ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
119         _pools = new HashMap();
120         _activeCount = new HashMap();
121     }
122 
123     public synchronized Object borrowObject(Object keythrows Exception {
124         Object obj = null;
125         Stack stack = (Stack)(_pools.get(key));
126         if(null == stack) {
127             stack = new Stack();
128             stack.ensureCapacity_initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
129             _pools.put(key,stack);
130         }
131         try {
132             obj = stack.pop();
133             _totIdle--;
134         catch(Exception e) {
135             if(null == _factory) {
136                 throw new NoSuchElementException();
137             else {
138                 obj = _factory.makeObject(key);
139             }
140         }
141         if(null != obj && null != _factory) {
142             _factory.activateObject(key,obj);
143         }
144         incrementActiveCount(key);
145         return obj;
146     }
147 
148     public synchronized void returnObject(Object key, Object objthrows Exception {
149         decrementActiveCount(key);
150         if(null == _factory || _factory.validateObject(key,obj)) {
151             Stack stack = (Stack)(_pools.get(key));
152             if(null == stack) {
153                 stack = new Stack();
154                 stack.ensureCapacity_initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
155                 _pools.put(key,stack);
156             }
157             if(null != _factory) {
158                 try {
159                     _factory.passivateObject(key,obj);
160                 catch(Exception e) {
161                     _factory.destroyObject(key,obj);
162                     return;
163                 }
164             }
165             if(stack.size() < _maxSleeping) {
166                 stack.push(obj);
167                 _totIdle++;
168             else {
169                 if(null != _factory) {
170                     _factory.destroyObject(key,obj);
171                 }
172             }
173         else {
174             if(null != _factory) {
175                 _factory.destroyObject(key,obj);
176             }
177         }
178     }
179 
180     public synchronized void invalidateObject(Object key, Object objthrows Exception {
181         decrementActiveCount(key);
182         if(null != _factory) {
183             _factory.destroyObject(key,obj);
184         }
185         notifyAll()// _totalActive has changed
186     }
187 
188     public void addObject(Object keythrows Exception {
189         Object obj = _factory.makeObject(key);
190         synchronized(this) {
191             incrementActiveCount(key)// returnObject will decrement this
192             returnObject(key,obj);
193         }
194     }
195 
196     public int getNumIdle() {
197         return _totIdle;
198     }
199 
200     public int getNumActive() {
201         return _totActive;
202     }
203 
204     public int getNumActive(Object key) {
205         return getActiveCount(key);
206     }
207 
208     public synchronized int getNumIdle(Object key) {
209         try {
210             return((Stack)(_pools.get(key))).size();
211         catch(Exception e) {
212             return 0;
213         }
214     }
215 
216     public synchronized void clear() {
217         Iterator it = _pools.keySet().iterator();
218         while(it.hasNext()) {
219             Object key = it.next();
220             Stack stack = (Stack)(_pools.get(key));
221             destroyStack(key,stack);
222         }
223         _totIdle = 0;
224         _pools.clear();
225         _activeCount.clear();
226     }
227 
228     public synchronized void clear(Object key) {
229         Stack stack = (Stack)(_pools.remove(key));
230         destroyStack(key,stack);
231     }
232 
233     private synchronized void destroyStack(Object key, Stack stack) {
234         if(null == stack) {
235             return;
236         else {
237             if(null != _factory) {
238                 Enumeration enum = stack.elements();
239                 while(enum.hasMoreElements()) {
240                     try {
241                         _factory.destroyObject(key,enum.nextElement());
242                     catch(Exception e) {
243                         // ignore error, keep destroying the rest
244                     }
245                 }
246             }
247             _totIdle -= stack.size();
248             _activeCount.remove(key);
249             stack.clear();
250         }
251     }
252 
253     public synchronized String toString() {
254         StringBuffer buf = new StringBuffer();
255         buf.append(getClass().getName());
256         buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
257         Iterator it = _pools.keySet().iterator();
258         while(it.hasNext()) {
259             Object key = it.next();
260             buf.append(" |").append(key).append("|=");
261             Stack s = (Stack)(_pools.get(key));
262             buf.append(s.size());
263         }
264         return buf.toString();
265     }
266 
267     public synchronized void close() throws Exception {
268         clear();
269         _pools = null;
270         _factory = null;
271         _activeCount = null;
272     }
273 
274     public synchronized void setFactory(KeyedPoolableObjectFactory factorythrows IllegalStateException {
275         if(< getNumActive()) {
276             throw new IllegalStateException("Objects are already active");
277         else {
278             clear();
279             _factory = factory;
280         }
281     }
282 
283     private int getActiveCount(Object key) {
284         try {
285             return ((Integer)_activeCount.get(key)).intValue();
286         catch(NoSuchElementException e) {
287             return 0;
288         catch(NullPointerException e) {
289             return 0;
290         }
291     }
292 
293     private void incrementActiveCount(Object key) {
294         _totActive++;
295         Integer old = (Integer)(_activeCount.get(key));
296         if(null == old) { 
297             _activeCount.put(key,new Integer(1));
298         else {
299             _activeCount.put(key,new Integer(old.intValue() 1));
300         }
301     }
302 
303     private void decrementActiveCount(Object key) {
304         _totActive--;
305         Integer active = (Integer)(_activeCount.get(key));
306         if(null == active) {
307             // do nothing, either null or zero is OK
308         else if(active.intValue() <= 1) {
309             _activeCount.remove(key);
310         else {
311             _activeCount.put(key, new Integer(active.intValue() 1));
312         }
313     }
314 
315     /** The default cap on the number of "sleeping" instances in the pool. */
316     protected static final int DEFAULT_MAX_SLEEPING  = 8;
317 
318     /**
319      * The default initial size of the pool
320      * (this specifies the size of the container, it does not
321      * cause the pool to be pre-populated.)
322      */
323     protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
324 
325     /** My named-set of pools. */
326     protected HashMap _pools = null;
327 
328     /** My {@link KeyedPoolableObjectFactory}. */
329     protected KeyedPoolableObjectFactory _factory = null;
330 
331     /** The cap on the number of "sleeping" instances in <i>each</i> pool. */
332     protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
333 
334     /** The initial capacity of each pool. */
335     protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
336 
337     /** Total number of object borrowed and not yet retuened for all pools */
338     protected int _totActive = 0;
339 
340     /** Total number of objects "sleeping" for all pools */
341     protected int _totIdle = 0;
342 
343     /** Number of active objects borrowed and not yet returned by pool */
344     protected HashMap _activeCount = null;
345 
346 }