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    import org.apache.commons.pool.PoolUtils;
024    
025    import java.util.HashMap;
026    import java.util.Iterator;
027    import java.util.Map;
028    import java.util.NoSuchElementException;
029    import java.util.Stack;
030    
031    /**
032     * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> implementation.
033     * <p>
034     * Given a {@link KeyedPoolableObjectFactory}, this class will maintain
035     * a simple pool of instances.  A finite number of "sleeping"
036     * or inactive instances is enforced, but when the pool is
037     * empty, new instances are created to support the new load.
038     * Hence this class places no limit on the number of "active"
039     * instances created by the pool, but is quite useful for
040     * re-using <code>Object</code>s without introducing
041     * artificial limits.
042     * </p>
043     *
044     * @author Rodney Waldhoff
045     * @author Sandy McArthur
046     * @version $Revision: 990437 $ $Date: 2010-08-28 13:17:57 -0700 (Sat, 28 Aug 2010) $
047     * @see Stack
048     * @since Pool 1.0
049     */
050    public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
051        /**
052         * Create a new pool using no factory.
053         * Clients must first set the {@link #setFactory factory} or
054         * may populate the pool using {@link #returnObject returnObject}
055         * before they can be {@link #borrowObject borrowed}.
056         *
057         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
058         * @see #setFactory(KeyedPoolableObjectFactory)
059         */
060        public StackKeyedObjectPool() {
061            this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
062        }
063    
064        /**
065         * Create a new pool using no factory.
066         * Clients must first set the {@link #setFactory factory} or
067         * may populate the pool using {@link #returnObject returnObject}
068         * before they can be {@link #borrowObject borrowed}.
069         *
070         * @param max cap on the number of "sleeping" instances in the pool
071         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
072         * @see #setFactory(KeyedPoolableObjectFactory)
073         */
074        public StackKeyedObjectPool(int max) {
075            this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
076        }
077    
078        /**
079         * Create a new pool using no factory.
080         * Clients must first set the {@link #setFactory factory} or
081         * may populate the pool using {@link #returnObject returnObject}
082         * before they can be {@link #borrowObject borrowed}.
083         *
084         * @param max cap on the number of "sleeping" instances in the pool
085         * @param init initial size of the pool (this specifies the size of the container,
086         *             it does not cause the pool to be pre-populated.)
087         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
088         * @see #setFactory(KeyedPoolableObjectFactory)
089         */
090        public StackKeyedObjectPool(int max, int init) {
091            this((KeyedPoolableObjectFactory)null,max,init);
092        }
093    
094        /**
095         * Create a new <code>SimpleKeyedObjectPool</code> using
096         * the specified <code>factory</code> to create new instances.
097         *
098         * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
099         */
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    }