    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.lang.ref.SoftReference;
    021    import java.lang.ref.ReferenceQueue;
    022    import java.lang.ref.Reference;
    023    import java.util.ArrayList;
    024    import java.util.Iterator;
    025    import java.util.List;
    026    import java.util.NoSuchElementException;
    027
    028    import org.apache.commons.pool.BaseObjectPool;
    029    import org.apache.commons.pool.ObjectPool;
    030    import org.apache.commons.pool.PoolableObjectFactory;
    031    import org.apache.commons.pool.PoolUtils;
    032
    033    /**
    034     * A {@link java.lang.ref.SoftReference SoftReference} based
    035     * {@link ObjectPool}.
    036     *
    037     * @author Rodney Waldhoff
    038     * @author Sandy McArthur
    039     * @version $Revision: 965338 $ $Date: 2010-07-18 18:09:29 -0700 (Sun, 18 Jul 2010) $
    040     * @since Pool 1.0
    041     */
    042    public class SoftReferenceObjectPool extends BaseObjectPool implements ObjectPool {
    043        /**
    044         * Create a <code>SoftReferenceObjectPool</code> without a factory.
    045         * {@link #setFactory(PoolableObjectFactory) setFactory} should be called
    046         * before any attempts to use the pool are made.
    047         * Generally speaking you should prefer the {@link #SoftReferenceObjectPool(PoolableObjectFactory)} constructor.
    048         *
    049         * @see #SoftReferenceObjectPool(PoolableObjectFactory)
    050         * @deprecated to be removed in pool 2.0.  Use {@link #SoftReferenceObjectPool(PoolableObjectFactory)}.
    051         */
    052        public SoftReferenceObjectPool() {
    053            _pool = new ArrayList();
    054            _factory = null;
    055        }
    056
    057        /**
    058         * Create a <code>SoftReferenceObjectPool</code> with the specified factory.
    059         *
    060         * @param factory object factory to use.
    061         */
    062        public SoftReferenceObjectPool(PoolableObjectFactory factory) {
    063            _pool = new ArrayList();
    064            _factory = factory;
    065        }
    066
    067        /**
    068         * Create a <code>SoftReferenceObjectPool</code> with the specified factory and initial idle object count.
    069         *
    070         * @param factory object factory to use.
    071         * @param initSize initial size to attempt to prefill the pool.
    072         * @throws Exception when there is a problem prefilling the pool.
    073         * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
    074         * @deprecated because this is a SoftReference pool, prefilled idle obejects may be garbage collected before they are used.
    075         *      To be removed in Pool 2.0.
    076         */
    077        public SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize) throws Exception, IllegalArgumentException {
    078            if (factory == null) {
    079                throw new IllegalArgumentException("factory required to prefill the pool.");
    080            }
    081            _pool = new ArrayList(initSize);
    082            _factory = factory;
    083            PoolUtils.prefill(this, initSize);
    084        }
    085
    086        /**
    087         * <p>Borrow an object from the pool.  If there are no idle instances available in the pool, the configured
    088         * factory's {@link PoolableObjectFactory#makeObject()} method is invoked to create a new instance.</p>
    089         *
    090         * <p>All instances are {@link PoolableObjectFactory#activateObject(Object) activated} and
    091         * {@link PoolableObjectFactory#validateObject(Object) validated} before being returned by this
    092         * method.  If validation fails or an exception occurs activating or validating an idle instance,
    093         * the failing instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed} and another
    094         * instance is retrieved from the pool, validated and activated.  This process continues until either the
    095         * pool is empty or an instance passes validation.  If the pool is empty on activation or
    096         * it does not contain any valid instances, the factory's <code>makeObject</code> method is used
    097         * to create a new instance.  If the created instance either raises an exception on activation or
    098         * fails validation, <code>NoSuchElementException</code> is thrown. Exceptions thrown by <code>MakeObject</code>
    099         * are propagated to the caller; but other than <code>ThreadDeath</code> or <code>VirtualMachineError</code>,
    100         * exceptions generated by activation, validation or destroy methods are swallowed silently.</p>
    101         *
    102         * @throws NoSuchElementException if a valid object cannot be provided
    103         * @throws IllegalStateException if invoked on a {@link #close() closed} pool
    104         * @throws Exception if an exception occurs creating a new instance
    105         * @return a valid, activated object instance
    106         */
    107        public synchronized Object borrowObject() throws Exception {
    108            assertOpen();
    109            Object obj = null;
    110            boolean newlyCreated = false;
    111            while(null == obj) {
    112                if(_pool.isEmpty()) {
    113                    if(null == _factory) {
    114                        throw new NoSuchElementException();
    115                    } else {
    116                        newlyCreated = true;
    117                        obj = _factory.makeObject();
    118                    }
    119                } else {
    120                    SoftReference ref = (SoftReference)(_pool.remove(_pool.size() - 1));
    121                    obj = ref.get();
    122                    ref.clear(); // prevent this ref from being enqueued with refQueue.
    123                }
    124                if (null != _factory && null != obj) {
    125                    try {
    126                        _factory.activateObject(obj);
    127                        if (!_factory.validateObject(obj)) {
    128                            throw new Exception("ValidateObject failed");
    129                        }
    130                    } catch (Throwable t) {
    131                        PoolUtils.checkRethrow(t);
    132                        try {
    133                            _factory.destroyObject(obj);
    134                        } catch (Throwable t2) {
    135                            PoolUtils.checkRethrow(t2);
    136                            // Swallowed
    137                        } finally {
    138                            obj = null;
    139                        }
    140                        if (newlyCreated) {
    141                            throw new NoSuchElementException(
    142                                "Could not create a validated object, cause: " +
    143                                t.getMessage());
    144                        }
    145                    }
    146                }
    147            }
    148            _numActive++;
    149            return obj;
    150        }
    151
    152        /**
    153         * <p>Returns an instance to the pool after successful validation and passivation. The returning instance
    154         * is destroyed if any of the following are true:<ul>
    155         *   <li>the pool is closed</li>
    156         *   <li>{@link PoolableObjectFactory#validateObject(Object) validation} fails</li>
    157         *   <li>{@link PoolableObjectFactory#passivateObject(Object) passivation} throws an exception</li>
    158         * </ul>
    159         *</p>
    160         *
    161         * <p>Exceptions passivating or destroying instances are silently swallowed.  Exceptions validating
    162         * instances are propagated to the client.</p>
    163         *
    164         * @param obj instance to return to the pool
    165         */
    166        public synchronized void returnObject(Object obj) throws Exception {
    167            boolean success = !isClosed();
    168            if (_factory != null) {
    169                if(!_factory.validateObject(obj)) {
    170                    success = false;
    171                } else {
    172                    try {
    173                        _factory.passivateObject(obj);
    174                    } catch(Exception e) {
    175                        success = false;
    176                    }
    177                }
    178            }
    179
    180            boolean shouldDestroy = !success;
    181            _numActive--;
    182            if(success) {
    183                _pool.add(new SoftReference(obj, refQueue));
    184            }
    185            notifyAll(); // _numActive has changed
    186
    187            if (shouldDestroy && _factory != null) {
    188                try {
    189                    _factory.destroyObject(obj);
    190                } catch(Exception e) {
    191                    // ignored
    192                }
    193            }
    194        }
    195
    196        /**
    197         * {@inheritDoc}
    198         */
    199        public synchronized void invalidateObject(Object obj) throws Exception {
    200            _numActive--;
    201            if (_factory != null) {
    202                _factory.destroyObject(obj);
    203            }
    204            notifyAll(); // _numActive has changed
    205        }
    206
    207        /**
    208         * <p>Create an object, and place it into the pool.
    209         * addObject() is useful for "pre-loading" a pool with idle objects.</p>
    210         *
    211         * <p>Before being added to the pool, the newly created instance is
    212         * {@link PoolableObjectFactory#validateObject(Object) validated} and
    213         * {@link PoolableObjectFactory#passivateObject(Object) passivated}.  If validation
    214         * fails, the new instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed}.
    215         * Exceptions generated by the factory <code>makeObject</code> or <code>passivate</code> are
    216         * propagated to the caller. Exceptions destroying instances are silently swallowed.</p>
    217         *
    218         * @throws IllegalStateException if invoked on a {@link #close() closed} pool
    219         * @throws Exception when the {@link #getFactory() factory} has a problem creating or passivating an object.
    220         */
    221        public synchronized void addObject() throws Exception {
    222            assertOpen();
    223            if (_factory == null) {
    224                throw new IllegalStateException("Cannot add objects without a factory.");
    225            }
    226            Object obj = _factory.makeObject();
    227
    228            boolean success = true;
    229            if(!_factory.validateObject(obj)) {
    230                success = false;
    231            } else {
    232                _factory.passivateObject(obj);
    233            }
    234
    235            boolean shouldDestroy = !success;
    236            if(success) {
    237                _pool.add(new SoftReference(obj, refQueue));
    238                notifyAll(); // _numActive has changed
    239            }
    240
    241            if(shouldDestroy) {
    242                try {
    243                    _factory.destroyObject(obj);
    244                } catch(Exception e) {
    245                    // ignored
    246                }
    247            }
    248        }
    249
    250        /**
    251         * Returns an approximation not less than the of the number of idle instances in the pool.
    252         *
    253         * @return estimated number of idle instances in the pool
    254         */
    255        public synchronized int getNumIdle() {
    256            pruneClearedReferences();
    257            return _pool.size();
    258        }
    259
    260        /**
    261         * Return the number of instances currently borrowed from this pool.
    262         *
    263         * @return the number of instances currently borrowed from this pool
    264         */
    265        public synchronized int getNumActive() {
    266            return _numActive;
    267        }
    268
    269        /**
    270         * Clears any objects sitting idle in the pool.
    271         */
    272        public synchronized void clear() {
    273            if(null != _factory) {
    274                Iterator iter = _pool.iterator();
    275                while(iter.hasNext()) {
    276                    try {
    277                        Object obj = ((SoftReference)iter.next()).get();
    278                        if(null != obj) {
    279                            _factory.destroyObject(obj);
    280                        }
    281                    } catch(Exception e) {
    282                        // ignore error, keep destroying the rest
    283                    }
    284                }
    285            }
    286            _pool.clear();
    287            pruneClearedReferences();
    288        }
    289
    290        /**
    291         * <p>Close this pool, and free any resources associated with it. Invokes
    292         * {@link #clear()} to destroy and remove instances in the pool.</p>
    293         *
    294         * <p>Calling {@link #addObject} or {@link #borrowObject} after invoking
    295         * this method on a pool will cause them to throw an
    296         * {@link IllegalStateException}.</p>
    297         *
    298         * @throws Exception never - exceptions clearing the pool are swallowed
    299         */
    300        public void close() throws Exception {
    301            super.close();
    302            clear();
    303        }
    304
    305        /**
    306         * Sets the {@link PoolableObjectFactory factory} this pool uses
    307         * to create new instances. Trying to change
    308         * the <code>factory</code> while there are borrowed objects will
    309         * throw an {@link IllegalStateException}.
    310         *
    311         * @param factory the {@link PoolableObjectFactory} used to create new instances.
    312         * @throws IllegalStateException when the factory cannot be set at this time
    313         * @deprecated to be removed in pool 2.0
    314         */
    315        public synchronized void setFactory(PoolableObjectFactory factory) throws IllegalStateException {
    316            assertOpen();
    317            if(0 < getNumActive()) {
    318                throw new IllegalStateException("Objects are already active");
    319            } else {
    320                clear();
    321                _factory = factory;
    322            }
    323        }
    324
    325        /**
    326         * If any idle objects were garbage collected, remove their
    327         * {@link Reference} wrappers from the idle object pool.
    328         */
    329        private void pruneClearedReferences() {
    330            Reference ref;
    331            while ((ref = refQueue.poll()) != null) {
    332                try {
    333                    _pool.remove(ref);
    334                } catch (UnsupportedOperationException uoe) {
    335                    // ignored
    336                }
    337            }
    338        }
    339
    340        /**
    341         * Returns the {@link PoolableObjectFactory} used by this pool to create and manage object instances.
    342         *
    343         * @return the factory
    344         * @since 1.5.5
    345         */
    346        public synchronized PoolableObjectFactory getFactory() {
    347            return _factory;
    348        }
    349
    350        /** My pool. */
    351        private List _pool = null;
    352
    353        /** My {@link PoolableObjectFactory}. */
    354        private PoolableObjectFactory _factory = null;
    355
    356        /**
    357         * Queue of broken references that might be able to be removed from <code>_pool</code>.
    358         * This is used to help {@link #getNumIdle()} be more accurate with minimial
    359         * performance overhead.
    360         */
    361        private final ReferenceQueue refQueue = new ReferenceQueue();
    362
    363        /** Number of active objects. */
    364        private int _numActive = 0;
    365    }