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.util.Iterator;
021 import java.util.NoSuchElementException;
022 import java.util.Stack;
023
024 import org.apache.commons.pool.BaseObjectPool;
025 import org.apache.commons.pool.ObjectPool;
026 import org.apache.commons.pool.PoolableObjectFactory;
027
028 /**
029 * A simple, {@link java.util.Stack Stack}-based {@link ObjectPool} implementation.
030 * <p>
031 * Given a {@link PoolableObjectFactory}, this class will maintain
032 * a simple pool of instances. A finite number of "sleeping"
033 * or idle instances is enforced, but when the pool is
034 * empty, new instances are created to support the new load.
035 * Hence this class places no limit on the number of "active"
036 * instances created by the pool, but is quite useful for
037 * re-using <tt>Object</tt>s without introducing
038 * artificial limits.
039 *
040 * @author Rodney Waldhoff
041 * @author Dirk Verbeeck
042 * @author Sandy McArthur
043 * @version $Revision: 777748 $ $Date: 2009-05-22 20:00:44 -0400 (Fri, 22 May 2009) $
044 * @since Pool 1.0
045 */
046 public class StackObjectPool extends BaseObjectPool implements ObjectPool {
047 /**
048 * Create a new pool using
049 * no factory.
050 * Clients must first {@link #setFactory(PoolableObjectFactory) set the factory}
051 * else this pool will not behave correctly.
052 * Clients may first populate the pool
053 * using {@link #returnObject(java.lang.Object)}
054 * before they can be {@link #borrowObject borrowed} but this useage is <strong>discouraged</strong>.
055 *
056 * @see #StackObjectPool(PoolableObjectFactory)
057 */
058 public StackObjectPool() {
059 this((PoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
060 }
061
062 /**
063 * Create a new pool using
064 * no factory.
065 * Clients must first {@link #setFactory(PoolableObjectFactory) set the factory}
066 * else this pool will not behave correctly.
067 * Clients may first populate the pool
068 * using {@link #returnObject(java.lang.Object)}
069 * before they can be {@link #borrowObject borrowed} but this useage is <strong>discouraged</strong>.
070 *
071 * @param maxIdle cap on the number of "sleeping" instances in the pool
072 * @see #StackObjectPool(PoolableObjectFactory, int)
073 */
074 public StackObjectPool(int maxIdle) {
075 this((PoolableObjectFactory)null,maxIdle,DEFAULT_INIT_SLEEPING_CAPACITY);
076 }
077
078 /**
079 * Create a new pool using
080 * no factory.
081 * Clients must first {@link #setFactory(PoolableObjectFactory) set the factory}
082 * else this pool will not behave correctly.
083 * Clients may first populate the pool
084 * using {@link #returnObject(java.lang.Object)}
085 * before they can be {@link #borrowObject borrowed} but this useage is <strong>discouraged</strong>.
086 *
087 * @param maxIdle cap on the number of "sleeping" instances in the pool
088 * @param initIdleCapacity initial size of the pool (this specifies the size of the container,
089 * it does not cause the pool to be pre-populated.)
090 * @see #StackObjectPool(PoolableObjectFactory, int, int)
091 */
092 public StackObjectPool(int maxIdle, int initIdleCapacity) {
093 this((PoolableObjectFactory)null,maxIdle,initIdleCapacity);
094 }
095
096 /**
097 * Create a new <tt>StackObjectPool</tt> using
098 * the specified <i>factory</i> to create new instances.
099 *
100 * @param factory the {@link PoolableObjectFactory} used to populate the pool
101 */
102 public StackObjectPool(PoolableObjectFactory factory) {
103 this(factory,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
104 }
105
106 /**
107 * Create a new <tt>SimpleObjectPool</tt> using
108 * the specified <i>factory</i> to create new instances,
109 * capping the number of "sleeping" instances to <i>max</i>.
110 *
111 * @param factory the {@link PoolableObjectFactory} used to populate the pool
112 * @param maxIdle cap on the number of "sleeping" instances in the pool
113 */
114 public StackObjectPool(PoolableObjectFactory factory, int maxIdle) {
115 this(factory,maxIdle,DEFAULT_INIT_SLEEPING_CAPACITY);
116 }
117
118 /**
119 * Create a new <tt>SimpleObjectPool</tt> using
120 * the specified <i>factory</i> to create new instances,
121 * capping the number of "sleeping" instances to <i>max</i>,
122 * and initially allocating a container capable of containing
123 * at least <i>init</i> instances.
124 *
125 * @param factory the {@link PoolableObjectFactory} used to populate the pool
126 * @param maxIdle cap on the number of "sleeping" instances in the pool
127 * @param initIdleCapacity initial size of the pool (this specifies the size of the container,
128 * it does not cause the pool to be pre-populated.)
129 */
130 public StackObjectPool(PoolableObjectFactory factory, int maxIdle, int initIdleCapacity) {
131 _factory = factory;
132 _maxSleeping = (maxIdle < 0 ? DEFAULT_MAX_SLEEPING : maxIdle);
133 int initcapacity = (initIdleCapacity < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : initIdleCapacity);
134 _pool = new Stack();
135 _pool.ensureCapacity( initcapacity > _maxSleeping ? _maxSleeping : initcapacity);
136 }
137
138 public synchronized Object borrowObject() throws Exception {
139 assertOpen();
140 Object obj = null;
141 boolean newlyCreated = false;
142 while (null == obj) {
143 if (!_pool.empty()) {
144 obj = _pool.pop();
145 } else {
146 if(null == _factory) {
147 throw new NoSuchElementException();
148 } else {
149 obj = _factory.makeObject();
150 newlyCreated = true;
151 if (obj == null) {
152 throw new NoSuchElementException("PoolableObjectFactory.makeObject() returned null.");
153 }
154 }
155 }
156 if (null != _factory && null != obj) {
157 try {
158 _factory.activateObject(obj);
159 if (!_factory.validateObject(obj)) {
160 throw new Exception("ValidateObject failed");
161 }
162 } catch (Throwable t) {
163 try {
164 _factory.destroyObject(obj);
165 } catch (Throwable t2) {
166 // swallowed
167 } finally {
168 obj = null;
169 }
170 if (newlyCreated) {
171 throw new NoSuchElementException(
172 "Could not create a validated object, cause: " +
173 t.getMessage());
174 }
175 }
176 }
177 }
178 _numActive++;
179 return obj;
180 }
181
182 public synchronized void returnObject(Object obj) throws Exception {
183 boolean success = !isClosed();
184 if(null != _factory) {
185 if(!_factory.validateObject(obj)) {
186 success = false;
187 } else {
188 try {
189 _factory.passivateObject(obj);
190 } catch(Exception e) {
191 success = false;
192 }
193 }
194 }
195
196 boolean shouldDestroy = !success;
197
198 _numActive--;
199 if (success) {
200 Object toBeDestroyed = null;
201 if(_pool.size() >= _maxSleeping) {
202 shouldDestroy = true;
203 toBeDestroyed = _pool.remove(0); // remove the stalest object
204 }
205 _pool.push(obj);
206 obj = toBeDestroyed; // swap returned obj with the stalest one so it can be destroyed
207 }
208 notifyAll(); // _numActive has changed
209
210 if(shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null
211 try {
212 _factory.destroyObject(obj);
213 } catch(Exception e) {
214 // ignored
215 }
216 }
217 }
218
219 public synchronized void invalidateObject(Object obj) throws Exception {
220 _numActive--;
221 if (null != _factory) {
222 _factory.destroyObject(obj);
223 }
224 notifyAll(); // _numActive has changed
225 }
226
227 /**
228 * Return the number of instances
229 * currently idle in this pool.
230 *
231 * @return the number of instances currently idle in this pool
232 */
233 public synchronized int getNumIdle() {
234 return _pool.size();
235 }
236
237 /**
238 * Return the number of instances currently borrowed from this pool.
239 *
240 * @return the number of instances currently borrowed from this pool
241 */
242 public synchronized int getNumActive() {
243 return _numActive;
244 }
245
246 /**
247 * Clears any objects sitting idle in the pool.
248 */
249 public synchronized void clear() {
250 if(null != _factory) {
251 Iterator it = _pool.iterator();
252 while(it.hasNext()) {
253 try {
254 _factory.destroyObject(it.next());
255 } catch(Exception e) {
256 // ignore error, keep destroying the rest
257 }
258 }
259 }
260 _pool.clear();
261 }
262
263 /**
264 * Close this pool, and free any resources associated with it.
265 * <p>
266 * Calling {@link #addObject} or {@link #borrowObject} after invoking
267 * this method on a pool will cause them to throw an
268 * {@link IllegalStateException}.
269 * </p>
270 *
271 * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed.
272 */
273 public void close() throws Exception {
274 super.close();
275 clear();
276 }
277
278 /**
279 * Create an object, and place it into the pool.
280 * addObject() is useful for "pre-loading" a pool with idle objects.
281 * @throws Exception when the {@link #_factory} has a problem creating an object.
282 */
283 public synchronized void addObject() throws Exception {
284 assertOpen();
285 if (_factory == null) {
286 throw new IllegalStateException("Cannot add objects without a factory.");
287 }
288 Object obj = _factory.makeObject();
289
290 boolean success = true;
291 if(!_factory.validateObject(obj)) {
292 success = false;
293 } else {
294 _factory.passivateObject(obj);
295 }
296
297 boolean shouldDestroy = !success;
298
299 if (success) {
300 Object toBeDestroyed = null;
301 if(_pool.size() >= _maxSleeping) {
302 shouldDestroy = true;
303 toBeDestroyed = _pool.remove(0); // remove the stalest object
304 }
305 _pool.push(obj);
306 obj = toBeDestroyed; // swap returned obj with the stalest one so it can be destroyed
307 }
308 notifyAll(); // _numIdle has changed
309
310 if(shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null
311 try {
312 _factory.destroyObject(obj);
313 } catch(Exception e) {
314 // ignored
315 }
316 }
317 }
318
319 /**
320 * Sets the {@link PoolableObjectFactory factory} this pool uses
321 * to create new instances. Trying to change
322 * the <code>factory</code> while there are borrowed objects will
323 * throw an {@link IllegalStateException}.
324 *
325 * @param factory the {@link PoolableObjectFactory} used to create new instances.
326 * @throws IllegalStateException when the factory cannot be set at this time
327 */
328 public synchronized void setFactory(PoolableObjectFactory factory) throws IllegalStateException {
329 assertOpen();
330 if(0 < getNumActive()) {
331 throw new IllegalStateException("Objects are already active");
332 } else {
333 clear();
334 _factory = factory;
335 }
336 }
337
338 /** The default cap on the number of "sleeping" instances in the pool. */
339 protected static final int DEFAULT_MAX_SLEEPING = 8;
340
341 /**
342 * The default initial size of the pool
343 * (this specifies the size of the container, it does not
344 * cause the pool to be pre-populated.)
345 */
346 protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
347
348 /** My pool. */
349 protected Stack _pool = null;
350
351 /** My {@link PoolableObjectFactory}. */
352 protected PoolableObjectFactory _factory = null;
353
354 /** The cap on the number of "sleeping" instances in the pool. */
355 protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
356
357 /** Number of object borrowed but not yet returned to the pool. */
358 protected int _numActive = 0;
359 }