/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.pool;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class SimpleObjectPool extends AbstractObjectPool {
    //已从池中取出的对象数目
    private volatile int numWorking = 0;
    private List pool = Collections.synchronizedList(new ArrayList());

    /**
     * the number of "sleeping" instances in the pool.
     */
    private int maxRest = 10;

    private int initNum = 0;

    /**
     * Poolable object will remove when it timeout
     * if timeout occured, present thant system is not busy
     */
    private long timeout = 5 * 60 * 1000; // 5m

    public SimpleObjectPool(ObjectFactory factory) {
        this(factory, 0, 10);
    }

    public SimpleObjectPool(ObjectFactory factory, int initNum, int maxRest) {
        this(factory, initNum, maxRest, 5 * 60 * 1000);
    }

    public SimpleObjectPool(ObjectFactory factory, int initNum, int maxRest, long timeout) {
        super(factory);
        this.factory = factory;
        this.initNum = initNum;
        this.maxRest = maxRest;
        this.timeout = timeout;
    }

    public SimpleObjectPool(Class factoryClass, Class poolableClass) {
        this(factoryClass, poolableClass, 0, 10);
    }

    public SimpleObjectPool(Class factoryClass, Class poolableClass, int initNum, int maxRest) {
        this(factoryClass, poolableClass, initNum, maxRest, 5 * 60 * 1000);
    }

    public SimpleObjectPool(Class factoryClass, Class poolableClass, int initNum, int maxRest, long timeout) {
        super(factoryClass, poolableClass);
        this.initNum = initNum;
        this.maxRest = maxRest;
        this.timeout = timeout;
    }

    /**
     * retrieve Object from object pool
     *
     * @return
     */
    public synchronized PoolableObject retrieveObject() throws Exception {
        logger.debug("retrieve object, pool size is " + pool.size() + ", max rest is " + maxRest);
        PoolableObject obj = null;
        synchronized(pool) {
            while(pool.size() > 0) {
                obj = (PoolableObject) pool.remove(0);
                if(!pool.isEmpty()) {
                    // check timeout, get the first poolable object not timeout
                    if(obj.getSleepTime() > timeout) {
                        factory.destroyObject(obj);
                        obj = null;
                    }
                    else {
                        // just return the last one whether it timeout or not
                        break;
                    }
                }
            }
/*
      if(pool.size() > 0){
        obj = (PoolableObject)pool.remove(0);
      }
*/
        }
        if(obj == null) { // 池中已经没有了
            obj = factory.makeObject();
        }
        if(obj != null) {
            obj.activate();
        }
        numWorking++;
        return obj;
    }

    /**
     * restore the retrived object to object pool
     *
     * @return true if object passivate && push success, false if failed
     */
    public boolean restoreObject(PoolableObject obj) {
        logger.debug("restore object " + obj + ", pool size " + pool.size());
        numWorking--;
        PoolableObject realObject = obj;

// Proxy PoolableObject 也支持 destory 等操作，所以不用特殊对待
//    if(Proxy.isProxyClass(obj.getClass())) {
//      realObject = ((PoolableObjectInvocationHandler)Proxy.getInvocationHandler(obj)).getPoolableObject();
//    }

        if(factory.validateObject(realObject)) {
            try {
                obj.passivate();
                synchronized(pool) {
                    // 未达初始值或者系统忙且还没有达到上限，返回池
//          if(pool.size() < initNum || (pool.size() < maxRest && numWorking > 0)) {
                    if(pool.size() < maxRest) {
                        pool.add(realObject);
                        return true;
                    }
                    else {
                        factory.destroyObject(realObject);
                        return false;
                    }
                }
            }
            catch(Exception e) {
                try {
                    factory.destroyObject(realObject);
                }
                catch(Exception ex) {
                    logger.warn(e);
                }
                return false;
                // do nothing , just jump to below
            }
        }
        else {
            try {
                factory.destroyObject(realObject);
            }
            catch(Exception e) {
                logger.warn(e.getMessage(), e);
            }
            return false;
        }
    }

    public boolean removeObject(PoolableObject obj) {
        logger.debug("remove object " + obj);
        return pool.remove(obj);
    }

    // clear the pool
    public synchronized void clear() {
        logger.debug("clear object pool");
        Iterator iter = pool.iterator();
        while(iter.hasNext()) {
            try {
                factory.destroyObject((PoolableObject) iter.next());
            }
            catch(Exception e) {
                logger.warn(e);
            }
        }
        synchronized(pool) {
            pool.clear();
        }
    }

    public int getWorking() {
        return numWorking;
    }

    public int getRest() {
        return pool.size();
    }

    protected void doInit() throws Exception {
        logger.debug("make " + initNum + " poolable object");
        int i = initNum;
        while(i-- > 0) {
            try {
                pool.add(factory.makeObject());
            }
            catch(Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    // just do clear
    protected void doDestroy() throws Exception {
        this.clear();
    }

    public int getInitNum() {
        return initNum;
    }

    public int getMaxRest() {
        return maxRest;
    }

    public static void main(String[] args) {

    }
}