    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    }