001 /* 002 * (c) 2004, 2005, 2009, 2010 ThoughtWorks Ltd 003 * All rights reserved. 004 * 005 * The software in this package is published under the terms of the BSD 006 * style license a copy of which has been included with this distribution in 007 * the LICENSE.txt file. 008 * 009 * Created on 01-Jul-2004 010 */ 011 package com.thoughtworks.proxy.toys.pool; 012 013 import java.io.ByteArrayOutputStream; 014 import java.io.IOException; 015 import java.io.NotSerializableException; 016 import java.io.ObjectInputStream; 017 import java.io.ObjectOutputStream; 018 import java.io.Serializable; 019 import java.lang.ref.WeakReference; 020 import java.lang.reflect.Method; 021 import java.util.ArrayList; 022 import java.util.HashMap; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.Map; 026 027 import com.thoughtworks.proxy.ProxyFactory; 028 import com.thoughtworks.proxy.factory.InvokerReference; 029 import com.thoughtworks.proxy.factory.StandardProxyFactory; 030 import com.thoughtworks.proxy.kit.NoOperationResetter; 031 import com.thoughtworks.proxy.kit.ObjectReference; 032 import com.thoughtworks.proxy.kit.Resetter; 033 import com.thoughtworks.proxy.kit.SimpleReference; 034 import com.thoughtworks.proxy.toys.delegate.DelegatingInvoker; 035 import com.thoughtworks.proxy.toys.delegate.DelegationMode; 036 037 /** 038 * A simple pool implementation that collects its unused components of a specific type automatically. 039 * <p> 040 * The pool will only manage instances that were explicitly passed into the pool before. For more sophisticated pooling 041 * strategies, derive from this class or wrap it. 042 * </p> 043 * <p> 044 * The implementation will provide these instances wrapped by a proxy, that will return the instance automatically to 045 * the pool, if it falls out of scope and is collected by the garbage collector. Since the pool only returns instances 046 * wrapped by a proxy that implements the {@link Poolable} interface, this can be used to release the instance manually 047 * to the pool also. With an implementation of the {@link Resetter} interface each element's status can be reset or the 048 * element can be dropped from the pool at all, if it is exhausted. 049 * </p> 050 * <p> 051 * A client can use the pool's monitor for an improved synchronization. Every time an object is returned to the pool, all 052 * waiting Threads of the monitor will be notified. This notification will happen independently of the result of the 053 * {@link Resetter#reset(Object)} method. 054 * </p> 055 * <p> 056 * A Pool instance can be created as usual with a builder, but also using various constructors to support dependency 057 * injection. 058 * </p> 059 * 060 * @author Jörg Schaible 061 * @author Paul Hammant 062 * @see com.thoughtworks.proxy.toys.pool 063 * @since 0.2 064 */ 065 public class Pool<T> implements Serializable { 066 private static final long serialVersionUID = 1L; 067 private static final Method returnInstanceToPool; 068 069 static { 070 try { 071 returnInstanceToPool = Poolable.class.getMethod("returnInstanceToPool"); 072 } catch (NoSuchMethodException e) { 073 throw new ExceptionInInitializerError(e.toString()); 074 } 075 } 076 077 private Class<?> types[]; 078 private ProxyFactory factory; 079 private transient Map<T, WeakReference<T>> busyInstances; 080 private transient List<ObjectReference<T>> availableInstances; 081 private Resetter<? super T> resetter; 082 private SerializationMode serializationMode = SerializationMode.STANDARD; 083 084 /** 085 * Creates a factory for a pool instance which proxy the managed elements in the pool. 086 * 087 * @param type the type of the instances 088 * @return return the pool with parameters specified 089 * @since 1.0 090 */ 091 public static <T> PoolResettedBy<T> create(Class<T> type) { 092 return new PoolResettedBy<T>(new Pool<T>(type, new NoOperationResetter<T>())); 093 } 094 095 public static class PoolBuild<T> { 096 097 protected Pool<T> pool; 098 099 private PoolBuild(Pool<T> pool) { 100 this.pool = pool; 101 } 102 103 /** 104 * Build the pool using the {@link StandardProxyFactory}. 105 * 106 * @return the pool with predefined instances 107 * @since 1.0 108 */ 109 public Pool<T> build() { 110 return build(new StandardProxyFactory()); 111 } 112 113 /** 114 * Build the pool using a special {@link ProxyFactory}. 115 * 116 * @param factory the proxy factory to use 117 * @return the pool with predefined instances 118 * @since 0.2 119 */ 120 public Pool<T> build(ProxyFactory factory) { 121 pool.factory = factory; 122 return pool; 123 } 124 } 125 126 public static class PoolResettedBy<T> extends PoolWith<T> { 127 private PoolResettedBy(Pool<T> pool) { 128 super(pool); 129 } 130 131 public PoolWith<T> resettedBy(Resetter<? super T> resetter) { 132 pool.resetter = resetter; 133 return new PoolWith<T>(pool); 134 } 135 } 136 137 public static class PoolWith<T> extends PoolModeOrBuild<T> { 138 private PoolWith(Pool<T> pool) { 139 super(pool); 140 } 141 142 public PoolModeOrBuild<T> with(T... instances) { 143 pool.add(instances); 144 return new PoolModeOrBuild<T>(pool); 145 } 146 } 147 148 public static class PoolModeOrBuild<T> extends PoolBuild<T> { 149 150 private PoolModeOrBuild(Pool<T> pool) { 151 super(pool); 152 } 153 154 /** 155 * Specify the serializationMode 156 * <ul> 157 * <li>{@link SerializationMode#STANDARD}: the standard mode, i.e. all elements of the 158 * pool are also serialized and a {@link NotSerializableException} may thrown</li> 159 * <li>{@link SerializationMode#NONE}: no element of the pool is also serialized and it 160 * must be populated again after serialization</li> 161 * <li>{@link SerializationMode#FORCE}: all element of the pool are serialized, if 162 * possible. Otherwise the pool is empty after serialization and must be populated 163 * again.</li> 164 * </ul> 165 * 166 * @param serializationMode 167 * @return the pool with a certain serialization mode 168 * @throws IllegalArgumentException if the serialization mode is not one of the 169 * predefined values 170 * @since 0.2 171 */ 172 public PoolBuild<T> mode(SerializationMode serializationMode) { 173 pool.serializationMode = serializationMode; 174 return new PoolBuild<T>(pool); 175 } 176 } 177 178 /** 179 * Construct an Pool using the {@link StandardProxyFactory} for elements that do not have to 180 * be resetted. 181 * 182 * @param type the type of the instances 183 * @since 1.0 184 */ 185 public Pool(final Class<T> type) { 186 this(type, new NoOperationResetter<T>(), new StandardProxyFactory()); 187 } 188 189 /** 190 * Construct an Pool using the {@link StandardProxyFactory}. 191 * 192 * @param type the type of the instances 193 * @param resetter the resetter of the pooled elements 194 * @since 0.2 195 */ 196 public Pool(final Class<T> type, final Resetter<? super T> resetter) { 197 this(type, resetter, new StandardProxyFactory()); 198 } 199 200 /** 201 * Construct a populated Pool with a specific proxy factory for elements that do not have to 202 * be resetted. 203 * 204 * @param type the type of the instances 205 * @param proxyFactory the proxy factory to use 206 * @since 1.0 207 */ 208 public Pool(final Class<T> type, final ProxyFactory proxyFactory) { 209 this(type, new NoOperationResetter<T>(), proxyFactory, SerializationMode.STANDARD); 210 } 211 212 /** 213 * Construct a populated Pool with a specific proxy factory. 214 * 215 * @param type the type of the instances 216 * @param resetter the resetter of the pooled elements 217 * @param proxyFactory the proxy factory to use 218 * @since 0.2 219 */ 220 public Pool(final Class<T> type, final Resetter<? super T> resetter, final ProxyFactory proxyFactory) { 221 this(type, resetter, proxyFactory, SerializationMode.STANDARD); 222 } 223 224 /** 225 * Construct a populated Pool with a specific proxy factory and a serialization mode. This 226 * mode specify the behavior in case of a serialization of the Pool: 227 * <ul> 228 * <li>{@link SerializationMode#STANDARD}: the standard mode, i.e. all elements of the pool 229 * are also serialized and a {@link NotSerializableException} may thrown</li> 230 * <li>{@link SerializationMode#NONE}: no element of the pool is also serialized and it must 231 * be populated again after serialization</li> 232 * <li>{@link SerializationMode#FORCE}: all element of the pool are serialized, if possible. 233 * Otherwise the pool is empty after serialization and must be populated again.</li> 234 * </ul> 235 * 236 * @param type the type of the instances 237 * @param resetter the resetter of the pooled elements 238 * @param proxyFactory the proxy factory to use 239 * @param mode the serialization mode. 240 * @since 1.0 241 */ 242 public Pool(final Class<T> type, final Resetter<? super T> resetter, final ProxyFactory proxyFactory, final SerializationMode mode) { 243 this(); 244 this.types = new Class[]{type, Poolable.class}; 245 this.resetter = resetter; 246 this.factory = proxyFactory; 247 this.serializationMode = mode; 248 } 249 250 private Pool() { 251 busyInstances = new HashMap<T, WeakReference<T>>(); 252 availableInstances = new ArrayList<ObjectReference<T>>(); 253 } 254 255 /** 256 * Add an array of new instances as resources to the pool. The pool's monitor will be notified. 257 * 258 * @param instances the instances 259 * @throws NullPointerException if instance is <code>null</code> 260 * @since 0.2 261 */ 262 public synchronized void add(final T... instances) { 263 if (instances != null) { 264 for (T instance : instances) { 265 if (instance == null) { 266 throw new NullPointerException(); 267 } 268 availableInstances.add(new SimpleReference<T>(instance)); 269 } 270 notifyAll(); 271 } 272 } 273 274 /** 275 * Get an instance from the pool. If no instance is immediately available, the method will check internally for 276 * returned objects from the garbage collector. This can be forced by calling {@link System#gc()} first. 277 * 278 * @return an available instance from the pool or <em>null</em>. 279 * @since 0.2 280 */ 281 public synchronized T get() { 282 final T result; 283 if (availableInstances.size() > 0 || getAvailable() > 0) { 284 final ObjectReference<T> delegate = availableInstances.remove(0); 285 result = new PoolingInvoker<T>(this, factory, delegate, DelegationMode.DIRECT).proxy(); 286 final WeakReference<T> weakReference = new WeakReference<T>(result); 287 busyInstances.put(delegate.get(), weakReference); 288 } else { 289 result = null; 290 } 291 return result; 292 } 293 294 /** 295 * Release a pool instance manually. 296 * 297 * @param object the instance to release 298 * @throws ClassCastException if object was not {@link Poolable}. 299 * @throws IllegalArgumentException if the object was not from this pool. 300 * @since 0.2 301 */ 302 public void release(final T object) { 303 final Poolable poolable = Poolable.class.cast(object); 304 @SuppressWarnings("unchecked") 305 final PoolingInvoker<T> invoker = PoolingInvoker.class.cast(InvokerReference.class.cast(poolable).getInvoker()); 306 if (this != invoker.getPoolInstance()) { 307 throw new IllegalArgumentException("Release object from different pool"); 308 } 309 poolable.returnInstanceToPool(); 310 } 311 312 /** 313 * Return the number of available instances of the pool. The method will also try to collect any pool instance that 314 * was freed by the garbage collector. This can be forced by calling {@link System#gc()} first. The pool's monitor 315 * will be notified, if any object was collected and the {@link Resetter} returned the object. 316 * 317 * @return the number of available instances. 318 * @since 0.2 319 */ 320 public synchronized int getAvailable() { 321 if (busyInstances.size() > 0) { 322 final List<T> freedInstances = new ArrayList<T>(); 323 for (final T target : busyInstances.keySet()) { 324 final WeakReference<T> ref = busyInstances.get(target); 325 if (ref.get() == null) { 326 freedInstances.add(target); 327 } 328 } 329 final List<ObjectReference<T>> resettedInstances = new ArrayList<ObjectReference<T>>(); 330 for (final T element : freedInstances) { 331 busyInstances.remove(element); 332 if (resetter.reset(element)) { 333 resettedInstances.add(new SimpleReference<T>(element)); 334 } 335 } 336 availableInstances.addAll(resettedInstances); 337 if (freedInstances.size() > 0) { 338 notifyAll(); 339 } 340 } 341 return availableInstances.size(); 342 } 343 344 /** 345 * Retrieve the number of instances managed by the pool. 346 * 347 * @return the number of instances. 348 * @since 0.2 349 */ 350 public synchronized int size() { 351 return availableInstances.size() + busyInstances.size(); 352 } 353 354 private synchronized void returnInstanceToPool(final ObjectReference<T> reference) { 355 busyInstances.remove(reference.get()); 356 if (resetter.reset(reference.get())) { 357 availableInstances.add(reference); 358 } 359 notifyAll(); 360 } 361 362 private synchronized void writeObject(final ObjectOutputStream out) throws IOException { 363 out.defaultWriteObject(); 364 final List<ObjectReference<T>> instances = new ArrayList<ObjectReference<T>>(availableInstances); 365 Iterator<T> iter; 366 for (iter = busyInstances.keySet().iterator(); iter.hasNext();) { 367 instances.add(new SimpleReference<T>(iter.next())); 368 } 369 SerializationMode mode = serializationMode; 370 if (mode == SerializationMode.FORCE) { 371 try { 372 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 373 final ObjectOutputStream testStream = new ObjectOutputStream(buffer); 374 testStream.writeObject(instances); // force NotSerializableException 375 testStream.close(); 376 mode = SerializationMode.STANDARD; 377 } catch (final NotSerializableException e) { 378 mode = SerializationMode.NONE; 379 } 380 } 381 if (mode == SerializationMode.STANDARD) { 382 out.writeObject(instances); 383 } else { 384 out.writeObject(new ArrayList<ObjectReference<T>>()); 385 } 386 } 387 388 private synchronized void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 389 in.defaultReadObject(); 390 @SuppressWarnings("unchecked") 391 final List<ObjectReference<T>> list = List.class.cast(in.readObject()); 392 availableInstances = list; 393 busyInstances = new HashMap<T, WeakReference<T>>(); 394 } 395 396 /** 397 * The {@link com.thoughtworks.proxy.Invoker} of the proxy. 398 * 399 * @since 0.2 400 */ 401 protected static class PoolingInvoker<T> extends DelegatingInvoker<T> { 402 private static final long serialVersionUID = 1L; 403 404 // explicit reference for serialization via reflection 405 private Pool<T> pool; 406 407 /** 408 * Construct a PoolingInvoker. 409 * 410 * @param pool the corresponding {@link Pool} 411 * @param proxyFactory the {@link ProxyFactory} to use 412 * @param delegateReference the {@link ObjectReference} with the delegate 413 * @param delegationMode one of the {@linkplain DelegationMode delegation modes} 414 * @since 1.0 415 */ 416 protected PoolingInvoker( 417 Pool<T> pool, ProxyFactory proxyFactory, ObjectReference<T> delegateReference, DelegationMode delegationMode) { 418 super(proxyFactory, delegateReference, delegationMode); 419 this.pool = pool; 420 } 421 422 @Override 423 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 424 Object result; 425 if (method.equals(returnInstanceToPool)) { 426 returnInstanceToPool(); 427 result = Void.TYPE; 428 } else 429 result = super.invoke(proxy, method, args); 430 return result; 431 } 432 433 /** 434 * Return the current instance to the pool. The pool's monitor will be notified, if the {@link Resetter} returns 435 * the object. 436 * 437 * @since 0.2 438 */ 439 public void returnInstanceToPool() { 440 pool.returnInstanceToPool(getDelegateReference()); 441 } 442 443 /** 444 * Create a proxy for the types of the pool. 445 * 446 * @return the new proxy instance 447 * @since 0.2 448 */ 449 protected T proxy() { 450 return getProxyFactory().<T>createProxy(this, pool.types); 451 } 452 453 private Pool<T> getPoolInstance() { 454 return pool; 455 } 456 } 457 }