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 }