fr.aliasource.webmail.pool.Pool.java Source code

Java tutorial

Introduction

Here is the source code for fr.aliasource.webmail.pool.Pool.java

Source

/* ***** BEGIN LICENSE BLOCK *****
 * Version: GPL 2.0
 *
 * The contents of this file are subject to the GNU General Public
 * License Version 2 or later (the "GPL").
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Initial Developer of the Original Code is
 *   MiniG.org project members
 *
 * ***** END LICENSE BLOCK ***** */

package fr.aliasource.webmail.pool;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import fr.aliasource.webmail.pool.impl.KeepAliveTask;
import fr.aliasource.webmail.pool.impl.PoolableProxy;

public class Pool<T extends IPoolable> {

    private Set<T> objectsInUse;
    private LinkedBlockingQueue<T> availableObjects;
    private Log logger;
    private String poolId;
    private Timer keepAliveTimer;
    private String destroyMethod;

    public Pool(String poolId, IPoolableObjectFactory<T> factory, int poolSize) {
        this(poolId, factory, poolSize, "destroy");
    }

    public Pool(String poolId, IPoolableObjectFactory<T> factory, int poolSize, String destroyMethodName) {
        this(poolId, factory, poolSize, "destroy", 10000);
    }

    public Pool(String poolId, IPoolableObjectFactory<T> factory, int poolSize, String destroyMethodName,
            long keepAlivePeriod) {
        this.logger = LogFactory.getLog(getClass());
        this.poolId = poolId;
        this.objectsInUse = Collections.synchronizedSet(new HashSet<T>());
        this.destroyMethod = destroyMethodName;
        this.availableObjects = new LinkedBlockingQueue<T>(poolSize);

        for (int i = 0; i < poolSize; i++) {
            logger.info(poolId + ": Adding pooled object...");
            availableObjects.add(factory.createNewObject());
            logger.info(poolId + ": Pooled object added.");
        }

        keepAliveTimer = new Timer(poolId + "-keepalive-timer", true);
        KeepAliveTask<T> kaTsk = new KeepAliveTask<T>(availableObjects, factory, this);
        keepAliveTimer.scheduleAtFixedRate(kaTsk, 10000, keepAlivePeriod);
    }

    public T get() {
        return get(10, TimeUnit.SECONDS, true);
    }

    public T get(long timeout, TimeUnit tu, boolean retry) {
        T ret = null;
        do {
            try {
                ret = availableObjects.poll(timeout, tu);
            } catch (InterruptedException e) {
                logger.error(poolId + ": Interrupted exception");
            }
            if (ret == null) {
                String message = poolId + ": Took more than " + timeout + " " + tu + " to get a pooled item.";
                if (!retry) {
                    throw new RuntimeException(message);
                } else {
                    logger.warn(message);
                }
            }
        } while (ret == null);

        T proxied = PoolableProxy.createProxy(this, ret, destroyMethod);
        objectsInUse.add(ret);
        return proxied;
    }

    public void reclaim(T poolable) {
        boolean present = objectsInUse.remove(poolable);
        if (!present) {
            logger.error("Cannot reclaim an object not from this pool " + poolable.getClass());
            return;
        }

        // the keep alive task might have refilled the pool
        boolean accepted = availableObjects.offer(poolable);
        if (!accepted) {
            logger.warn("could not offer poolable to avaiableObjects");
            if (!availableObjects.contains(poolable)) {
                try {
                    nativeDestroy(poolable);
                } catch (Throwable e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
    }

    public void destroy() {
        if (!objectsInUse.isEmpty()) {
            logger.warn(poolId + ": Closing pool with active IPoolable(s).");
        }
        keepAliveTimer.cancel();
        for (T poolable : availableObjects) {
            try {
                nativeDestroy(poolable);
            } catch (Exception e) {
                logger.warn("cannot call destroy method '" + destroyMethod + "'", e);
            }
        }
        logger.info(poolId + ": pool destroyed.");
    }

    public void nativeDestroy(T poolable)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method m = poolable.getClass().getMethod(destroyMethod);
        m.invoke(poolable);
    }

    public String getUsageReport() {
        StringBuilder sb = new StringBuilder();
        sb.append(poolId);
        sb.append(" inUse: ");
        sb.append(objectsInUse.size());
        sb.append(" available: ");
        sb.append(availableObjects.size());
        sb.append(" currentTotal: " + (objectsInUse.size() + availableObjects.size()));
        return sb.toString();
    }

    public int getUsageCount() {
        // Production logs show that this can return -1... JDK bug ?!
        return Math.max(0, objectsInUse.size());
    }

}