View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.pool.impl;
19  
20  import java.lang.ref.Reference;
21  import java.lang.ref.ReferenceQueue;
22  import java.lang.ref.SoftReference;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.NoSuchElementException;
27  
28  import org.apache.commons.pool.BaseObjectPool;
29  import org.apache.commons.pool.ObjectPool;
30  import org.apache.commons.pool.PoolUtils;
31  import org.apache.commons.pool.PoolableObjectFactory;
32  
33  /**
34   * A {@link java.lang.ref.SoftReference SoftReference} based
35   * {@link ObjectPool}.
36   *
37   * @param <T> the type of objects held in this pool
38   * 
39   * @author Rodney Waldhoff
40   * @author Sandy McArthur
41   * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
42   * @since Pool 1.0
43   */
44  public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> implements ObjectPool<T> {
45      /**
46       * Create a <code>SoftReferenceObjectPool</code> without a factory.
47       * {@link #setFactory(PoolableObjectFactory) setFactory} should be called
48       * before any attempts to use the pool are made.
49       * Generally speaking you should prefer the {@link #SoftReferenceObjectPool(PoolableObjectFactory)} constructor.
50       *
51       * @see #SoftReferenceObjectPool(PoolableObjectFactory)
52       * @deprecated to be removed in pool 2.0.  Use {@link #SoftReferenceObjectPool(PoolableObjectFactory)}.
53       */
54      @Deprecated
55      public SoftReferenceObjectPool() {
56          _pool = new ArrayList<SoftReference<T>>();
57          _factory = null;
58      }
59  
60      /**
61       * Create a <code>SoftReferenceObjectPool</code> with the specified factory.
62       *
63       * @param factory object factory to use.
64       */
65      public SoftReferenceObjectPool(PoolableObjectFactory<T> factory) {
66          _pool = new ArrayList<SoftReference<T>>();
67          _factory = factory;
68      }
69  
70      /**
71       * Create a <code>SoftReferenceObjectPool</code> with the specified factory and initial idle object count.
72       *
73       * @param factory object factory to use.
74       * @param initSize initial size to attempt to prefill the pool.
75       * @throws Exception when there is a problem prefilling the pool.
76       * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
77       * @deprecated because this is a SoftReference pool, prefilled idle obejects may be garbage collected before they are used.
78       *      To be removed in Pool 2.0.
79       */
80      @Deprecated
81      public SoftReferenceObjectPool(PoolableObjectFactory<T> factory, int initSize) throws Exception, IllegalArgumentException {
82          if (factory == null) {
83              throw new IllegalArgumentException("factory required to prefill the pool.");
84          }
85          _pool = new ArrayList<SoftReference<T>>(initSize);
86          _factory = factory;
87          PoolUtils.prefill(this, initSize);
88      }
89  
90      /**
91       * <p>Borrow an object from the pool.  If there are no idle instances available in the pool, the configured
92       * factory's {@link PoolableObjectFactory#makeObject()} method is invoked to create a new instance.</p>
93       * 
94       * <p>All instances are {@link PoolableObjectFactory#activateObject(Object) activated} and
95       * {@link PoolableObjectFactory#validateObject(Object) validated} before being returned by this
96       * method.  If validation fails or an exception occurs activating or validating an idle instance,
97       * the failing instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed} and another
98       * instance is retrieved from the pool, validated and activated.  This process continues until either the
99       * pool is empty or an instance passes validation.  If the pool is empty on activation or
100      * it does not contain any valid instances, the factory's <code>makeObject</code> method is used
101      * to create a new instance.  If the created instance either raises an exception on activation or
102      * fails validation, <code>NoSuchElementException</code> is thrown. Exceptions thrown by <code>MakeObject</code>
103      * are propagated to the caller; but other than <code>ThreadDeath</code> or <code>VirtualMachineError</code>,
104      * exceptions generated by activation, validation or destroy methods are swallowed silently.</p>
105      * 
106      * @throws NoSuchElementException if a valid object cannot be provided
107      * @throws IllegalStateException if invoked on a {@link #close() closed} pool
108      * @throws Exception if an exception occurs creating a new instance
109      * @return a valid, activated object instance
110      */
111     @Override
112     public synchronized T borrowObject() throws Exception {
113         assertOpen();
114         T obj = null;
115         boolean newlyCreated = false;
116         while(null == obj) {
117             if(_pool.isEmpty()) {
118                 if(null == _factory) {
119                     throw new NoSuchElementException();
120                 } else {
121                     newlyCreated = true;
122                     obj = _factory.makeObject();
123                 }
124             } else {
125                 SoftReference<T> ref = _pool.remove(_pool.size() - 1);
126                 obj = ref.get();
127                 ref.clear(); // prevent this ref from being enqueued with refQueue.
128             }
129             if (null != _factory && null != obj) {
130                 try {
131                     _factory.activateObject(obj);
132                     if (!_factory.validateObject(obj)) {
133                         throw new Exception("ValidateObject failed");
134                     }
135                 } catch (Throwable t) {
136                     PoolUtils.checkRethrow(t);
137                     try {
138                         _factory.destroyObject(obj);
139                     } catch (Throwable t2) {
140                         PoolUtils.checkRethrow(t2);
141                         // Swallowed
142                     } finally {
143                         obj = null;
144                     }
145                     if (newlyCreated) {
146                         throw new NoSuchElementException(
147                             "Could not create a validated object, cause: " +
148                             t.getMessage());
149                     }
150                 }
151             }
152         }
153         _numActive++;
154         return obj;
155     }
156 
157     /**
158      * <p>Returns an instance to the pool after successful validation and passivation. The returning instance
159      * is destroyed if any of the following are true:<ul>
160      *   <li>the pool is closed</li>
161      *   <li>{@link PoolableObjectFactory#validateObject(Object) validation} fails</li>
162      *   <li>{@link PoolableObjectFactory#passivateObject(Object) passivation} throws an exception</li>
163      * </ul>
164      *</p>
165      * 
166      * <p>Exceptions passivating or destroying instances are silently swallowed.  Exceptions validating
167      * instances are propagated to the client.</p>
168      * 
169      * @param obj instance to return to the pool
170      */
171     @Override
172     public synchronized void returnObject(T obj) throws Exception {
173         boolean success = !isClosed();
174         if (_factory != null) {
175             if(!_factory.validateObject(obj)) {
176                 success = false;
177             } else {
178                 try {
179                     _factory.passivateObject(obj);
180                 } catch(Exception e) {
181                     success = false;
182                 }
183             }
184         }
185 
186         boolean shouldDestroy = !success;
187         _numActive--;
188         if(success) {
189             _pool.add(new SoftReference<T>(obj, refQueue));
190         }
191         notifyAll(); // _numActive has changed
192 
193         if (shouldDestroy && _factory != null) {
194             try {
195                 _factory.destroyObject(obj);
196             } catch(Exception e) {
197                 // ignored
198             }
199         }
200     }
201 
202     /**
203      * {@inheritDoc}
204      */
205     @Override
206     public synchronized void invalidateObject(T obj) throws Exception {
207         _numActive--;
208         if (_factory != null) {
209             _factory.destroyObject(obj);
210         }
211         notifyAll(); // _numActive has changed
212     }
213 
214     /**
215      * <p>Create an object, and place it into the pool.
216      * addObject() is useful for "pre-loading" a pool with idle objects.</p>
217      * 
218      * <p>Before being added to the pool, the newly created instance is
219      * {@link PoolableObjectFactory#validateObject(Object) validated} and 
220      * {@link PoolableObjectFactory#passivateObject(Object) passivated}.  If validation
221      * fails, the new instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed}.
222      * Exceptions generated by the factory <code>makeObject</code> or <code>passivate</code> are
223      * propagated to the caller. Exceptions destroying instances are silently swallowed.</p>
224      * 
225      * @throws IllegalStateException if invoked on a {@link #close() closed} pool
226      * @throws Exception when the {@link #getFactory() factory} has a problem creating or passivating an object.
227      */
228     @Override
229     public synchronized void addObject() throws Exception {
230         assertOpen();
231         if (_factory == null) {
232             throw new IllegalStateException("Cannot add objects without a factory.");
233         }
234         T obj = _factory.makeObject();
235 
236         boolean success = true;
237         if(!_factory.validateObject(obj)) {
238             success = false;
239         } else {
240             _factory.passivateObject(obj);
241         }
242 
243         boolean shouldDestroy = !success;
244         if(success) {
245             _pool.add(new SoftReference<T>(obj, refQueue));
246             notifyAll(); // _numActive has changed
247         }
248 
249         if(shouldDestroy) {
250             try {
251                 _factory.destroyObject(obj);
252             } catch(Exception e) {
253                 // ignored
254             }
255         }
256     }
257 
258     /**
259      * Returns an approximation not less than the of the number of idle instances in the pool.
260      * 
261      * @return estimated number of idle instances in the pool
262      */
263     @Override
264     public synchronized int getNumIdle() {
265         pruneClearedReferences();
266         return _pool.size();
267     }
268 
269     /**
270      * Return the number of instances currently borrowed from this pool.
271      *
272      * @return the number of instances currently borrowed from this pool
273      */
274     @Override
275     public synchronized int getNumActive() {
276         return _numActive;
277     }
278 
279     /**
280      * Clears any objects sitting idle in the pool.
281      */
282     @Override
283     public synchronized void clear() {
284         if(null != _factory) {
285             Iterator<SoftReference<T>> iter = _pool.iterator();
286             while(iter.hasNext()) {
287                 try {
288                     T obj = iter.next().get();
289                     if(null != obj) {
290                         _factory.destroyObject(obj);
291                     }
292                 } catch(Exception e) {
293                     // ignore error, keep destroying the rest
294                 }
295             }
296         }
297         _pool.clear();
298         pruneClearedReferences();
299     }
300 
301     /**
302      * <p>Close this pool, and free any resources associated with it. Invokes
303      * {@link #clear()} to destroy and remove instances in the pool.</p>
304      * 
305      * <p>Calling {@link #addObject} or {@link #borrowObject} after invoking
306      * this method on a pool will cause them to throw an
307      * {@link IllegalStateException}.</p>
308      *
309      * @throws Exception never - exceptions clearing the pool are swallowed
310      */
311     @Override
312     public void close() throws Exception {
313         super.close();
314         clear();
315     }
316 
317     /**
318      * Sets the {@link PoolableObjectFactory factory} this pool uses
319      * to create new instances. Trying to change
320      * the <code>factory</code> while there are borrowed objects will
321      * throw an {@link IllegalStateException}.
322      *
323      * @param factory the {@link PoolableObjectFactory} used to create new instances.
324      * @throws IllegalStateException when the factory cannot be set at this time
325      * @deprecated to be removed in pool 2.0
326      */
327     @Deprecated
328     @Override
329     public synchronized void setFactory(PoolableObjectFactory<T> factory) throws IllegalStateException {
330         assertOpen();
331         if(0 < getNumActive()) {
332             throw new IllegalStateException("Objects are already active");
333         } else {
334             clear();
335             _factory = factory;
336         }
337     }
338 
339     /**
340      * If any idle objects were garbage collected, remove their
341      * {@link Reference} wrappers from the idle object pool.
342      */
343     private void pruneClearedReferences() {
344         Reference<? extends T> ref;
345         while ((ref = refQueue.poll()) != null) {
346             try {
347                 _pool.remove(ref);
348             } catch (UnsupportedOperationException uoe) {
349                 // ignored
350             }
351         }
352     }
353     
354     /**
355      * Returns the {@link PoolableObjectFactory} used by this pool to create and manage object instances.
356      * 
357      * @return the factory
358      * @since 1.5.5
359      */
360     public synchronized PoolableObjectFactory<T> getFactory() { 
361         return _factory;
362     }
363 
364     /** My pool. */
365     private final List<SoftReference<T>> _pool;
366 
367     /** My {@link PoolableObjectFactory}. */
368     private PoolableObjectFactory<T> _factory = null;
369 
370     /**
371      * Queue of broken references that might be able to be removed from <code>_pool</code>.
372      * This is used to help {@link #getNumIdle()} be more accurate with minimial
373      * performance overhead.
374      */
375     private final ReferenceQueue<T> refQueue = new ReferenceQueue<T>();
376 
377     /** Number of active objects. */
378     private int _numActive = 0; //@GuardeBy("this")
379 }