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 */
017
018 package org.apache.commons.pool.impl;
019
020 import java.lang.ref.SoftReference;
021 import java.lang.ref.ReferenceQueue;
022 import java.lang.ref.Reference;
023 import java.util.ArrayList;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.NoSuchElementException;
027
028 import org.apache.commons.pool.BaseObjectPool;
029 import org.apache.commons.pool.ObjectPool;
030 import org.apache.commons.pool.PoolableObjectFactory;
031 import org.apache.commons.pool.PoolUtils;
032
033 /**
034 * A {@link java.lang.ref.SoftReference SoftReference} based
035 * {@link ObjectPool}.
036 *
037 * @author Rodney Waldhoff
038 * @author Sandy McArthur
039 * @version $Revision: 777748 $ $Date: 2009-05-22 20:00:44 -0400 (Fri, 22 May 2009) $
040 * @since Pool 1.0
041 */
042 public class SoftReferenceObjectPool extends BaseObjectPool implements ObjectPool {
043 /**
044 * Create a <code>SoftReferenceObjectPool</code> without a factory.
045 * {@link #setFactory(PoolableObjectFactory) setFactory} should be called
046 * before any attempts to use the pool are made.
047 * Generally speaking you should prefer the {@link #SoftReferenceObjectPool(PoolableObjectFactory)} constructor.
048 *
049 * @see #SoftReferenceObjectPool(PoolableObjectFactory)
050 */
051 public SoftReferenceObjectPool() {
052 _pool = new ArrayList();
053 _factory = null;
054 }
055
056 /**
057 * Create a <code>SoftReferenceObjectPool</code> with the specified factory.
058 *
059 * @param factory object factory to use.
060 */
061 public SoftReferenceObjectPool(PoolableObjectFactory factory) {
062 _pool = new ArrayList();
063 _factory = factory;
064 }
065
066 /**
067 * Create a <code>SoftReferenceObjectPool</code> with the specified factory and initial idle object count.
068 *
069 * @param factory object factory to use.
070 * @param initSize initial size to attempt to prefill the pool.
071 * @throws Exception when there is a problem prefilling the pool.
072 * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
073 * @deprecated because this is a SoftReference pool, prefilled idle obejects may be garbage collected before they are used.
074 * To be removed in Pool 3.0.
075 */
076 public SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize) throws Exception, IllegalArgumentException {
077 if (factory == null) {
078 throw new IllegalArgumentException("factory required to prefill the pool.");
079 }
080 _pool = new ArrayList(initSize);
081 _factory = factory;
082 PoolUtils.prefill(this, initSize);
083 }
084
085 public synchronized Object borrowObject() throws Exception {
086 assertOpen();
087 Object obj = null;
088 boolean newlyCreated = false;
089 while(null == obj) {
090 if(_pool.isEmpty()) {
091 if(null == _factory) {
092 throw new NoSuchElementException();
093 } else {
094 newlyCreated = true;
095 obj = _factory.makeObject();
096 }
097 } else {
098 SoftReference ref = (SoftReference)(_pool.remove(_pool.size() - 1));
099 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 }