KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > picocontainer > gems > adapters > PoolingComponentAdapter


1 /*****************************************************************************
2  * Copyright (C) NanoContainer Organization. All rights reserved. *
3  * ------------------------------------------------------------------------- *
4  * The software in this package is published under the terms of the BSD *
5  * style license a copy of which has been included with this distribution in *
6  * the LICENSE.txt file. *
7  * *
8  * Original code by Aslak Hellesoy & Joerg Schaible *
9  *****************************************************************************/

10 package org.picocontainer.gems.adapters;
11
12 import java.io.ByteArrayOutputStream JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.io.NotSerializableException JavaDoc;
15 import java.io.ObjectInputStream JavaDoc;
16 import java.io.ObjectOutputStream JavaDoc;
17 import java.io.Serializable JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.List JavaDoc;
21
22 import org.picocontainer.ComponentAdapter;
23 import org.picocontainer.LifecycleManager;
24 import org.picocontainer.PicoContainer;
25 import org.picocontainer.defaults.DecoratingComponentAdapter;
26 import org.picocontainer.defaults.LifecycleStrategy;
27
28 import com.thoughtworks.proxy.ProxyFactory;
29 import com.thoughtworks.proxy.factory.StandardProxyFactory;
30 import com.thoughtworks.proxy.kit.NoOperationResetter;
31 import com.thoughtworks.proxy.kit.Resetter;
32 import com.thoughtworks.proxy.toys.nullobject.Null;
33 import com.thoughtworks.proxy.toys.pool.Pool;
34
35
36 /**
37  * {@link ComponentAdapter} implementation that pools components.
38  * <p>
39  * The implementation utilizes a delegated ComponentAdapter to create the instances of the pool. The
40  * pool can be configured to grow unlimited or to a maximum size. If a component is requested from
41  * this adapter, the implementation returns an availailabe instance from the pool or will create a
42  * new one, if the maximum pool size is not reached yet. If none is available, the implementation
43  * can wait a defined time for a returned object before it throws a {@link PoolException}.
44  * </p>
45  * <p>
46  * This implementation uses the {@link Pool} toy from the <a
47  * HREF="http://proxytoys.codehaus.org">ProxyToys</a> project. This ensures, that any component,
48  * that is out of scope will be automatically returned to the pool by the garbage collector.
49  * Additionally will every component instance also implement
50  * {@link com.thoughtworks.proxy.toys.pool.Poolable}, that can be used to return the instance
51  * manually. After returning an instance it should not be used in client code anymore.
52  * </p>
53  * <p>
54  * Before a returning object is added to the available instances of the pool again, it should be
55  * reinitialized to a normalized state. By providing a proper Resetter implementation this can be
56  * done automatically. If the object cannot be reused anymore it can also be dropped and the pool
57  * may request a new instance.
58  * </p>
59  * <p>
60  * The pool supports components with a lifecylce. If the delegated {@link ComponentAdapter}
61  * implements a {@link LifecycleStrategy}, any component retrieved form the pool will be started
62  * before and stopped again, when it returns back into the pool. Also if a component cannot be
63  * resetted it will automatically be disposed. If the container of the pool is disposed, that any
64  * returning object is also disposed and will not return to the pool anymore. Note, that current
65  * implementation cannot dispose pooled objects.
66  * </p>
67  *
68  * @author J&ouml;rg Schaible
69  * @author Aslak Helles&oslash;y
70  * @since 1.2
71  */

72 public class PoolingComponentAdapter extends DecoratingComponentAdapter implements LifecycleManager {
73
74     private static final long serialVersionUID = 1L;
75
76     /**
77      * Context of the PoolingComponentAdapter used to initialize it.
78      *
79      * @author J&ouml;rg Schaible
80      * @since 1.2
81      */

82     public static interface Context {
83         /**
84          * Retrieve the maximum size of the pool. An implementation may return the maximum value or
85          * {@link PoolingComponentAdapter#UNLIMITED_SIZE} for <em>unlimited</em> growth.
86          *
87          * @return the maximum pool size
88          * @since 1.2
89          */

90         int getMaxSize();
91
92         /**
93          * Retrieve the maximum number of milliseconds to wait for a returned element. An
94          * implementation may return alternatively {@link PoolingComponentAdapter#BLOCK_ON_WAIT} or
95          * {@link PoolingComponentAdapter#FAIL_ON_WAIT}.
96          *
97          * @return the maximum number of milliseconds to wait
98          * @since 1.2
99          */

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 JavaDoc 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 JavaDoc("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 JavaDoc();
263
264         final Class JavaDoc type = delegate.getComponentKey() instanceof Class JavaDoc ? (Class JavaDoc)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 JavaDoc getComponentInstance(PicoContainer container) {
294         if (delegateHasLifecylce) {
295             if (disposed) throw new IllegalStateException JavaDoc("Already disposed");
296         }
297         Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc {
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 JavaDoc 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 JavaDoc("Already started");
388             if (disposed) throw new IllegalStateException JavaDoc("Already disposed");
389             for (final Iterator JavaDoc 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 JavaDoc("Not started yet");
408             if (disposed) throw new IllegalStateException JavaDoc("Already disposed");
409             for (final Iterator JavaDoc 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 JavaDoc("Not stopped yet");
426             if (disposed) throw new IllegalStateException JavaDoc("Already disposed");
427             disposed = true;
428             for (final Iterator JavaDoc 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 JavaDoc out) throws IOException JavaDoc {
436         out.defaultWriteObject();
437         int mode = serializationMode;
438         if (mode == Pool.SERIALIZATION_FORCE && components.size() > 0) {
439             try {
440                 final ByteArrayOutputStream JavaDoc buffer = new ByteArrayOutputStream JavaDoc();
441                 final ObjectOutputStream JavaDoc testStream = new ObjectOutputStream JavaDoc(buffer);
442                 testStream.writeObject(components); // force NotSerializableException
443
testStream.close();
444             } catch (final NotSerializableException JavaDoc 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 JavaDoc());
452         }
453     }
454
455     private void readObject(final ObjectInputStream JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
456         in.defaultReadObject();
457         components = (List JavaDoc)in.readObject();
458     }
459 }
460
Popular Tags