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    }