Package com.thoughtworks.proxy.toys.pool

Source Code of com.thoughtworks.proxy.toys.pool.Pool$PoolingInvoker

/*
* Created on 01-Jul-2004
*
* (c) 2004-2005 ThoughtWorks
*
* See license.txt for licence details
*/
package com.thoughtworks.proxy.toys.pool;

import com.thoughtworks.proxy.ProxyFactory;
import com.thoughtworks.proxy.factory.InvokerReference;
import com.thoughtworks.proxy.factory.StandardProxyFactory;
import com.thoughtworks.proxy.kit.ObjectReference;
import com.thoughtworks.proxy.kit.Resetter;
import com.thoughtworks.proxy.kit.SimpleReference;
import com.thoughtworks.proxy.toys.delegate.Delegating;
import com.thoughtworks.proxy.toys.delegate.DelegatingInvoker;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


/**
* A simple pool implementation that collects its unused components of a specific type automatically.
* <p>
* The pool will only manage instances that were explicitly passed into the pool before. For more sophisticated pooling
* strategies, derive from this class or wrap it.
* </p>
* <p>
* The implementation will provide these instances wrapped by a proxy, that will return the instance automatically to
* the pool, if it falls out of scope and is collected by the garbage collector. Since the pool only returns instances
* wrapped by a proxy that implements the {@link Poolable} interface, this can be used to release th instance manually
* to the pool also. With an implementation of the {@link Resetter} interface each element's status can be reset or the
* element can be dropped from the pool at all, if it is exhausted.
* </p>
* <p>
* A client can use the pool's monitor for an improved synchronization. Everytime an object is returned to the pool, all
* waiting Threads of the monitor will be notified. This notification will happen independently of the result of the
* {@link Resetter#reset(Object)} method.
* </p>
*
* @author J&ouml;rg Schaible
* @since 0.2
* @see com.thoughtworks.proxy.toys.pool
*/
public class Pool implements Serializable {
    private static final long serialVersionUID = 1L;
    private static final Method returnInstanceToPool;

    static {
        try {
            returnInstanceToPool = Poolable.class.getMethod("returnInstanceToPool", null);
        } catch (NoSuchMethodException e) {
            throw new InternalError();
        }
    }

    /**
     * <code>SERIALIZATION_FORCE</code> is the value for serialization of the pool with or without serializable
     * objects. If the obejcts cannot be serialized, the pool is emtpy after serialization und must be populated again.
     */
    public final static int SERIALIZATION_FORCE = 1;
    /**
     * <code>SERIALIZATION_STANDARD</code> is the value for the standard serialization of the pool with its objects.
     * If the obejcts cannot be serialized, a {@link NotSerializableException} is thrown.
     */
    public final static int SERIALIZATION_STANDARD = 0;
    /**
     * <code>SERIALIZATION_NONE</code> is the value for serialization of the pool without the objects. The pool is
     * emtpy after serialization und must be populated again.
     */
    public final static int SERIALIZATION_NONE = -1;

    private Class types[];
    private ProxyFactory factory;
    private transient Map busyInstances;
    private transient List availableInstances;
    private Resetter resetter;
    private int serializationMode;

    /**
     * Construct an Pool using the {@link StandardProxyFactory}.
     *
     * @param type the type of the instances
     * @param resetter the resetter of the pooled elements
     * @since 0.2
     */
    public Pool(final Class type, final Resetter resetter) {
        this(type, resetter, new StandardProxyFactory());
    }

    /**
     * Construct a populated Pool with a specific proxy factory.
     *
     * @param type the type of the instances
     * @param resetter the resetter of the pooled elements
     * @param proxyFactory the proxy factory to use
     * @since 0.2
     */
    public Pool(final Class type, final Resetter resetter, final ProxyFactory proxyFactory) {
        this();
        this.types = new Class[]{type, Poolable.class};
        this.factory = proxyFactory;
        this.resetter = resetter;
    }

    /**
     * Construct a populated Pool with a specific proxy factory and a serialization mode. This mode specify the
     * behaviour in case of a serialization of the Pool:
     * <ul>
     * <li>{@link #SERIALIZATION_STANDARD}: the standard mode, i.e. all elements of the pool are also serialized and a
     * {@link NotSerializableException} may thrown</li>
     * <li>{@link #SERIALIZATION_NONE}: no element of the pool is also serialized and it must be populated again after
     * serialization</li>
     * <li>{@link #SERIALIZATION_FORCE}: all element of the pool are serialized, if possible. Otherwise the pool is
     * empty after serialization and must be populated again.</li>
     * </ul>
     *
     * @param type the type of the instances
     * @param resetter the resetter of the pooled elements
     * @param proxyFactory the proxy factory to use
     * @param serializationMode <code>true</code> if serialization is done even if the pooled objects are not
     *            serializable. The deserialized Pool will not have any objects in the Pool though.
     * @throws IllegalArgumentException if the serialization mode is not one of the predefined values
     * @since 0.2
     */
    public Pool(final Class type, final Resetter resetter, final ProxyFactory proxyFactory, final int serializationMode) {
        this();
        this.types = new Class[]{type, Poolable.class};
        this.factory = proxyFactory;
        this.resetter = resetter;
        this.serializationMode = serializationMode;
        if (Math.abs(serializationMode) > 1) {
            throw new IllegalArgumentException("Invalid serialization mode");
        }
    }

    private Pool() {
        busyInstances = new HashMap();
        availableInstances = new ArrayList();
    }

    /**
     * Add a new instance as resource to the pool. The pool's monitor will be notified.
     *
     * @param instance the new instance
     * @throws NullPointerException if instance is <code>null</code>
     * @since 0.2
     */
    public synchronized void add(final Object instance) {
        if (instance == null) {
            throw new NullPointerException();
        }
        availableInstances.add(new SimpleReference(instance));
        notifyAll();
    }

    /**
     * Add an array of new instances as resources to the pool. The pool's monitor will be notified.
     *
     * @param instances the instances
     * @throws NullPointerException if instance is <code>null</code>
     * @since 0.2
     */
    public synchronized void add(final Object instances[]) {
        if (instances != null) {
            for (int i = 0; i < instances.length; ++i) {
                if (instances[i] == null) {
                    throw new NullPointerException();
                }
                availableInstances.add(new SimpleReference(instances[i]));
            }
            notifyAll();
        }
    }

    /**
     * Get an instance from the pool. If no instance is immediately available, the method will check internally for
     * returned objects from the garbage collector. This can be foreced by calling {@link System#gc()} first.
     *
     * @return an available instance from the pool or <em>null</em>.
     * @since 0.2
     */
    public synchronized Object get() {
        final Object result;
        if (availableInstances.size() > 0 || getAvailable() > 0) {
            final ObjectReference delegate = (ObjectReference)availableInstances.remove(0);
            result = new PoolingInvoker(this, factory, delegate, Delegating.MODE_DIRECT).proxy();
            final Object weakReference = new WeakReference(result);
            busyInstances.put(delegate.get(), weakReference);
        } else {
            result = null;
        }
        return result;
    }

    /**
     * Release a pool instance manually.
     *
     * @param object the instance to release
     * @throws ClassCastException if object was not {@link Poolable}.
     * @throws IllegalArgumentException if the object was not from this pool.
     * @since 0.2
     */
    public void release(final Object object) {
        final Poolable poolable = (Poolable)object;
        final PoolingInvoker invoker = (PoolingInvoker)((InvokerReference)object).getInvoker();
        if (this != invoker.getPoolInstance()) {
            throw new IllegalArgumentException("Release object from different pool");
        }
        poolable.returnInstanceToPool();
    }

    /**
     * Return the number of available instances of the pool. The method will also try to collect any pool instance that
     * was freed by the garbage collector. This can be foreced by calling {@link System#gc()} first. The pool's monitor
     * will be notified, if any object was collected and the {@link Resetter} retunred the object.
     *
     * @return the number of available instances.
     * @since 0.2
     */
    public synchronized int getAvailable() {
        if (busyInstances.size() > 0) {
            final List freedInstances = new ArrayList();
            for (final Iterator iter = busyInstances.keySet().iterator(); iter.hasNext();) {
                final Object target = iter.next();
                final WeakReference ref = (WeakReference)busyInstances.get(target);
                if (ref.get() == null) {
                    freedInstances.add(target);
                }
            }
            final List resettedInstances = new ArrayList();
            for (Iterator iter = freedInstances.iterator(); iter.hasNext();) {
                final Object element = iter.next();
                busyInstances.remove(element);
                if (resetter.reset(element)) {
                    resettedInstances.add(new SimpleReference(element));
                }
            }
            availableInstances.addAll(resettedInstances);
            if (freedInstances.size() > 0) {
                notifyAll();
            }
        }
        return availableInstances.size();
    }

    /**
     * Retrieve the number of instances managed by the pool.
     *
     * @return the number of instances.
     * @since 0.2
     */
    public synchronized int size() {
        return availableInstances.size() + busyInstances.size();
    }

    private synchronized void returnInstanceToPool(final ObjectReference reference) {
        busyInstances.remove(reference.get());
        if (resetter.reset(reference.get())) {
            availableInstances.add(reference);
        }
        notifyAll();
    }

    private synchronized void writeObject(final ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        final List instances = new ArrayList(availableInstances);
        for (final Iterator iter = busyInstances.keySet().iterator(); iter.hasNext();) {
            instances.add(new SimpleReference(iter.next()));
        }
        int mode = serializationMode;
        if (mode == SERIALIZATION_FORCE) {
            try {
                final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                final ObjectOutputStream testStream = new ObjectOutputStream(buffer);
                testStream.writeObject(instances); // force NotSerializableException
                testStream.close();
                mode = SERIALIZATION_STANDARD;
            } catch (final NotSerializableException e) {
                mode = SERIALIZATION_NONE;
            }
        }
        if (mode == SERIALIZATION_STANDARD) {
            out.writeObject(instances);
        } else {
            out.writeObject(new ArrayList());
        }
    }

    private synchronized void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        availableInstances = (List)in.readObject();
        busyInstances = new HashMap();
    }

    /**
     * The {@link com.thoughtworks.proxy.Invoker} of the proxy.
     *
     * @since 0.2
     */
    protected static class PoolingInvoker extends DelegatingInvoker {
        private static final long serialVersionUID = 1L;

        // explicit reference for serialization via reflection
        private Pool pool;

        /**
         * Construct a PoolingInvoker.
         *
         * @param pool the corresponding {@link Pool}
         * @param proxyFactory the {@link ProxyFactory} to use
         * @param delegateReference the {@link ObjectReference} with the delegate
         * @param delegationMode onde of the {@linkplain Delegating delgation modes}
         * @since 0.2
         */
        protected PoolingInvoker(
                Pool pool, ProxyFactory proxyFactory, ObjectReference delegateReference, int delegationMode) {
            super(proxyFactory, delegateReference, delegationMode);
            this.pool = pool;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result;
            if (method.equals(returnInstanceToPool)) {
                result = returnInstanceToPool();
            } else
                result = super.invoke(proxy, method, args);
            return result;
        }

        /**
         * Return the current instance to the pool. The pool's monitor will be notified, if the {@link Resetter} returns
         * the object.
         *
         * @return {@link Void#TYPE}
         * @since 0.2
         */
        public Object returnInstanceToPool() {
            pool.returnInstanceToPool(getDelegateReference());
            return Void.TYPE;
        }

        /**
         * Create a proxy for the types of the pool.
         *
         * @return the new proxy instance
         * @since 0.2
         */
        protected Object proxy() {
            return getProxyFactory().createProxy(pool.types, this);
        }

        private Pool getPoolInstance() {
            return pool;
        }
    }
}
TOP

Related Classes of com.thoughtworks.proxy.toys.pool.Pool$PoolingInvoker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.