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 */
011package com.thoughtworks.proxy.toys.pool;
012
013import java.io.ByteArrayOutputStream;
014import java.io.IOException;
015import java.io.NotSerializableException;
016import java.io.ObjectInputStream;
017import java.io.ObjectOutputStream;
018import java.io.Serializable;
019import java.lang.ref.WeakReference;
020import java.lang.reflect.Method;
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026
027import com.thoughtworks.proxy.ProxyFactory;
028import com.thoughtworks.proxy.factory.InvokerReference;
029import com.thoughtworks.proxy.factory.StandardProxyFactory;
030import com.thoughtworks.proxy.kit.NoOperationResetter;
031import com.thoughtworks.proxy.kit.ObjectReference;
032import com.thoughtworks.proxy.kit.Resetter;
033import com.thoughtworks.proxy.kit.SimpleReference;
034import com.thoughtworks.proxy.toys.delegate.DelegatingInvoker;
035import 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&ouml;rg Schaible
061 * @author Paul Hammant
062 * @see com.thoughtworks.proxy.toys.pool
063 * @since 0.2
064 */
065public 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}