/**
 * JOnAS: Java(TM) Open Application Server Copyright (C) 1999 Bull S.A. Contact:
 * jonas-team@objectweb.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.objectweb.jonas_ejb.container;

import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.ejb.EJBException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;

import org.objectweb.jonas_ejb.deployment.api.MethodDesc;
import org.objectweb.jonas_ejb.deployment.api.SessionStatelessDesc;
import org.objectweb.jonas_ejb.lib.BeanNaming;
import org.objectweb.jonas_ejb.lib.EJBInvocation;

import org.objectweb.jonas.common.Log;

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

/**
 * This class is a factory for JServiceEndpoint objects.
 * @author Philippe Durieux
 */
public abstract class JServiceEndpointHome {

    /**
     * Logger, used also in the generated part.
     */
    protected static Logger logger = Log.getLogger(Log.JONAS_WS_EJBPROVIDER_PREFIX);
    protected static Logger lognaming = Log.getLogger(Log.JONAS_NAMING_PREFIX);

    protected SessionStatelessDesc dd;

    protected JFactory bf;

    // The static table of all JServiceEndpointHome objects
    protected static Map sehomeList = new HashMap();

    /**
     * constructor
     * @param dd The Session Deployment Decriptor
     * @param bf The Session Factory
     */
    public JServiceEndpointHome(SessionStatelessDesc dd, JStatelessFactory bf) {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        this.dd = dd;
        this.bf = bf;
    }

    /**
     * register this bean to JNDI (rebind) We register actually a Reference
     * object.
     */
    protected void register() throws NamingException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        String name = dd.getJndiServiceEndpointName();

        // Keep it in the static list for later retrieving.
        sehomeList.put(name, this);

        Reference ref = new Reference("org.objectweb.jonas_ejb.container.JServiceEndpointHome",
                "org.objectweb.jonas_ejb.container.HomeFactory", null);
        ref.add(new StringRefAddr("bean.name", name));
        bf.getInitialContext().rebind(name, ref);
    }

    /**
     * unregister this bean in JNDI (unbind)
     */
    protected void unregister() throws NamingException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        String name = dd.getJndiServiceEndpointName();
        // unregister in default InitialContext
        bf.getInitialContext().unbind(name);
        // remove from the static list
        sehomeList.remove(name);
    }

    /**
     * Get JServiceEndpointHome by its name
     * used by HomeFactory to retrieve object from its reference.
     * @param beanName The Bean JNDI local Name
     * @return The Bean ServiceEndpointHome
     */
    public static JServiceEndpointHome getSEHome(String beanName) {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        JServiceEndpointHome seh = (JServiceEndpointHome) sehomeList.get(beanName);
        return seh;
    }

    /**
     * unique create method
     */
    public JServiceEndpoint create() throws RemoteException {
        if (TraceEjb.isDebugIc()) {
            TraceEjb.interp.log(BasicLevel.DEBUG, "");
        }
        RequestCtx rctx = bf.preInvoke(MethodDesc.TX_NOT_SET);
        bf.checkSecurity(null);
        JStatelessSwitch bs = null;
        try {
            bs = (JStatelessSwitch) ((JStatelessFactory) bf).createEJB();
        } catch (javax.ejb.AccessLocalException e) {
            throw new EJBException("Security Exception thrown by an enterprise Bean", e);
        } catch (EJBException e) {
            rctx.sysExc = e;
            throw e;
        } catch (RuntimeException e) {
            rctx.sysExc = e;
            throw new EJBException("RuntimeException thrown by an enterprise Bean", e);
        } catch (Error e) {
            rctx.sysExc = e;
            throw new EJBException("Error thrown by an enterprise Bean" + e);
        } catch (RemoteException e) {
            rctx.sysExc = e;
            throw new EJBException("Remote Exception raised:", e);
        } finally {
            bf.postInvoke(rctx);
        }
        return bs.getServiceEndpoint();
    }

    /**
     * Set the bean environment in current context
     * @return previous Context
     */
    public Context setCompCtx() {
        if (lognaming.isLoggable(BasicLevel.DEBUG)) {
            lognaming.log(BasicLevel.DEBUG, "");
        }
        return bf.setComponentContext();
    }

    /**
     * Restore old value
     * @return previous Context
     */
    public void resetCompCtx(Context ctx) {
        if (lognaming.isLoggable(BasicLevel.DEBUG)) {
            lognaming.log(BasicLevel.DEBUG, "reset ctx:" + ctx);
        }
        bf.resetComponentContext(ctx);
    }

    /**
     * Check security with the Meesage Context
     */
    public void checkSecurity(MessageContext msgContext) {

        EJBInvocation ejb = new EJBInvocation();
        QName  q = null;
        try {
            SOAPEnvelope env = msgContext.getMessage().getSOAPPart().getEnvelope();
            SOAPBody body = env.getBody();
            Iterator it = body.getChildElements();
            SOAPElement operation = (SOAPElement) it.next();

            q = new QName(operation.getNamespaceURI(), operation.getLocalName());
            ejb.methodPermissionSignature = BeanNaming.getSignature(bf.getEJBName(), msgContext.getOperationByQName(q).getMethod());
        } catch (AxisFault e ) {
           // error during getting the operation from the message
           // not possible to check the security
           if (lognaming.isLoggable(BasicLevel.WARN))
                lognaming.log(BasicLevel.WARN, "can't retreive the operation from message...can not check the security");
           return;
        } catch (SOAPException e) {
            // error during getting the operation from the message
            // not possible to check the security
            if (lognaming.isLoggable(BasicLevel.WARN))
                 lognaming.log(BasicLevel.WARN, "can't retreive the operation from message...can not check the security");
            return;
        }

        bf.checkSecurity(ejb);
    }
    /**
     * @return A JServiceEndpoint that can be used by the JonasEJBProvider
     */
    public abstract JServiceEndpoint createServiceEndpointObject() throws RemoteException;


}

