    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 */
    017package org.apache.commons.pool2.impl;
    018
    019import java.lang.ref.Reference;
    020import java.lang.ref.ReferenceQueue;
    021import java.lang.ref.SoftReference;
    022import java.util.ArrayList;
    023import java.util.Iterator;
    024import java.util.NoSuchElementException;
    025
    026import org.apache.commons.pool2.BaseObjectPool;
    027import org.apache.commons.pool2.ObjectPool;
    028import org.apache.commons.pool2.PoolUtils;
    029import org.apache.commons.pool2.PooledObjectFactory;
    030
    031/**
    032 * A {@link java.lang.ref.SoftReference SoftReference} based {@link ObjectPool}.
    033 * <p>
    034 * This class is intended to be thread-safe.
    035 *
    036 * @param <T>
    037 *            Type of element pooled in this pool.
    038 *
    039 * @version $Revision: 1531139 $
    040 *
    041 * @since 2.0
    042 */
    043public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> {
    044
    045    /** Factory to source pooled objects */
    046    private final PooledObjectFactory<T> factory;
    047
    048    /**
    049     * Queue of broken references that might be able to be removed from
    050     * <code>_pool</code>. This is used to help {@link #getNumIdle()} be more
    051     * accurate with minimal performance overhead.
    052     */
    053    private final ReferenceQueue<T> refQueue = new ReferenceQueue<T>();
    054
    055    /** Count of instances that have been checkout out to pool clients */
    056    private int numActive = 0; // @GuardedBy("this")
    057
    058    /** Total number of instances that have been destroyed */
    059    private long destroyCount = 0;
    060
    061    /** Total number of instances that have been created */
    062    private long createCount = 0;
    063
    064    /** Idle references - waiting to be borrowed */
    065    private final LinkedBlockingDeque<PooledSoftReference<T>> idleReferences =
    066        new LinkedBlockingDeque<PooledSoftReference<T>>();
    067
    068    /** All references - checked out or waiting to be borrowed. */
    069    private final ArrayList<PooledSoftReference<T>> allReferences =
    070        new ArrayList<PooledSoftReference<T>>();
    071
    072    /**
    073     * Create a <code>SoftReferenceObjectPool</code> with the specified factory.
    074     *
    075     * @param factory object factory to use.
    076     */
    077    public SoftReferenceObjectPool(PooledObjectFactory<T> factory) {
    078        this.factory = factory;
    079    }
    080
    081    /**
    082     * Borrow an object from the pool. If there are no idle instances available
    083     * in the pool, the configured factory's
    084     * {@link PooledObjectFactory#makeObject()} method is invoked to create a
    085     * new instance.
    086     * <p>
    087     * All instances are {@link PooledObjectFactory#activateObject(
    088     * org.apache.commons.pool2.PooledObject) activated}
    089     * and {@link PooledObjectFactory#validateObject(
    090     * org.apache.commons.pool2.PooledObject)
    091     * validated} before being returned by this method. If validation fails or
    092     * an exception occurs activating or validating an idle instance, the
    093     * failing instance is {@link PooledObjectFactory#destroyObject(
    094     * org.apache.commons.pool2.PooledObject)
    095     * destroyed} and another instance is retrieved from the pool, validated and
    096     * activated. This process continues until either the pool is empty or an
    097     * instance passes validation. If the pool is empty on activation or it does
    098     * not contain any valid instances, the factory's <code>makeObject</code>
    099     * method is used to create a new instance. If the created instance either
    100     * raises an exception on activation or fails validation,
    101     * <code>NoSuchElementException</code> is thrown. Exceptions thrown by
    102     * <code>MakeObject</code> are propagated to the caller; but other than
    103     * <code>ThreadDeath</code> or <code>VirtualMachineError</code>, exceptions
    104     * generated by activation, validation or destroy methods are swallowed
    105     * silently.
    106     *
    107     * @throws NoSuchElementException
    108     *             if a valid object cannot be provided
    109     * @throws IllegalStateException
    110     *             if invoked on a {@link #close() closed} pool
    111     * @throws Exception
    112     *             if an exception occurs creating a new instance
    113     * @return a valid, activated object instance
    114     */
    115    @SuppressWarnings("null") // ref can not be null
    116    @Override
    117    public synchronized T borrowObject() throws Exception {
    118        assertOpen();
    119        T obj = null;
    120        boolean newlyCreated = false;
    121        PooledSoftReference<T> ref = null;
    122        while (null == obj) {
    123            if (idleReferences.isEmpty()) {
    124                if (null == factory) {
    125                    throw new NoSuchElementException();
    126                } else {
    127                    newlyCreated = true;
    128                    obj = factory.makeObject().getObject();
    129                    createCount++;
    130                    // Do not register with the queue
    131                    ref = new PooledSoftReference<T>(new SoftReference<T>(obj));
    132                    allReferences.add(ref);
    133                }
    134            } else {
    135                ref = idleReferences.pollFirst();
    136                obj = ref.getObject();
    137                // Clear the reference so it will not be queued, but replace with a
    138                // a new, non-registered reference so we can still track this object
    139                // in allReferences
    140                ref.getReference().clear();
    141                ref.setReference(new SoftReference<T>(obj));
    142            }
    143            if (null != factory && null != obj) {
    144                try {
    145                    factory.activateObject(ref);
    146                    if (!factory.validateObject(ref)) {
    147                        throw new Exception("ValidateObject failed");
    148                    }
    149                } catch (Throwable t) {
    150                    PoolUtils.checkRethrow(t);
    151                    try {
    152                        destroy(ref);
    153                    } catch (Throwable t2) {
    154                        PoolUtils.checkRethrow(t2);
    155                        // Swallowed
    156                    } finally {
    157                        obj = null;
    158                    }
    159                    if (newlyCreated) {
    160                        throw new NoSuchElementException(
    161                                "Could not create a validated object, cause: " +
    162                                        t.getMessage());
    163                    }
    164                }
    165            }
    166        }
    167        numActive++;
    168        ref.allocate();
    169        return obj;
    170    }
    171
    172    /**
    173     * Returns an instance to the pool after successful validation and
    174     * passivation. The returning instance is destroyed if any of the following
    175     * are true:
    176     * <ul>
    177     * <li>the pool is closed</li>
    178     * <li>{@link PooledObjectFactory#validateObject(
    179     * org.apache.commons.pool2.PooledObject) validation} fails
    180     * </li>
    181     * <li>{@link PooledObjectFactory#passivateObject(
    182     * org.apache.commons.pool2.PooledObject) passivation}
    183     * throws an exception</li>
    184     * </ul>
    185     * Exceptions passivating or destroying instances are silently swallowed.
    186     * Exceptions validating instances are propagated to the client.
    187     *
    188     * @param obj
    189     *            instance to return to the pool
    190     */
    191    @Override
    192    public synchronized void returnObject(T obj) throws Exception {
    193        boolean success = !isClosed();
    194        final PooledSoftReference<T> ref = findReference(obj);
    195        if (ref == null) {
    196            throw new IllegalStateException(
    197                "Returned object not currently part of this pool");
    198        }
    199        if (factory != null) {
    200            if (!factory.validateObject(ref)) {
    201                success = false;
    202            } else {
    203                try {
    204                    factory.passivateObject(ref);
    205                } catch (Exception e) {
    206                    success = false;
    207                }
    208            }
    209        }
    210
    211        boolean shouldDestroy = !success;
    212        numActive--;
    213        if (success) {
    214
    215            // Deallocate and add to the idle instance pool
    216            ref.deallocate();
    217            idleReferences.add(ref);
    218        }
    219        notifyAll(); // numActive has changed
    220
    221        if (shouldDestroy && factory != null) {
    222            try {
    223                destroy(ref);
    224            } catch (Exception e) {
    225                // ignored
    226            }
    227        }
    228    }
    229
    230    /**
    231     * {@inheritDoc}
    232     */
    233    @Override
    234    public synchronized void invalidateObject(T obj) throws Exception {
    235        final PooledSoftReference<T> ref = findReference(obj);
    236        if (ref == null) {
    237            throw new IllegalStateException(
    238                "Object to invalidate is not currently part of this pool");
    239        }
    240        if (factory != null) {
    241            destroy(ref);
    242        }
    243        numActive--;
    244        notifyAll(); // numActive has changed
    245    }
    246
    247    /**
    248     * Create an object, and place it into the pool. addObject() is useful for
    249     * "pre-loading" a pool with idle objects.
    250     * <p>
    251     * Before being added to the pool, the newly created instance is
    252     * {@link PooledObjectFactory#validateObject(
    253     * org.apache.commons.pool2.PooledObject) validated} and
    254     * {@link PooledObjectFactory#passivateObject(
    255     * org.apache.commons.pool2.PooledObject) passivated}. If
    256     * validation fails, the new instance is
    257     * {@link PooledObjectFactory#destroyObject(
    258     * org.apache.commons.pool2.PooledObject) destroyed}. Exceptions
    259     * generated by the factory <code>makeObject</code> or
    260     * <code>passivate</code> are propagated to the caller. Exceptions
    261     * destroying instances are silently swallowed.
    262     *
    263     * @throws IllegalStateException
    264     *             if invoked on a {@link #close() closed} pool
    265     * @throws Exception
    266     *             when the {@link #getFactory() factory} has a problem creating
    267     *             or passivating an object.
    268     */
    269    @Override
    270    public synchronized void addObject() throws Exception {
    271        assertOpen();
    272        if (factory == null) {
    273            throw new IllegalStateException(
    274                    "Cannot add objects without a factory.");
    275        }
    276        T obj = factory.makeObject().getObject();
    277        createCount++;
    278        // Create and register with the queue
    279        PooledSoftReference<T> ref = new PooledSoftReference<T>(
    280                new SoftReference<T>(obj, refQueue));
    281        allReferences.add(ref);
    282
    283        boolean success = true;
    284        if (!factory.validateObject(ref)) {
    285            success = false;
    286        } else {
    287            factory.passivateObject(ref);
    288        }
    289
    290        boolean shouldDestroy = !success;
    291        if (success) {
    292            idleReferences.add(ref);
    293            notifyAll(); // numActive has changed
    294        }
    295
    296        if (shouldDestroy) {
    297            try {
    298                destroy(ref);
    299            } catch (Exception e) {
    300                // ignored
    301            }
    302        }
    303    }
    304
    305    /**
    306     * Returns an approximation not less than the of the number of idle
    307     * instances in the pool.
    308     *
    309     * @return estimated number of idle instances in the pool
    310     */
    311    @Override
    312    public synchronized int getNumIdle() {
    313        pruneClearedReferences();
    314        return idleReferences.size();
    315    }
    316
    317    /**
    318     * Return the number of instances currently borrowed from this pool.
    319     *
    320     * @return the number of instances currently borrowed from this pool
    321     */
    322    @Override
    323    public synchronized int getNumActive() {
    324        return numActive;
    325    }
    326
    327    /**
    328     * Clears any objects sitting idle in the pool.
    329     */
    330    @Override
    331    public synchronized void clear() {
    332        if (null != factory) {
    333            Iterator<PooledSoftReference<T>> iter = idleReferences.iterator();
    334            while (iter.hasNext()) {
    335                try {
    336                    final PooledSoftReference<T> ref = iter.next();
    337                    if (null != ref.getObject()) {
    338                        factory.destroyObject(ref);
    339                    }
    340                } catch (Exception e) {
    341                    // ignore error, keep destroying the rest
    342                }
    343            }
    344        }
    345        idleReferences.clear();
    346        pruneClearedReferences();
    347    }
    348
    349    /**
    350     * Close this pool, and free any resources associated with it. Invokes
    351     * {@link #clear()} to destroy and remove instances in the pool.
    352     * <p>
    353     * Calling {@link #addObject} or {@link #borrowObject} after invoking this
    354     * method on a pool will cause them to throw an
    355     * {@link IllegalStateException}.
    356     */
    357    @Override
    358    public void close() {
    359        super.close();
    360        clear();
    361    }
    362
    363    /**
    364     * Returns the {@link PooledObjectFactory} used by this pool to create and
    365     * manage object instances.
    366     *
    367     * @return the factory
    368     */
    369    public synchronized PooledObjectFactory<T> getFactory() {
    370        return factory;
    371    }
    372
    373    /**
    374     * If any idle objects were garbage collected, remove their
    375     * {@link Reference} wrappers from the idle object pool.
    376     */
    377    private void pruneClearedReferences() {
    378        // Remove wrappers for enqueued references from idle and allReferences lists
    379        removeClearedReferences(idleReferences.iterator());
    380        removeClearedReferences(allReferences.iterator());
    381        while (refQueue.poll() != null) {}
    382    }
    383
    384    /**
    385     * Find the PooledSoftReference in allReferences that points to obj.
    386     *
    387     * @param obj returning object
    388     * @return PooledSoftReference wrapping a soft reference to obj
    389     */
    390    private PooledSoftReference<T> findReference(T obj) {
    391        Iterator<PooledSoftReference<T>> iterator = allReferences.iterator();
    392        while (iterator.hasNext()) {
    393            final PooledSoftReference<T> reference = iterator.next();
    394            if (reference.getObject() != null && reference.getObject().equals(obj)) {
    395                return reference;
    396            }
    397        }
    398        return null;
    399    }
    400
    401    /**
    402     * Destroy a {@code PooledSoftReference} and remove it from the idle and all
    403     * references pools.
    404     *
    405     * @param toDestroy PooledSoftReference to destroy
    406     * @throws Exception
    407     */
    408    private void destroy(PooledSoftReference<T> toDestroy) throws Exception {
    409        toDestroy.invalidate();
    410        idleReferences.remove(toDestroy);
    411        allReferences.remove(toDestroy);
    412        try {
    413            factory.destroyObject(toDestroy);
    414        } finally {
    415            destroyCount++;
    416            toDestroy.getReference().clear();
    417        }
    418    }
    419
    420    /**
    421     * Clears cleared references from iterator's collection
    422     * @param iterator iterator over idle/allReferences
    423     */
    424    private void removeClearedReferences(Iterator<PooledSoftReference<T>> iterator) {
    425        PooledSoftReference<T> ref;
    426        while (iterator.hasNext()) {
    427            ref = iterator.next();
    428            if (ref.getReference() == null || ref.getReference().isEnqueued()) {
    429                iterator.remove();
    430            }
    431        }
    432    }
    433}