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.Reference;
021    import java.lang.ref.ReferenceQueue;
022    import java.lang.ref.SoftReference;
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.PoolUtils;
031    import org.apache.commons.pool.PoolableObjectFactory;
032    
033    /**
034     * A {@link java.lang.ref.SoftReference SoftReference} based
035     * {@link ObjectPool}.
036     *
037     * @param <T> the type of objects held in this pool
038     * 
039     * @author Rodney Waldhoff
040     * @author Sandy McArthur
041     * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
042     * @since Pool 1.0
043     */
044    public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> implements ObjectPool<T> {
045        /**
046         * Create a <code>SoftReferenceObjectPool</code> without a factory.
047         * {@link #setFactory(PoolableObjectFactory) setFactory} should be called
048         * before any attempts to use the pool are made.
049         * Generally speaking you should prefer the {@link #SoftReferenceObjectPool(PoolableObjectFactory)} constructor.
050         *
051         * @see #SoftReferenceObjectPool(PoolableObjectFactory)
052         * @deprecated to be removed in pool 2.0.  Use {@link #SoftReferenceObjectPool(PoolableObjectFactory)}.
053         */
054        @Deprecated
055        public SoftReferenceObjectPool() {
056            _pool = new ArrayList<SoftReference<T>>();
057            _factory = null;
058        }
059    
060        /**
061         * Create a <code>SoftReferenceObjectPool</code> with the specified factory.
062         *
063         * @param factory object factory to use.
064         */
065        public SoftReferenceObjectPool(PoolableObjectFactory<T> factory) {
066            _pool = new ArrayList<SoftReference<T>>();
067            _factory = factory;
068        }
069    
070        /**
071         * Create a <code>SoftReferenceObjectPool</code> with the specified factory and initial idle object count.
072         *
073         * @param factory object factory to use.
074         * @param initSize initial size to attempt to prefill the pool.
075         * @throws Exception when there is a problem prefilling the pool.
076         * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
077         * @deprecated because this is a SoftReference pool, prefilled idle obejects may be garbage collected before they are used.
078         *      To be removed in Pool 2.0.
079         */
080        @Deprecated
081        public SoftReferenceObjectPool(PoolableObjectFactory<T> factory, int initSize) throws Exception, IllegalArgumentException {
082            if (factory == null) {
083                throw new IllegalArgumentException("factory required to prefill the pool.");
084            }
085            _pool = new ArrayList<SoftReference<T>>(initSize);
086            _factory = factory;
087            PoolUtils.prefill(this, initSize);
088        }
089    
090        /**
091         * <p>Borrow an object from the pool.  If there are no idle instances available in the pool, the configured
092         * factory's {@link PoolableObjectFactory#makeObject()} method is invoked to create a new instance.</p>
093         * 
094         * <p>All instances are {@link PoolableObjectFactory#activateObject(Object) activated} and
095         * {@link PoolableObjectFactory#validateObject(Object) validated} before being returned by this
096         * method.  If validation fails or an exception occurs activating or validating an idle instance,
097         * the failing instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed} and another
098         * instance is retrieved from the pool, validated and activated.  This process continues until either the
099         * 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    }