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