/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2006 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 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.CommonEjb;
import org.ow2.jonas.deployment.ejb.xml.JonasSession;
import org.ow2.jonas.deployment.ejb.xml.Session;
import org.ow2.jonas.lib.util.BeanNaming;

import java.lang.reflect.Method;
import java.util.Iterator;




/**
 * class to hold meta-information related to a stateless session bean.
 *
 * @author Christophe Ney [cney@batisseurs.com] : Initial developer
 * @author Helene Joanin
 */
public class SessionStatelessDesc extends SessionDesc {

    /**
     * Default service endpoint jndi suffix
     */
    private static final String SERVICE_ENDPOINT_JNDI_SUFFIX = "_SE";

    /**
     * class name of the JOnAS wrapper (without package prefix)
     */
    private String wrpServiceEndpointName;
    private String wrpSEHomeName;

    /**
     * Singleton=True if only 1 SessionSwitch (and 1 Remote) when possible.
     */
    protected boolean singleton = true;

    /**
     * fully qualified class name of the JOnAS wrapper
     */
    private String fullWrpServiceEndpointName;
    private String fullWrpSEHomeName;

    /**
     * ServiceEndpoint Class
     */
    private Class serviceEndpointClass;

    /**
     * jndi name
     */
    private String serviceEndpointJndiName;

    /**
     * constructor: called when the DeploymentDescriptor is read. Currently,
     * called by both GenIC and createContainer.
     *
     * @param classLoader ClassLoader to load Bean's classes
     * @param ses XML Element for session standard deployment descriptor
     * @param asd Assembly Descriptor of the EjbJar
     * @param jSes XML Element for session jonas deployment descriptor
     * @param filename bean's jar filename
     *
     * @throws DeploymentDescException When SessionDesc cannot be instanciated
     */
    public SessionStatelessDesc(ClassLoader classLoader, Session ses, AssemblyDescriptor asd, JonasSession jSes,
               JLinkedList jMDRList, String filename) throws DeploymentDescException {
        super(classLoader, ses, asd, jSes, jMDRList, filename);

        // create wrapper names
        String ejbIdentifier = getIdentifier();
        if (getServiceEndpointClass() != null) {
            String packageName = BeanDesc.GENERATED_PREFIX +
                BeanNaming.getPackageName(getServiceEndpointClass().getName());
            wrpServiceEndpointName = new String("JOnAS" + ejbIdentifier + "ServiceEndpoint");
            fullWrpServiceEndpointName = BeanNaming.getClassName(packageName, wrpServiceEndpointName);
            wrpSEHomeName = new String("JOnAS" + ejbIdentifier + "SEHome");
            fullWrpSEHomeName = BeanNaming.getClassName(packageName, wrpSEHomeName);
        }

        // get jndi-endpoint-name
        if (jSes.getJndiEndpointName() != null) {
            serviceEndpointJndiName = jSes.getJndiEndpointName();
        } else {
            serviceEndpointJndiName = getJndiName() + SERVICE_ENDPOINT_JNDI_SUFFIX;
        }

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

        // 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());
            }
        }
    }

    /**
     * Check that the bean descriptor is valid
     *
     * @exception DeploymentDescException thrown for non-valid bean
     */
    public void check() throws DeploymentDescException {
        super.check();
        // ejbClass should not implement javax.ejb.SessionSynchronization
        if (javax.ejb.SessionSynchronization.class.isAssignableFrom(ejbClass)) {
            throw new DeploymentDescException(ejbClass.getName()
                    + " should NOT implement javax.ejb.SessionSynchronization");
        }
    }

    /**
     * Permit Methods addition from subtypes Protected method that need to be
     * overridden in subclasses
     *
     * @param len method array length
     * @return new len value
     *
     * @throws DeploymentDescException when java reflection cannot be used on
     *         classes
     */
    protected int addEJBMethodDesc(int len) throws DeploymentDescException {

        if (this.serviceEndpointClass != null) {
            // session bean or entity bean with local interface
            Method[] m = this.serviceEndpointClass.getMethods();
            for (int i = 0; i < m.length; i++) {
                addMethodDesc(m[i], this.serviceEndpointClass);
                len++;
                // check RemoteException is thrown
                checkRemoteException(m[i], true);
            }
        }
        return len;
    }

    /**
     * load class for service-endpoint
     *
     * @param bd XML Element representing EJB Descriptor
     * @param classLoader ClassLoader used to load classes
     *
     * @throws DeploymentDescException when a class cannot be loaded
     */
    protected void loadExtraClasses(CommonEjb bd, ClassLoader classLoader) throws DeploymentDescException {

        Session ses = (Session) bd;

        // load service-endpoint interface
        if (ses.getServiceEndpoint() != null) {
            try {
                serviceEndpointClass = classLoader.loadClass(ses.getServiceEndpoint());
                // check service-endpoint extends java.rmi.Remote
                if (!java.rmi.Remote.class.isAssignableFrom(serviceEndpointClass)) {
                    throw new DeploymentDescException("ServiceEndpoint class '" + ses.getServiceEndpoint()
                            + "' does not extend java.rmi.Remote");
                }
            } catch (ClassNotFoundException e) {
                throw new DeploymentDescException("ServiceEndpoint class not found for bean " + ejbName, e);
            }
        }

    }

    /**
     * Returns the parent Class for a method given an interface type.
     *
     * @param intfType type of the interface (Home/Remote/LocalHome/Local or
     *        ServiceEndpoint)
     *
     * @return the parent class for a method given an interface type
     *
     * @throws DeploymentDescException when intfType is unknown
     */
    protected Class getParentClass(String intfType) throws DeploymentDescException {
        Class pClass = null;
        if (intfType.equals("Home")) {
            pClass = javax.ejb.EJBHome.class;
        } else if (intfType.equals("Remote")) {
            pClass = javax.ejb.EJBObject.class;
        } else if (intfType.equals("LocalHome")) {
            pClass = javax.ejb.EJBLocalHome.class;
        } else if (intfType.equals("Local")) {
            pClass = javax.ejb.EJBLocalObject.class;
        } else if (intfType.equals("ServiceEndpoint")) {
            pClass = java.rmi.Remote.class;
        } else {
            throw new DeploymentDescException(intfType + " is invalid value for method-intf on bean " + ejbName);
        }
        return pClass;
    }

    /**
     * @return Returns the serviceEndpointClass.
     */
    public Class getServiceEndpointClass() {
        return serviceEndpointClass;
    }

    private void checkValidServiceEndpointInterface() {
        // extends java.rmi.Remote
        // arguments and returns types are valid types for JAX-RPC (may be
        // Holders)
        // throws must includes java.rmi.RemoteException
        // each endpoint method must have a matching session bean method :
        // - same name
        // - same number and types of arguments and same return type
        // - session exceptions must eb included in endpoint
        // no EJBObject or EJBLocalObject
        // service endpoint interface must not include constants (public final
        // static)
    }

    /**
     * @return jndi name where ServiceEndpoint interface will be bound
     */
    public String getJndiServiceEndpointName() {
        return serviceEndpointJndiName;
    }

    /**
     * @return Returns the fullWrpServiceEndpointName.
     */
    public String getFullWrpServiceEndpointName() {
        return fullWrpServiceEndpointName;
    }

    /**
     * @return Returns the wrpServiceEndpointName.
     */
    public String getWrpServiceEndpointName() {
        return wrpServiceEndpointName;
    }

    /**
     * @return Returns the fullWrpSEHomeName.
     */
    public String getFullWrpSEHomeName() {
        return fullWrpSEHomeName;
    }

    /**
     * @return Returns the wrpSEHomeName.
     */
    public String getWrpSEHomeName() {
        return wrpSEHomeName;
    }

    /**
     * @return true for singleton session bean
     */
    public boolean isSingleton() {
        return singleton;
    }

}
