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.SoftReference;
21  import java.lang.ref.ReferenceQueue;
22  import java.lang.ref.Reference;
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.PoolableObjectFactory;
31  import org.apache.commons.pool.PoolUtils;
32  
33  /**
34   * A {@link java.lang.ref.SoftReference SoftReference} based
35   * {@link ObjectPool}.
36   *
37   * @author Rodney Waldhoff
38   * @author Sandy McArthur
39   * @version $Revision: 777748 $ $Date: 2009-05-22 20:00:44 -0400 (Fri, 22 May 2009) $
40   * @since Pool 1.0
41   */
42  public class SoftReferenceObjectPool extends BaseObjectPool implements ObjectPool {
43      /**
44       * Create a <code>SoftReferenceObjectPool</code> without a factory.
45       * {@link #setFactory(PoolableObjectFactory) setFactory} should be called
46       * before any attempts to use the pool are made.
47       * Generally speaking you should prefer the {@link #SoftReferenceObjectPool(PoolableObjectFactory)} constructor.
48       *
49       * @see #SoftReferenceObjectPool(PoolableObjectFactory)
50       */
51      public SoftReferenceObjectPool() {
52          _pool = new ArrayList();
53          _factory = null;
54      }
55  
56      /**
57       * Create a <code>SoftReferenceObjectPool</code> with the specified factory.
58       *
59       * @param factory object factory to use.
60       */
61      public SoftReferenceObjectPool(PoolableObjectFactory factory) {
62          _pool = new ArrayList();
63          _factory = factory;
64      }
65  
66      /**
67       * Create a <code>SoftReferenceObjectPool</code> with the specified factory and initial idle object count.
68       *
69       * @param factory object factory to use.
70       * @param initSize initial size to attempt to prefill the pool.
71       * @throws Exception when there is a problem prefilling the pool.
72       * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
73       * @deprecated because this is a SoftReference pool, prefilled idle obejects may be garbage collected before they are used.
74       *      To be removed in Pool 3.0.
75       */
76      public SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize) throws Exception, IllegalArgumentException {
77          if (factory == null) {
78              throw new IllegalArgumentException("factory required to prefill the pool.");
79          }
80          _pool = new ArrayList(initSize);
81          _factory = factory;
82          PoolUtils.prefill(this, initSize);
83      }
84  
85      public synchronized Object borrowObject() throws Exception {
86          assertOpen();
87          Object obj = null;
88          boolean newlyCreated = false;
89          while(null == obj) {
90              if(_pool.isEmpty()) {
91                  if(null == _factory) {
92                      throw new NoSuchElementException();
93                  } else {
94                      newlyCreated = true;
95                      obj = _factory.makeObject();
96                  }
97              } else {
98                  SoftReference ref = (SoftReference)(_pool.remove(_pool.size() - 1));
99                  obj = ref.get();
100                 ref.clear(); // prevent this ref from being enqueued with refQueue.
101             }
102             if (null != _factory && null != obj) {
103                 try {
104                     _factory.activateObject(obj);
105                     if (!_factory.validateObject(obj)) {
106                         throw new Exception("ValidateObject failed");
107                     }
108                 } catch (Throwable t) {
109                     try {
110                         _factory.destroyObject(obj);
111                     } catch (Throwable t2) {
112                         // swallowed
113                     } finally {
114                         obj = null;
115                     }
116                     if (newlyCreated) {
117                         throw new NoSuchElementException(
118                             "Could not create a validated object, cause: " +
119                             t.getMessage());
120                     }
121                 }
122             }
123         }
124         _numActive++;
125         return obj;
126     }
127 
128     public synchronized void returnObject(Object obj) throws Exception {
129         boolean success = !isClosed();
130         if (_factory != null) {
131             if(!_factory.validateObject(obj)) {
132                 success = false;
133             } else {
134                 try {
135                     _factory.passivateObject(obj);
136                 } catch(Exception e) {
137                     success = false;
138                 }
139             }
140         }
141 
142         boolean shouldDestroy = !success;
143         _numActive--;
144         if(success) {
145             _pool.add(new SoftReference(obj, refQueue));
146         }
147         notifyAll(); // _numActive has changed
148 
149         if (shouldDestroy && _factory != null) {
150             try {
151                 _factory.destroyObject(obj);
152             } catch(Exception e) {
153                 // ignored
154             }
155         }
156     }
157 
158     public synchronized void invalidateObject(Object obj) throws Exception {
159         _numActive--;
160         if (_factory != null) {
161             _factory.destroyObject(obj);
162         }
163         notifyAll(); // _numActive has changed
164     }
165 
166     /**
167      * Create an object, and place it into the pool.
168      * addObject() is useful for "pre-loading" a pool with idle objects.
169      */
170     public synchronized void addObject() throws Exception {
171         assertOpen();
172         if (_factory == null) {
173             throw new IllegalStateException("Cannot add objects without a factory.");
174         }
175         Object obj = _factory.makeObject();
176 
177         boolean success = true;
178         if(!_factory.validateObject(obj)) {
179             success = false;
180         } else {
181             _factory.passivateObject(obj);
182         }
183 
184         boolean shouldDestroy = !success;
185         if(success) {
186             _pool.add(new SoftReference(obj, refQueue));
187             notifyAll(); // _numActive has changed
188         }
189 
190         if(shouldDestroy) {
191             try {
192                 _factory.destroyObject(obj);
193             } catch(Exception e) {
194                 // ignored
195             }
196         }
197     }
198 
199     /** Returns an approximation not less than the of the number of idle instances in the pool. */
200     public synchronized int getNumIdle() {
201         pruneClearedReferences();
202         return _pool.size();
203     }
204 
205     /**
206      * Return the number of instances currently borrowed from this pool.
207      *
208      * @return the number of instances currently borrowed from this pool
209      */
210     public synchronized int getNumActive() {
211         return _numActive;
212     }
213 
214     /**
215      * Clears any objects sitting idle in the pool.
216      */
217     public synchronized void clear() {
218         if(null != _factory) {
219             Iterator iter = _pool.iterator();
220             while(iter.hasNext()) {
221                 try {
222                     Object obj = ((SoftReference)iter.next()).get();
223                     if(null != obj) {
224                         _factory.destroyObject(obj);
225                     }
226                 } catch(Exception e) {
227                     // ignore error, keep destroying the rest
228                 }
229             }
230         }
231         _pool.clear();
232         pruneClearedReferences();
233     }
234 
235     public void close() throws Exception {
236         super.close();
237         clear();
238     }
239 
240     /**
241      * Sets the {@link PoolableObjectFactory factory} this pool uses
242      * to create new instances. Trying to change
243      * the <code>factory</code> while there are borrowed objects will
244      * throw an {@link IllegalStateException}.
245      *
246      * @param factory the {@link PoolableObjectFactory} used to create new instances.
247      * @throws IllegalStateException when the factory cannot be set at this time
248      */
249     public synchronized void setFactory(PoolableObjectFactory factory) throws IllegalStateException {
250         assertOpen();
251         if(0 < getNumActive()) {
252             throw new IllegalStateException("Objects are already active");
253         } else {
254             clear();
255             _factory = factory;
256         }
257     }
258 
259     /**
260      * If any idle objects were garbage collected, remove their
261      * {@link Reference} wrappers from the idle object pool.
262      */
263     private void pruneClearedReferences() {
264         Reference ref;
265         while ((ref = refQueue.poll()) != null) {
266             try {
267                 _pool.remove(ref);
268             } catch (UnsupportedOperationException uoe) {
269                 // ignored
270             }
271         }
272     }
273 
274     /** My pool. */
275     private List _pool = null;
276 
277     /** My {@link PoolableObjectFactory}. */
278     private PoolableObjectFactory _factory = null;
279 
280     /**
281      * Queue of broken references that might be able to be removed from <code>_pool</code>.
282      * This is used to help {@link #getNumIdle()} be more accurate with minimial
283      * performance overhead.
284      */
285     private final ReferenceQueue refQueue = new ReferenceQueue();
286 
287     /** Number of active objects. */
288     private int _numActive = 0;
289 }