001    /*****************************************************************************
002     * Copyright (C) NanoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     * Original code by Aslak Hellesoy & Joerg Schaible                                       *
009     *****************************************************************************/
010    package org.picocontainer.gems.adapters;
011    
012    import java.io.ByteArrayOutputStream;
013    import java.io.IOException;
014    import java.io.NotSerializableException;
015    import java.io.ObjectInputStream;
016    import java.io.ObjectOutputStream;
017    import java.io.Serializable;
018    import java.util.ArrayList;
019    import java.util.Iterator;
020    import java.util.List;
021    
022    import org.picocontainer.ComponentAdapter;
023    import org.picocontainer.LifecycleManager;
024    import org.picocontainer.PicoContainer;
025    import org.picocontainer.defaults.DecoratingComponentAdapter;
026    import org.picocontainer.defaults.LifecycleStrategy;
027    
028    import com.thoughtworks.proxy.ProxyFactory;
029    import com.thoughtworks.proxy.factory.StandardProxyFactory;
030    import com.thoughtworks.proxy.kit.NoOperationResetter;
031    import com.thoughtworks.proxy.kit.Resetter;
032    import com.thoughtworks.proxy.toys.nullobject.Null;
033    import com.thoughtworks.proxy.toys.pool.Pool;
034    
035    
036    /**
037     * {@link ComponentAdapter} implementation that pools components.
038     * <p>
039     * The implementation utilizes a delegated ComponentAdapter to create the instances of the pool. The
040     * pool can be configured to grow unlimited or to a maximum size. If a component is requested from
041     * this adapter, the implementation returns an availailabe instance from the pool or will create a
042     * new one, if the maximum pool size is not reached yet. If none is available, the implementation
043     * can wait a defined time for a returned object before it throws a {@link PoolException}.
044     * </p>
045     * <p>
046     * This implementation uses the {@link Pool} toy from the <a
047     * href="http://proxytoys.codehaus.org">ProxyToys</a> project. This ensures, that any component,
048     * that is out of scope will be automatically returned to the pool by the garbage collector.
049     * Additionally will every component instance also implement
050     * {@link com.thoughtworks.proxy.toys.pool.Poolable}, that can be used to return the instance
051     * manually. After returning an instance it should not be used in client code anymore.
052     * </p>
053     * <p>
054     * Before a returning object is added to the available instances of the pool again, it should be
055     * reinitialized to a normalized state. By providing a proper Resetter implementation this can be
056     * done automatically. If the object cannot be reused anymore it can also be dropped and the pool
057     * may request a new instance.
058     * </p>
059     * <p>
060     * The pool supports components with a lifecylce. If the delegated {@link ComponentAdapter}
061     * implements a {@link LifecycleStrategy}, any component retrieved form the pool will be started
062     * before and stopped again, when it returns back into the pool. Also if a component cannot be
063     * resetted it will automatically be disposed. If the container of the pool is disposed, that any
064     * returning object is also disposed and will not return to the pool anymore. Note, that current
065     * implementation cannot dispose pooled objects.
066     * </p>
067     * 
068     * @author J&ouml;rg Schaible
069     * @author Aslak Helles&oslash;y
070     * @since 1.2
071     */
072    public class PoolingComponentAdapter extends DecoratingComponentAdapter implements LifecycleManager {
073    
074        private static final long serialVersionUID = 1L;
075    
076        /**
077         * Context of the PoolingComponentAdapter used to initialize it.
078         * 
079         * @author J&ouml;rg Schaible
080         * @since 1.2
081         */
082        public static interface Context {
083            /**
084             * Retrieve the maximum size of the pool. An implementation may return the maximum value or
085             * {@link PoolingComponentAdapter#UNLIMITED_SIZE} for <em>unlimited</em> growth.
086             * 
087             * @return the maximum pool size
088             * @since 1.2
089             */
090            int getMaxSize();
091    
092            /**
093             * Retrieve the maximum number of milliseconds to wait for a returned element. An
094             * implementation may return alternatively {@link PoolingComponentAdapter#BLOCK_ON_WAIT} or
095             * {@link PoolingComponentAdapter#FAIL_ON_WAIT}.
096             * 
097             * @return the maximum number of milliseconds to wait
098             * @since 1.2
099             */
100            int getMaxWaitInMilliseconds();
101    
102            /**
103             * Allow the implementation to invoke the garbace collector manually if the pool is
104             * exhausted.
105             * 
106             * @return <code>true</code> for an internal call to {@link System#gc()}
107             * @since 1.2
108             */
109            boolean autostartGC();
110    
111            /**
112             * Retrieve the ProxyFactory to use to create the pooling proxies.
113             * 
114             * @return the {@link ProxyFactory}
115             * @since 1.2
116             */
117            ProxyFactory getProxyFactory();
118    
119            /**
120             * Retrieve the {@link Resetter} of the objects returning to the pool.
121             * 
122             * @return the Resetter instance
123             * @since 1.2
124             */
125            Resetter getResetter();
126    
127            /**
128             * Retrieve the serialization mode of the pool. Following values are possible:
129             * <ul>
130             * <li>{@link Pool#SERIALIZATION_STANDARD}</li>
131             * <li>{@link Pool#SERIALIZATION_NONE}</li>
132             * <li>{@link Pool#SERIALIZATION_FORCE}</li>
133             * </ul>
134             * 
135             * @return the serialization mode
136             * @since 1.2
137             */
138            int getSerializationMode();
139        }
140    
141        /**
142         * The default context for a PoolingComponentAdapter.
143         * 
144         * @author J&ouml;rg Schaible
145         * @since 1.2
146         */
147        public static class DefaultContext implements Context {
148    
149            /**
150             * {@inheritDoc} Returns {@link PoolingComponentAdapter#DEFAULT_MAX_SIZE}.
151             */
152            public int getMaxSize() {
153                return DEFAULT_MAX_SIZE;
154            }
155    
156            /**
157             * {@inheritDoc} Returns {@link PoolingComponentAdapter#FAIL_ON_WAIT}.
158             */
159            public int getMaxWaitInMilliseconds() {
160                return FAIL_ON_WAIT;
161            }
162    
163            /**
164             * {@inheritDoc} Returns <code>false</code>.
165             */
166            public boolean autostartGC() {
167                return false;
168            }
169    
170            /**
171             * {@inheritDoc} Returns a {@link StandardProxyFactory}.
172             */
173            public ProxyFactory getProxyFactory() {
174                return new StandardProxyFactory();
175            }
176    
177            /**
178             * {@inheritDoc} Returns the {@link PoolingComponentAdapter#DEFAULT_RESETTER}.
179             */
180            public Resetter getResetter() {
181                return DEFAULT_RESETTER;
182            }
183    
184            /**
185             * {@inheritDoc} Returns {@link Pool#SERIALIZATION_STANDARD}.
186             */
187            public int getSerializationMode() {
188                return Pool.SERIALIZATION_STANDARD;
189            }
190    
191        }
192    
193        /**
194         * <code>UNLIMITED_SIZE</code> is the value to set the maximum size of the pool to unlimited ({@link Integer#MAX_VALUE}
195         * in fact).
196         */
197        public static final int UNLIMITED_SIZE = Integer.MAX_VALUE;
198        /**
199         * <code>DEFAULT_MAX_SIZE</code> is the default size of the pool.
200         */
201        public static final int DEFAULT_MAX_SIZE = 8;
202        /**
203         * <code>BLOCK_ON_WAIT</code> forces the pool to wait until an object of the pool is returning
204         * in case none is immediately available.
205         */
206        public static final int BLOCK_ON_WAIT = 0;
207        /**
208         * <code>FAIL_ON_WAIT</code> forces the pool to fail none is immediately available.
209         */
210        public static final int FAIL_ON_WAIT = -1;
211        /**
212         * <code>DEFAULT_RESETTER</code> is a {@link NoOperationResetter} that is used by default.
213         */
214        public static final Resetter DEFAULT_RESETTER = new NoOperationResetter();
215    
216        private int maxPoolSize;
217        private int waitMilliSeconds;
218        private Pool pool;
219        private int serializationMode;
220        private boolean autostartGC;
221        private boolean started;
222        private boolean disposed;
223        private boolean delegateHasLifecylce;
224        private transient List components;
225    
226        /**
227         * Construct a PoolingComponentAdapter with default settings.
228         * 
229         * @param delegate the delegated ComponentAdapter
230         * @since 1.2
231         */
232        public PoolingComponentAdapter(ComponentAdapter delegate) {
233            this(delegate, new DefaultContext());
234        }
235    
236        /**
237         * Construct a PoolingComponentAdapter. Remember, that the implementation will request new
238         * components from the delegate as long as no component instance is available in the pool and
239         * the maximum pool size is not reached. Therefore the delegate may not return the same
240         * component instance twice. Ensure, that the used {@link ComponentAdapter} does not cache.
241         * 
242         * @param delegate the delegated ComponentAdapter
243         * @param context the {@link Context} of the pool
244         * @throws IllegalArgumentException if the maximum pool size or the serialization mode is
245         *             invalid
246         * @since 1.2
247         */
248        public PoolingComponentAdapter(ComponentAdapter delegate, Context context) {
249            super(delegate);
250            this.maxPoolSize = context.getMaxSize();
251            this.waitMilliSeconds = context.getMaxWaitInMilliseconds();
252            this.autostartGC = context.autostartGC();
253            this.serializationMode = context.getSerializationMode();
254            if (maxPoolSize <= 0) {
255                throw new IllegalArgumentException("Invalid maximum pool size");
256            }
257            started = false;
258            disposed = false;
259            delegateHasLifecylce = delegate instanceof LifecycleStrategy
260                    && ((LifecycleStrategy)delegate)
261                            .hasLifecycle(delegate.getComponentImplementation());
262            components = new ArrayList();
263    
264            final Class type = delegate.getComponentKey() instanceof Class ? (Class)delegate
265                    .getComponentKey() : delegate.getComponentImplementation();
266            final Resetter resetter = context.getResetter();
267            this.pool = new Pool(type, delegateHasLifecylce ? new LifecycleResetter(
268                    this, resetter) : resetter, context.getProxyFactory(), serializationMode);
269        }
270    
271        /**
272         * Construct an empty ComponentAdapter, used for serialization with reflection only.
273         * 
274         * @since 1.2
275         */
276        protected PoolingComponentAdapter() {
277            // @todo super class should support standard ctor
278            super((ComponentAdapter)Null.object(ComponentAdapter.class));
279        }
280    
281        /**
282         * {@inheritDoc}
283         * <p>
284         * As long as the maximum size of the pool is not reached and the pool is exhausted, the
285         * implementation will request its delegate for a new instance, that will be managed by the
286         * pool. Only if the maximum size of the pool is reached, the implementation may wait (depends
287         * on the initializing {@link Context}) for a returning object.
288         * </p>
289         * 
290         * @throws PoolException if the pool is exhausted or waiting for a returning object timed out or
291         *             was interrupted
292         */
293        public Object getComponentInstance(PicoContainer container) {
294            if (delegateHasLifecylce) {
295                if (disposed) throw new IllegalStateException("Already disposed");
296            }
297            Object componentInstance = null;
298            long now = System.currentTimeMillis();
299            boolean gc = autostartGC;
300            while (true) {
301                synchronized (pool) {
302                    componentInstance = pool.get();
303                    if (componentInstance != null) {
304                        break;
305                    }
306                    if (maxPoolSize > pool.size()) {
307                        final Object component = super.getComponentInstance(container);
308                        if (delegateHasLifecylce) {
309                            components.add(component);
310                            if (started) {
311                                start(component);
312                            }
313                        }
314                        pool.add(component);
315                    } else if (!gc) {
316                        long after = System.currentTimeMillis();
317                        if (waitMilliSeconds < 0) {
318                            throw new PoolException("Pool exhausted");
319                        }
320                        if (waitMilliSeconds > 0 && after - now > waitMilliSeconds) {
321                            throw new PoolException("Time out wating for returning object into pool");
322                        }
323                        try {
324                            pool.wait(waitMilliSeconds); // Note, the pool notifies after an object
325                                                            // was returned
326                        } catch (InterruptedException e) {
327                            // give the client code of the current thread a chance to abort also
328                            Thread.currentThread().interrupt();
329                            throw new PoolException(
330                                    "Interrupted waiting for returning object into the pool", e);
331                        }
332                    } else {
333                        System.gc();
334                        gc = false;
335                    }
336                }
337            }
338            return componentInstance;
339        }
340    
341        /**
342         * Retrieve the current size of the pool. The returned value reflects the number of all managed
343         * components.
344         * 
345         * @return the number of components.
346         * @since 1.2
347         */
348        public int size() {
349            return pool.size();
350        }
351    
352        static class LifecycleResetter implements Resetter, Serializable {
353            private static final long serialVersionUID = 1L;
354            private Resetter delegate;
355            private PoolingComponentAdapter adapter;
356    
357            LifecycleResetter(final PoolingComponentAdapter adapter, final Resetter delegate) {
358                this.adapter = adapter;
359                this.delegate = delegate;
360            }
361    
362            public boolean reset(Object object) {
363                final boolean result = delegate.reset(object);
364                if (!result || adapter.disposed) {
365                    if (adapter.started) {
366                        adapter.stop(object);
367                    }
368                    adapter.components.remove(object);
369                    if (!adapter.disposed) {
370                        adapter.dispose(object);
371                    }
372                }
373                return result && !adapter.disposed;
374            }
375    
376        }
377    
378        /**
379         * Start of the container ensures that at least one pooled component has been started. Applies
380         * only if the delegated {@link ComponentAdapter} supports a lifecylce by implementing
381         * {@link LifecycleStrategy}.
382         * 
383         * @throws IllegalStateException if pool was already disposed
384         */
385        public void start(final PicoContainer container) {
386            if (delegateHasLifecylce) {
387                if (started) throw new IllegalStateException("Already started");
388                if (disposed) throw new IllegalStateException("Already disposed");
389                for (final Iterator iter = components.iterator(); iter.hasNext();) {
390                    start(iter.next());
391                }
392                started = true;
393                if (pool.size() == 0) {
394                    getComponentInstance(container);
395                }
396            }
397        }
398    
399        /**
400         * Stop of the container has no effect for the pool. Applies only if the delegated
401         * {@link ComponentAdapter} supports a lifecylce by implementing {@link LifecycleStrategy}.
402         * 
403         * @throws IllegalStateException if pool was already disposed
404         */
405        public void stop(final PicoContainer container) {
406            if (delegateHasLifecylce) {
407                if (!started) throw new IllegalStateException("Not started yet");
408                if (disposed) throw new IllegalStateException("Already disposed");
409                for (final Iterator iter = components.iterator(); iter.hasNext();) {
410                    stop(iter.next());
411                }
412                started = false;
413            }
414        }
415    
416        /**
417         * Dispose of the container will dispose all returning objects. They will not be added to the
418         * pool anymore. Applies only if the delegated {@link ComponentAdapter} supports a lifecylce by
419         * implementing {@link LifecycleStrategy}.
420         * 
421         * @throws IllegalStateException if pool was already disposed
422         */
423        public void dispose(final PicoContainer container) {
424            if (delegateHasLifecylce) {
425                if (started) throw new IllegalStateException("Not stopped yet");
426                if (disposed) throw new IllegalStateException("Already disposed");
427                disposed = true;
428                for (final Iterator iter = components.iterator(); iter.hasNext();) {
429                    dispose(iter.next());
430                }
431                // @todo: Release pooled components and clear collection
432            }
433        }
434    
435        private synchronized void writeObject(final ObjectOutputStream out) throws IOException {
436            out.defaultWriteObject();
437            int mode = serializationMode;
438            if (mode == Pool.SERIALIZATION_FORCE && components.size() > 0) {
439                try {
440                    final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
441                    final ObjectOutputStream testStream = new ObjectOutputStream(buffer);
442                    testStream.writeObject(components); // force NotSerializableException
443                    testStream.close();
444                } catch (final NotSerializableException e) {
445                    mode = Pool.SERIALIZATION_NONE;
446                }
447            }
448            if (mode == Pool.SERIALIZATION_STANDARD) {
449                out.writeObject(components);
450            } else {
451                out.writeObject(new ArrayList());
452            }
453        }
454    
455        private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
456            in.defaultReadObject();
457            components = (List)in.readObject();
458        }
459    }