package es.ejie.frmk.infraestructura.poolCache;

import java.util.Enumeration;
import java.util.Vector;


/**
 * Implementacion basica y abstracta de una clase para manejar un recurso de pool.
 *
 */
public abstract class Q70RecursoPoolImpl implements Q70RecursoPool {
    protected static final int NO_MAX_RESOURCES = - 1;
    private int maxResources;
    private int checkedOut;
    private long timeout;
    private java.util.Vector pooledResources;

    /**
     * Constructor.
     * 
     * @param initResources Numero de recursos de pool iniciales
     * @param maxResources Numero maximo de recursos para el pool
     * @param timeout Tiempo de espera maximo para recuperar un recurso del pool
     */
    public Q70RecursoPoolImpl(int initResources, int maxResources, long timeout) {
      if (initResources < 0)
        throw new IllegalArgumentException("recursos iniciales debe ser mayor que 0");
      else if ((maxResources < initResources) && (maxResources != NO_MAX_RESOURCES))
        throw new IllegalArgumentException("maximo numero de recursos debe ser mayor que el minimo");
      else if (timeout < 0)
        throw new IllegalArgumentException("time out debe ser mayor que 0");

      this.maxResources    = maxResources;
      this.pooledResources = new Vector();
      this.checkedOut      = 0;
      this.timeout         = timeout;
    }

    /**
     * @see es.ejie.frmk.infraestructura.poolCache.Q70RecursoPool#freeResource(es.ejie.frmk.infraestructura.poolCache.Q70RecursoPoolable)
     */
    public synchronized void freeResource(Q70RecursoPoolable resource) {
      this.pooledResources.addElement(resource);
      this.checkedOut--;
      notifyAll();
    }

    private Q70RecursoPoolable getPooledResource() throws Q70PoolException {
      Q70RecursoPoolable resource  = null;

      if (this.pooledResources.size() > 0)
      {
        resource = (Q70RecursoPoolable)this.pooledResources.firstElement();
        this.pooledResources.removeElementAt(0);
      }
      else if ((this.maxResources == NO_MAX_RESOURCES) || (this.checkedOut < maxResources))
      {
        resource = newResource();
      }

      return resource;
    }

    /**
     * @see es.ejie.frmk.infraestructura.poolCache.Q70RecursoPool#getResource()
     */
    public synchronized Q70RecursoPoolable getResource() throws Q70PoolException {
      Q70RecursoPoolable resource  = null;
      long           startTime = System.currentTimeMillis();
      long           remaining = this.timeout;

      while (((resource = getPooledResource()) == null) && (remaining > 0))
      {
        try {wait(remaining);}
        catch (InterruptedException ex) {}

        remaining = this.timeout - (System.currentTimeMillis() - startTime);
      }

      if (resource != null)
        this.checkedOut++;
     else
	throw new Q70PoolException(this.getClass().getName(), "getResource", "Sobrepasado tiempo espera recurso", new Exception());             

      return resource;
    }

    /**
     * Devuelve un nuevo recurso de pool para un almacen concreto
     * 
     * @return Recurso de pool 
     * @throws Q70PoolException
     */
    protected abstract Q70RecursoPoolable newResource() throws Q70PoolException;

    /**
     * @see es.ejie.frmk.infraestructura.poolCache.Q70RecursoPool#release()
     */
    public synchronized void release() {
      for (Enumeration x = this.pooledResources.elements(); x.hasMoreElements();)
      {
        Q70RecursoPoolable resource = (Q70RecursoPoolable)x.nextElement();

        release(resource);
      }
      pooledResources.clear();
    }

    /**
     * Resetea un recurso. Este metodo será sobreescrito por las subclases
     * 
     * @param resource Recurso a resetear
     */
    protected abstract void release(Q70RecursoPoolable resource);

    
    /**
     * @see es.ejie.frmk.infraestructura.poolCache.Q70RecursoPool#size()
     */
    public int size() {return this.pooledResources.size();}
}
