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 }