/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.ow2.jonas.deployment.ejb;

import java.util.Iterator;

import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.common.xml.JLinkedList;
import org.ow2.jonas.deployment.ejb.xml.AssemblyDescriptor;
import org.ow2.jonas.deployment.ejb.xml.Entity;
import org.ow2.jonas.deployment.ejb.xml.JonasEntity;
import org.ow2.jonas.lib.util.BeanNaming;



import org.objectweb.util.monolog.api.BasicLevel;


/**
 * Base class to hold meta-information related to an entity bean.
 * @author Christophe Ney [cney@batisseurs.com] : Initial developer
 * @author Helene Joanin
 * @author Helene Joanin: take into account ejbSelect() methods.
 * @author Helene Joanin: unsetting transaction attribute set to a default value.
 */
public abstract class EntityDesc extends BeanDesc {


    /**
     * remote methods for which no transaction attribute is to be set
     */
    protected static final String METHODS_REMOTE_NO_TX = ",getEJBHome,getHandle,getPrimaryKey,isIdentical,";

    /**
     * home methods for which no transaction attribute is to be set
     */
    protected static final String METHODS_HOME_NO_TX = ",getEJBMetaData,getHomeHandle,";

    /**
     * This field contains the class name of the factory instanciate by the
     * container.
     */
    protected Class primaryKeyClass;
    protected boolean reentrant;
    protected int passivationTimeout = 0;
    protected int inactivityTimeout = 0;
    protected int deadlockTimeout = 20;  // default = 20 sec.
    protected int readTimeout = 15 * 60;    // default = 15mn.
    protected int maxWaitTime = 0;  // default = 0s
    protected boolean shared = false;
    protected boolean prefetch = false;
    protected boolean hardLimit = false;

    // Field used for pk auto-generate
    protected boolean jdbcAutomaticPk = false; // first implementation  with specific tag into JOnAS Descriptor file
    protected boolean pkObjectType = false;   // second implementation with prim-key-type=java.lang.Object (cf spec ?14.1.9.3)

    // mapping cleanup policy
    public static final int CLEANUP_NONE = 0;
    public static final int CLEANUP_CREATE = 1;
    public static final int CLEANUP_REMOVEDATA = 2;
    public static final int CLEANUP_REMOVEALL = 3;
    protected int cleanup = CLEANUP_CREATE;

    // lock policy
    public static final int LOCK_CONTAINER_READ_UNCOMMITTED = 0;
    public static final int LOCK_CONTAINER_SERIALIZED = 1;
    public static final int LOCK_CONTAINER_READ_COMMITTED = 2;
    public static final int LOCK_DATABASE = 3;
    public static final int LOCK_READ_ONLY = 4;
    public static final int LOCK_CONTAINER_READ_WRITE = 5;
    public static final int LOCK_CONTAINER_SERIALIZED_TRANSACTED = 6;
    protected int lockPolicy = LOCK_CONTAINER_SERIALIZED;

    /**
     * constructor to be used by parent node
     */
    public EntityDesc(ClassLoader classLoader,
                       Entity ent,
                       AssemblyDescriptor asd,
                       JonasEntity jEnt,
                       JLinkedList jMDRList,
                       String fileName)
        throws DeploymentDescException {

        super(classLoader, ent, jEnt, asd, jMDRList, fileName);

        // primary Key class
        try {
            // Test for automatic PK
            if (ent.getPrimKeyClass().equalsIgnoreCase("Object") || ent.getPrimKeyClass().equalsIgnoreCase("java.lang.Object")) {
                primaryKeyClass = classLoader.loadClass("java.lang.Integer");
                pkObjectType = true;
            }
            else
                primaryKeyClass = classLoader.loadClass(ent.getPrimKeyClass());
        } catch (ClassNotFoundException e) {
            throw new DeploymentDescException("Primary Key class not found for bean " + this.ejbName, e);
        }

        // passivation timeout
        if (jEnt.getPassivationTimeout() != null) {
            String tstr = jEnt.getPassivationTimeout();
            Integer tval = new Integer(tstr);
            passivationTimeout = tval.intValue();
        }

        // inactivity timeout
        if (jEnt.getInactivityTimeout() != null) {
            String tstr = jEnt.getInactivityTimeout();
            Integer tval = new Integer(tstr);
            inactivityTimeout = tval.intValue();
        }

        // deadlock timeout
        if (jEnt.getDeadlockTimeout() != null) {
            String tstr = jEnt.getDeadlockTimeout();
            Integer tval = new Integer(tstr);
            deadlockTimeout = tval.intValue();
        }

        // read timeout
        if (jEnt.getReadTimeout() != null) {
            String tstr = jEnt.getReadTimeout();
            Integer tval = new Integer(tstr);
            readTimeout = tval.intValue();
        }

        // max wait time
        if (jEnt.getMaxWaitTime() != null) {
            String tstr = jEnt.getMaxWaitTime();
            Integer tval = new Integer(tstr);
            maxWaitTime = tval.intValue();
        }

        // reentrant
        if (ent.getReentrant().equalsIgnoreCase("True")) {
            reentrant = true;
        } else if (ent.getReentrant().equalsIgnoreCase("False")) {
            reentrant = false;
        } else {
            throw new DeploymentDescException("Invalid reentrant value for bean " + this.ejbName);
        }

        // prefetch
        if (jEnt.getPrefetch() != null) {
            if (jEnt.getPrefetch().equalsIgnoreCase("True")) {
                prefetch = true;
            } else if (jEnt.getPrefetch().equalsIgnoreCase("False")) {
                prefetch = false;
            } else {
                throw new DeploymentDescException("Invalid prefetch value for bean " + this.ejbName);
            }
        }

        // hard limit
        if (jEnt.getHardLimit() != null) {
            if (jEnt.getHardLimit().equalsIgnoreCase("True")) {
                hardLimit = true;
            } else if (jEnt.getPrefetch().equalsIgnoreCase("False")) {
                hardLimit = false;
            } else {
                throw new DeploymentDescException("Invalid hard-limit value for bean " + this.ejbName);
            }
        }

        // min-pool-size
        if (jEnt.getMinPoolSize() != null) {
            String tstr = jEnt.getMinPoolSize();
            Integer tval = new Integer(tstr);
            poolMin = tval.intValue();
        }

        // max-cache-size
        if (jEnt.getMaxCacheSize() != null) {
            String tstr = jEnt.getMaxCacheSize();
            Integer tval = new Integer(tstr);
            cacheMax = tval.intValue();
        }

        // lock policy.
        // Set default value for shared, depending on policy.
        if (jEnt.getLockPolicy() != null) {
            String tstr = jEnt.getLockPolicy();
            if (tstr.equals("container-serialized")) {
                lockPolicy = LOCK_CONTAINER_SERIALIZED;
                shared = false;
            } else if (tstr.equals("container-serialized-transacted")) {
                lockPolicy = LOCK_CONTAINER_SERIALIZED_TRANSACTED;
                shared = false;
            } else if (tstr.equals("container-read-committed")) {
                lockPolicy = LOCK_CONTAINER_READ_COMMITTED;
                shared = true;
            } else if (tstr.equals("container-read-uncommitted")) {
                lockPolicy = LOCK_CONTAINER_READ_UNCOMMITTED;
                shared = false;
            } else if (tstr.equals("database")) {
                lockPolicy = LOCK_DATABASE;
                shared = true;
            } else if (tstr.equals("read-only")) {
                lockPolicy = LOCK_READ_ONLY;
                shared = true;
            } else if (tstr.equals("container-read-write")) {
                lockPolicy = LOCK_CONTAINER_READ_WRITE;
                shared = false;
            } else {
                throw new DeploymentDescException("Invalid lock-policy value for bean " + jEnt.getEjbName());
            }
        }

        // shared
        if (jEnt.getShared() != null) {
            if (jEnt.getShared().equalsIgnoreCase("True")) {
                shared = true;
            } else if (jEnt.getShared().equalsIgnoreCase("False")) {
                shared = false;
            } else {
                throw new DeploymentDescException("Invalid shared value for bean " + this.ejbName);
            }
        }

        // cleanup policy. Possible values are :
        // create       = create table only if does not exist yet. (default)
        // none         = nothing is done (not implemented)
        // removeall    = drop table and recreate it.
        // removedata   = remove all data if exist, create table if does not exist.
        if (jEnt.getCleanup() != null) {
            String tstr = jEnt.getCleanup();
            if (tstr.equals("create")) {
                cleanup = CLEANUP_CREATE;
            } else if (tstr.equals("none")) {
                cleanup = CLEANUP_NONE;
            } else if (tstr.equals("removeall")) {
                cleanup = CLEANUP_REMOVEALL;
            } else if (tstr.equals("removedata")) {
                cleanup = CLEANUP_REMOVEDATA;
            } else {
                throw new DeploymentDescException("Invalid cleanup value for bean " + jEnt.getEjbName());
            }
        }

        // cache TxAttribute for ejbTimeout
        for (Iterator i = getMethodDescIterator(); i.hasNext();) {
            MethodDesc methd = (MethodDesc) i.next();
            if (methd.getMethod().getName().equals("ejbTimeout")) {
                timerTxAttribute = methd.getTxAttribute();
                ejbTimeoutSignature = BeanNaming.getSignature(getEjbName(), methd.getMethod());
            }
        }
    }

    /**
     * @return the cleanup policy for this bean
     */
    public int getCleanupPolicy() {
        return cleanup;
    }

    /**
     * @return the lock policy for this bean
     */
    public int getLockPolicy() {
        return lockPolicy;
    }

    /**
     * check that trans-attribute is valid for bean
     */
    protected void checkTxAttribute(MethodDesc md) throws DeploymentDescException {
        java.lang.reflect.Method m = md.getMethod();
        if (md.getTxAttribute() == MethodDesc.TX_NOT_SET) {
            // exclude method list for home interface
            if (javax.ejb.EJBHome.class.isAssignableFrom(m.getDeclaringClass())
                && (METHODS_HOME_NO_TX.indexOf("," + m.getName() + ",") != -1)) {
                return;
            }
            // exclude method list for remote interface
            if (javax.ejb.EJBObject.class.isAssignableFrom(m.getDeclaringClass())
                && (METHODS_REMOTE_NO_TX.indexOf("," + m.getName() + ",") != -1)) {
                return;
            }
            // exclude ejbSelect methods
            if (md.isEjbSelect()) {
                return;
            }
            // trans-attribute not set !
            // trace a warning and set the tx-attribute with the default value
            logger.log(BasicLevel.WARN,
                       "trans-attribute missing for method "
                       + m.toString() + " in entity bean "
                       + getEjbName()
                       + " (set to the default value "
                       + MethodDesc.TX_STR_DEFAULT_VALUE
                       + ")");
            md.setTxAttribute(MethodDesc.TX_STR_DEFAULT_VALUE);
        }
    }

    /**
     * Get the passivation timeout value
     */
    public int getPassivationTimeout() {
        return passivationTimeout;
    }

    /**
     * Get the inactivity timeout value
     */
    public int getInactivityTimeout() {
        return inactivityTimeout;
    }

    /**
     * Get the deadlock timeout value
     */
    public int getDeadlockTimeout() {
        return deadlockTimeout;
    }

    /**
     * Get the read timeout value
     */
    public int getReadTimeout() {
        return readTimeout;
    }

    /**
     * Get the max wait time
     */
    public int getMaxWaitTime() {
        return maxWaitTime;
    }

    /**
     * Get the entity's primary key class.
     * @return Class for the primary key
     */
    public Class getPrimaryKeyClass() {
        return primaryKeyClass;
    }

    /**
     * Assessor for reentrant entity bean
     * @return true for reentrant entity bean
     */
    public boolean isReentrant() {
        return reentrant;
    }

    /**
     * @return true for shared entity bean
     */
    public boolean isShared() {
        return shared;
    }

    /**
     * @return true for prefetch entity bean
     */
    public boolean isPrefetch() {
        return prefetch;
    }

    /**
     * @return true for hard-limit entity bean
     */
    public boolean isHardLimit() {
        return hardLimit;
    }

    /**
     * Assessor for existence of automatic-pk element to True value
     * @param field public field of the bean class
     * @return true if automatic-pk element value is true else otherwise false
     */
    public boolean isAutomaticPk() {
        return jdbcAutomaticPk;
    }

    /**
     * Assessor for primary key undefined (declare like java.lang.Object type)
     * @param field public field of the bean class
     * @return true if primary key is undefined (java.lang.Object type)
     */
    public boolean isUndefinedPK() {
        return pkObjectType;
    }

    /**
     * String representation of the object for test purpose
     * @return String representation of this object
     */
    public String toString() {
        StringBuffer ret = new StringBuffer();
        ret.append(super.toString());
        ret.append("\nPrimaryKeyClass()    =" + getPrimaryKeyClass().toString());
        ret.append("\nReentrant()          =" + isReentrant());
        ret.append("\nPassivationTimeout() =" + getPassivationTimeout());
        ret.append("\nInactivityTimeout()  =" + getInactivityTimeout());
        ret.append("\nDeadlockTimeout()    =" + getDeadlockTimeout());
        ret.append("\nReadTimeout()        =" + getReadTimeout());
        ret.append("\nMaxWaitTime()        =" + getMaxWaitTime());
        ret.append("\nShared()             =" + isShared());
        ret.append("\nPoolMin()            =" + getPoolMin());
        ret.append("\nCacheMax()           =" + getCacheMax());
        return ret.toString();
    }

}

