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