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 java.util.HashMap;
021    import java.util.Iterator;
022    import java.util.Map;
023    import java.util.NoSuchElementException;
024    import java.util.Stack;
025    
026    import org.apache.commons.pool.BaseKeyedObjectPool;
027    import org.apache.commons.pool.KeyedObjectPool;
028    import org.apache.commons.pool.KeyedPoolableObjectFactory;
029    import org.apache.commons.pool.PoolUtils;
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     * @param <K> the type of keys in this pool
045     * @param <V> the type of objects held in this pool
046     * 
047     * @author Rodney Waldhoff
048     * @author Sandy McArthur
049     * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
050     * @see Stack
051     * @since Pool 1.0
052     */
053    public class StackKeyedObjectPool<K, V> extends BaseKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
054        /**
055         * Create a new pool using no factory.
056         * Clients must first set the {@link #setFactory factory} or
057         * may populate the pool using {@link #returnObject returnObject}
058         * before they can be {@link #borrowObject borrowed}.
059         *
060         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
061         * @see #setFactory(KeyedPoolableObjectFactory)
062         */
063        public StackKeyedObjectPool() {
064            this(null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
065        }
066    
067        /**
068         * Create a new pool using no factory.
069         * Clients must first set the {@link #setFactory factory} or
070         * may populate the pool using {@link #returnObject returnObject}
071         * before they can be {@link #borrowObject borrowed}.
072         *
073         * @param max cap on the number of "sleeping" instances in the pool
074         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
075         * @see #setFactory(KeyedPoolableObjectFactory)
076         */
077        public StackKeyedObjectPool(int max) {
078            this(null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
079        }
080    
081        /**
082         * Create a new pool using no factory.
083         * Clients must first set the {@link #setFactory factory} or
084         * may populate the pool using {@link #returnObject returnObject}
085         * before they can be {@link #borrowObject borrowed}.
086         *
087         * @param max cap on the number of "sleeping" instances in the pool
088         * @param init initial size of the pool (this specifies the size of the container,
089         *             it does not cause the pool to be pre-populated.)
090         * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
091         * @see #setFactory(KeyedPoolableObjectFactory)
092         */
093        public StackKeyedObjectPool(int max, int init) {
094            this(null,max,init);
095        }
096    
097        /**
098         * Create a new <code>SimpleKeyedObjectPool</code> using
099         * the specified <code>factory</code> to create new instances.
100         *
101         * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
102         */
103        public StackKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory) {
104            this(factory,DEFAULT_MAX_SLEEPING);
105        }
106    
107        /**
108         * Create a new <code>SimpleKeyedObjectPool</code> using
109         * the specified <code>factory</code> to create new instances.
110         * capping the number of "sleeping" instances to <code>max</code>
111         *
112         * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
113         * @param max cap on the number of "sleeping" instances in the pool
114         */
115        public StackKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory, int max) {
116            this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
117        }
118    
119        /**
120         * Create a new <code>SimpleKeyedObjectPool</code> using
121         * the specified <code>factory</code> to create new instances.
122         * capping the number of "sleeping" instances to <code>max</code>,
123         * and initially allocating a container capable of containing
124         * at least <code>init</code> instances.
125         *
126         * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
127         * @param max cap on the number of "sleeping" instances in the pool
128         * @param init initial size of the pool (this specifies the size of the container,
129         *             it does not cause the pool to be pre-populated.)
130         */
131        public StackKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory, int max, int init) {
132            _factory = factory;
133            _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max);
134            _initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
135            _pools = new HashMap<K, Stack<V>>();
136            _activeCount = new HashMap<K, Integer>();
137        }
138    
139        /**
140         * Borrows an object with the given key.  If there are no idle instances under the
141         * given key, a new one is created.
142         * 
143         * @param key the pool key
144         * @return keyed poolable object instance
145         */
146        @Override
147        public synchronized V borrowObject(K key) throws Exception {
148            assertOpen();
149            Stack<V> stack = (_pools.get(key));
150            if(null == stack) {
151                stack = new Stack<V>();
152                stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
153                _pools.put(key,stack);
154            }
155            V obj = null;
156            do {
157                boolean newlyMade = false;
158                if (!stack.empty()) {
159                    obj = stack.pop();
160                    _totIdle--;
161                } else {
162                    if(null == _factory) {
163                        throw new NoSuchElementException("pools without a factory cannot create new objects as needed.");
164                    } else {
165                        obj = _factory.makeObject(key);
166                        newlyMade = true;
167                    }
168                }
169                if (null != _factory && null != obj) {
170                    try {
171                        _factory.activateObject(key, obj);
172                        if (!_factory.validateObject(key, obj)) {
173                            throw new Exception("ValidateObject failed");
174                        }
175                    } catch (Throwable t) {
176                        PoolUtils.checkRethrow(t);
177                        try {
178                            _factory.destroyObject(key,obj);
179                        } catch (Throwable t2) {
180                            PoolUtils.checkRethrow(t2);
181                            // swallowed
182                        } finally {
183                            obj = null;
184                        }
185                        if (newlyMade) {
186                            throw new NoSuchElementException(
187                                "Could not create a validated object, cause: " +
188                                t.getMessage());
189                        }
190                    }
191                }
192            } while (obj == null);
193            incrementActiveCount(key);
194            return obj;
195        }
196    
197        /**
198         * Returns <code>obj</code> to the pool under <code>key</code>.  If adding the
199         * returning instance to the pool results in {@link #_maxSleeping maxSleeping}
200         * exceeded for the given key, the oldest instance in the idle object pool
201         * is destroyed to make room for the returning instance.
202         * 
203         * @param key the pool key
204         * @param obj returning instance
205         */
206        @Override
207        public synchronized void returnObject(K key, V obj) throws Exception {
208            decrementActiveCount(key);
209            if (null != _factory) {
210                if (_factory.validateObject(key, obj)) {
211                    try {
212                        _factory.passivateObject(key, obj);
213                    } catch (Exception ex) {
214                        _factory.destroyObject(key, obj);
215                        return;
216                    }
217                } else {
218                    return;
219                }
220            }
221    
222            if (isClosed()) {
223                if (null != _factory) {
224                    try {
225                        _factory.destroyObject(key, obj);
226                    } catch (Exception e) {
227                        // swallowed
228                    }
229                }
230                return;
231            }
232    
233            Stack<V> stack = _pools.get(key);
234            if(null == stack) {
235                stack = new Stack<V>();
236                stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
237                _pools.put(key,stack);
238            }
239            final int stackSize = stack.size();
240            if (stackSize >= _maxSleeping) {
241                final V staleObj;
242                if (stackSize > 0) {
243                    staleObj = stack.remove(0);
244                    _totIdle--;
245                } else {
246                    staleObj = obj;
247                }
248                if(null != _factory) {
249                    try {
250                        _factory.destroyObject(key, staleObj);
251                    } catch (Exception e) {
252                        // swallowed
253                    }
254                }
255            }
256            stack.push(obj);
257            _totIdle++;
258        }
259    
260        /**
261         * {@inheritDoc}
262         */
263        @Override
264        public synchronized void invalidateObject(K key, V obj) throws Exception {
265            decrementActiveCount(key);
266            if(null != _factory) {
267                _factory.destroyObject(key,obj);
268            }
269            notifyAll(); // _totalActive has changed
270        }
271    
272        /**
273         * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
274         * passivate it, and then placed in the idle object pool.
275         * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
276         *
277         * @param key the key a new instance should be added to
278         * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
279         * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been called on this pool.
280         */
281        @Override
282        public synchronized void addObject(K key) throws Exception {
283            assertOpen();
284            if (_factory == null) {
285                throw new IllegalStateException("Cannot add objects without a factory.");
286            }
287            V obj = _factory.makeObject(key);
288            try {
289                if (!_factory.validateObject(key, obj)) {
290                   return;
291                }
292            } catch (Exception e) {
293                try {
294                    _factory.destroyObject(key, obj);
295                } catch (Exception e2) {
296                    // swallowed
297                }
298                return;
299            }
300            _factory.passivateObject(key, obj);
301    
302            Stack<V> stack = _pools.get(key);
303            if(null == stack) {
304                stack = new Stack<V>();
305                stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
306                _pools.put(key,stack);
307            }
308    
309            final int stackSize = stack.size();
310            if (stackSize >= _maxSleeping) {
311                final V staleObj;
312                if (stackSize > 0) {
313                    staleObj = stack.remove(0);
314                    _totIdle--;
315                } else {
316                    staleObj = obj;
317                }
318                try {
319                    _factory.destroyObject(key, staleObj);
320                } catch (Exception e) {
321                    // Don't swallow destroying the newly created object.
322                    if (obj == staleObj) {
323                        throw e;
324                    }
325                }
326            } else {
327                stack.push(obj);
328                _totIdle++;
329            }
330        }
331    
332        /**
333         * Returns the total number of instances currently idle in this pool.
334         *
335         * @return the total number of instances currently idle in this pool
336         */
337        @Override
338        public synchronized int getNumIdle() {
339            return _totIdle;
340        }
341    
342        /**
343         * Returns the total number of instances current borrowed from this pool but not yet returned.
344         *
345         * @return the total number of instances currently borrowed from this pool
346         */
347        @Override
348        public synchronized int getNumActive() {
349            return _totActive;
350        }
351    
352        /**
353         * Returns the number of instances currently borrowed from but not yet returned
354         * to the pool corresponding to the given <code>key</code>.
355         *
356         * @param key the key to query
357         * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
358         */
359        @Override
360        public synchronized int getNumActive(K key) {
361            return getActiveCount(key);
362        }
363    
364        /**
365         * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
366         *
367         * @param key the key to query
368         * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
369         */
370        @Override
371        public synchronized int getNumIdle(K key) {
372            try {
373                return(_pools.get(key)).size();
374            } catch(Exception e) {
375                return 0;
376            }
377        }
378    
379        /**
380         * Clears the pool, removing all pooled instances.
381         */
382        @Override
383        public synchronized void clear() {
384            Iterator<K> it = _pools.keySet().iterator();
385            while(it.hasNext()) {
386                K key = it.next();
387                Stack<V> stack = _pools.get(key);
388                destroyStack(key,stack);
389            }
390            _totIdle = 0;
391            _pools.clear();
392            _activeCount.clear();
393        }
394    
395        /**
396         * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
397         *
398         * @param key the key to clear
399         */
400        @Override
401        public synchronized void clear(K key) {
402            Stack<V> stack = _pools.remove(key);
403            destroyStack(key,stack);
404        }
405    
406        /**
407         * Destroys all instances in the stack and clears the stack.
408         * 
409         * @param key key passed to factory when destroying instances
410         * @param stack stack to destroy
411         */
412        private synchronized void destroyStack(K key, Stack<V> stack) {
413            if(null == stack) {
414                return;
415            } else {
416                if(null != _factory) {
417                    Iterator<V> it = stack.iterator();
418                    while(it.hasNext()) {
419                        try {
420                            _factory.destroyObject(key,it.next());
421                        } catch(Exception e) {
422                            // ignore error, keep destroying the rest
423                        }
424                    }
425                }
426                _totIdle -= stack.size();
427                _activeCount.remove(key);
428                stack.clear();
429            }
430        }
431    
432        /**
433         * Returns a string representation of this StackKeyedObjectPool, including
434         * the number of pools, the keys and the size of each keyed pool.
435         * 
436         * @return Keys and pool sizes
437         */
438        @Override
439        public synchronized String toString() {
440            StringBuffer buf = new StringBuffer();
441            buf.append(getClass().getName());
442            buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
443            Iterator<K> it = _pools.keySet().iterator();
444            while(it.hasNext()) {
445                K key = it.next();
446                buf.append(" |").append(key).append("|=");
447                Stack<V> s = _pools.get(key);
448                buf.append(s.size());
449            }
450            return buf.toString();
451        }
452    
453        /**
454         * Close this pool, and free any resources associated with it.
455         * <p>
456         * Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking
457         * this method on a pool will cause them to throw an {@link IllegalStateException}.
458         * </p>
459         *
460         * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed.
461         */
462        @Override
463        public void close() throws Exception {
464            super.close();
465            clear();
466        }
467    
468        /**
469         * Sets the {@link KeyedPoolableObjectFactory factory} the pool uses
470         * to create new instances.
471         * Trying to change the <code>factory</code> after a pool has been used will frequently
472         * throw an {@link UnsupportedOperationException}.
473         *
474         * @param factory the {@link KeyedPoolableObjectFactory} used to manage object instances
475         * @throws IllegalStateException when the factory cannot be set at this time
476         * @deprecated to be removed in pool 2.0
477         */
478        @Deprecated
479        @Override
480        public synchronized void setFactory(KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException {
481            if(0 < getNumActive()) {
482                throw new IllegalStateException("Objects are already active");
483            } else {
484                clear();
485                _factory = factory;
486            }
487        }
488        
489        /**
490         * @return the {@link KeyedPoolableObjectFactory} used by this pool to manage object instances.
491         * @since 1.5.5
492         */
493        public synchronized KeyedPoolableObjectFactory<K, V> getFactory() {
494            return _factory;
495        }
496    
497        /**
498         * Returns the active instance count for the given key.
499         * 
500         * @param key pool key
501         * @return active count
502         */
503        private int getActiveCount(K key) {
504            try {
505                return _activeCount.get(key).intValue();
506            } catch(NoSuchElementException e) {
507                return 0;
508            } catch(NullPointerException e) {
509                return 0;
510            }
511        }
512    
513        /**
514         * Increment the active count for the given key. Also
515         * increments the total active count.
516         * 
517         * @param key pool key
518         */
519        private void incrementActiveCount(K key) {
520            _totActive++;
521            Integer old = _activeCount.get(key);
522            if(null == old) {
523                _activeCount.put(key,new Integer(1));
524            } else {
525                _activeCount.put(key,new Integer(old.intValue() + 1));
526            }
527        }
528    
529        /**
530         * Decrements the active count for the given key.
531         * Also decrements the total active count.
532         * 
533         * @param key pool key
534         */
535        private void decrementActiveCount(K key) {
536            _totActive--;
537            Integer active = _activeCount.get(key);
538            if(null == active) {
539                // do nothing, either null or zero is OK
540            } else if(active.intValue() <= 1) {
541                _activeCount.remove(key);
542            } else {
543                _activeCount.put(key, new Integer(active.intValue() - 1));
544            }
545        }
546    
547        
548        /**
549         * @return map of keyed pools
550         * @since 1.5.5
551         */
552        public Map<K, Stack<V>> getPools() {
553            return _pools;
554        }
555    
556        /**
557         * @return the cap on the number of "sleeping" instances in <code>each</code> pool.
558         * @since 1.5.5
559         */
560        public int getMaxSleeping() {
561            return _maxSleeping;
562        }
563    
564        /**
565         * @return the initial capacity of each pool.
566         * @since 1.5.5
567         */
568        public int getInitSleepingCapacity() {
569            return _initSleepingCapacity;
570        }
571    
572        /**
573         * @return the _totActive
574         */
575        public int getTotActive() {
576            return _totActive;
577        }
578    
579        /**
580         * @return the _totIdle
581         */
582        public int getTotIdle() {
583            return _totIdle;
584        }
585    
586        /**
587         * @return the _activeCount
588         * @since 1.5.5
589         */
590        public Map<K, Integer> getActiveCount() {
591            return _activeCount;
592        }
593    
594    
595        /** The default cap on the number of "sleeping" instances in the pool. */
596        protected static final int DEFAULT_MAX_SLEEPING  = 8;
597    
598        /**
599         * The default initial size of the pool
600         * (this specifies the size of the container, it does not
601         * cause the pool to be pre-populated.)
602         */
603        protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
604    
605        /**
606         *  My named-set of pools.
607         *  @deprecated to be removed in pool 2.0.  Use {@link #getPools()}
608         */
609        @Deprecated
610        protected HashMap<K, Stack<V>> _pools = null;
611    
612        /**
613         * My {@link KeyedPoolableObjectFactory}.
614         * @deprecated to be removed in pool 2.0.  Use {@link #getFactory()}
615         */
616        @Deprecated
617        protected KeyedPoolableObjectFactory<K, V> _factory = null;
618    
619        /**
620         *  The cap on the number of "sleeping" instances in <code>each</code> pool.
621         *  @deprecated to be removed in pool 2.0.  Use {@link #getMaxSleeping()}
622         */
623        @Deprecated
624        protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
625    
626        /**
627         * The initial capacity of each pool.
628         * @deprecated to be removed in pool 2.0.  Use {@link #getInitSleepingCapacity()}.
629         */
630        @Deprecated
631        protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
632    
633        /**
634         * Total number of object borrowed and not yet returned for all pools.
635         * @deprecated to be removed in pool 2.0.  Use {@link #getTotActive()}.
636         */
637        @Deprecated
638        protected int _totActive = 0;
639    
640        /**
641         * Total number of objects "sleeping" for all pools
642         * @deprecated to be removed in pool 2.0.  Use {@link #getTotIdle()}.
643         */
644        @Deprecated
645        protected int _totIdle = 0;
646    
647        /**
648         * Number of active objects borrowed and not yet returned by pool
649         * @deprecated to be removed in pool 2.0.  Use {@link #getActiveCount()}.
650         */
651        @Deprecated
652        protected HashMap<K, Integer> _activeCount = null;
653    
654    }