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örg Schaible
069 * @author Aslak Hellesø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ö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ö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 }