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: 1566584 $
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     *
407     * @throws Exception If an error occurs while trying to destroy the object
408     */
409    private void destroy(PooledSoftReference<T> toDestroy) throws Exception {
410        toDestroy.invalidate();
411        idleReferences.remove(toDestroy);
412        allReferences.remove(toDestroy);
413        try {
414            factory.destroyObject(toDestroy);
415        } finally {
416            destroyCount++;
417            toDestroy.getReference().clear();
418        }
419    }
420
421    /**
422     * Clears cleared references from iterator's collection
423     * @param iterator iterator over idle/allReferences
424     */
425    private void removeClearedReferences(Iterator<PooledSoftReference<T>> iterator) {
426        PooledSoftReference<T> ref;
427        while (iterator.hasNext()) {
428            ref = iterator.next();
429            if (ref.getReference() == null || ref.getReference().isEnqueued()) {
430                iterator.remove();
431            }
432        }
433    }
434}