001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.pool.impl;
019    
020    import org.apache.commons.pool.BaseKeyedObjectPool;
021    import org.apache.commons.pool.KeyedObjectPool;
022    import org.apache.commons.pool.KeyedPoolableObjectFactory;
023    
024    import java.util.HashMap;
025    import java.util.Iterator;
026    import java.util.NoSuchElementException;
027    import java.util.Stack;
028    
029    /**
030     * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> 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 <code>Object</code>s without introducing
039     * artificial limits.
040     * </p>
041     *
042     * @author Rodney Waldhoff
043     * @author Sandy McArthur
044     * @version $Revision: 778880 $ $Date: 2009-05-26 16:46:22 -0400 (Tue, 26 May 2009) $
045     * @see Stack
046     * @since Pool 1.0
047     */
048    public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
049        /**
050         * Create a new pool using no factory.
051         * Clients must first set the {@link #setFactory factory} or
052         * may populate the pool using {@link #returnObject returnObject}
053         * before they can be {@link #borrowObject borrowed}.
054         *
055         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
056         * @see #setFactory(KeyedPoolableObjectFactory)
057         */
058        public StackKeyedObjectPool() {
059            this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
060        }
061    
062        /**
063         * Create a new pool using no factory.
064         * Clients must first set the {@link #setFactory factory} or
065         * may populate the pool using {@link #returnObject returnObject}
066         * before they can be {@link #borrowObject borrowed}.
067         *
068         * @param max cap on the number of "sleeping" instances in the pool
069         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
070         * @see #setFactory(KeyedPoolableObjectFactory)
071         */
072        public StackKeyedObjectPool(int max) {
073            this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
074        }
075    
076        /**
077         * Create a new pool using no factory.
078         * Clients must first set the {@link #setFactory factory} or
079         * may populate the pool using {@link #returnObject returnObject}
080         * before they can be {@link #borrowObject borrowed}.
081         *
082         * @param max cap on the number of "sleeping" instances in the pool
083         * @param init initial size of the pool (this specifies the size of the container,
084         *             it does not cause the pool to be pre-populated.)
085         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
086         * @see #setFactory(KeyedPoolableObjectFactory)
087         */
088        public StackKeyedObjectPool(int max, int init) {
089            this((KeyedPoolableObjectFactory)null,max,init);
090        }
091    
092        /**
093         * Create a new <code>SimpleKeyedObjectPool</code> using
094         * the specified <code>factory</code> to create new instances.
095         *
096         * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
097         */
098        public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) {
099            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    }