/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

// $Id: InvokerProviderEJB.java,v 1.11.4.4 2005/04/12 16:07:47 starksm Exp $
package org.jboss.webservice.server;

// $Id: InvokerProviderEJB.java,v 1.11.4.4 2005/04/12 16:07:47 starksm Exp $

import org.jboss.axis.AxisFault;
import org.jboss.axis.MessageContext;
import org.jboss.axis.handlers.soap.SOAPService;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.InvocationKey;
import org.jboss.invocation.InvocationType;
import org.jboss.logging.Logger;
import org.jboss.metadata.ApplicationMetaData;
import org.jboss.metadata.BeanMetaData;
import org.jboss.security.SecurityAssociation;
import org.jboss.webservice.Constants;

import javax.management.MBeanException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import java.lang.reflect.Method;
import java.security.Principal;

/**
 * An Axis RPC provider for EJB endpoints.
 *
 * @author Thomas.Diesler@jboss.org
 * @since 15-April-2004
 */
public class InvokerProviderEJB extends InvokerProvider
{
   /** @since */
   static final long serialVersionUID = 7848928202043133442L;
   // provide logging
   private Logger log = Logger.getLogger(InvokerProviderEJB.class);

   private String jndiName;
   private ObjectName containerName;

   /**
    * Get deployment meta info
    */
   public void initServiceDesc(SOAPService service, MessageContext msgContext) throws AxisFault
   {
      super.initServiceDesc(service, msgContext);

      String ejbLink = portComponentInfo.getPortComponentMetaData().getEjbLink();
      if (ejbLink == null)
         throw new ServiceException("Cannot obtain ejb-link from port component");

      ApplicationMetaData applMetaData = (ApplicationMetaData)portComponentInfo.getDeploymentInfo().metaData;
      BeanMetaData beanMetaData = (BeanMetaData)applMetaData.getBeanByEjbName(ejbLink);
      if (beanMetaData == null)
         throw new ServiceException("Cannot obtain ejb meta data for: " + ejbLink);

      // verify the service endpoint
      String sei = portComponentInfo.getPortComponentMetaData().getServiceEndpointInterface();
      String serviceEndpoint = beanMetaData.getServiceEndpoint();
      if (sei.equals(serviceEndpoint) == false)
         throw new ServiceException("The <" + sei + "> does not correspond to <" + serviceEndpoint + "> in ejb-jar.xml");

      // get the bean's JNDI name
      jndiName = beanMetaData.getContainerObjectNameJndiName();
      if (jndiName == null)
         throw new ServiceException("Cannot obtain JNDI name for: " + ejbLink);

      try
      {
         containerName = new ObjectName("jboss.j2ee:jndiName=" + jndiName + ",service=EJB");
      }
      catch (MalformedObjectNameException e)
      {
         throw new ServiceException(e.toString());
      }
   }

   /**
    * Get the context CLassLoader for this service
    */
   protected ClassLoader getContextClassLoader()
   {
      return portComponentInfo.getDeploymentInfo().ucl;
   }

   /**
    * The actual invocation is done through the container, not through this object.
    */
   protected Object makeNewServiceObject(MessageContext msgContext, String className) throws Exception
   {
      log.debug("makeNewServiceObject: class=" + className);
      return null;
   }

   /**
    * This method encapsulates the method invocation.
    *
    * @param msgContext MessageContext
    * @param method     the target method.
    * @param obj        the target object
    * @param argValues  the method arguments
    */
   protected Object invokeServiceEndpoint(MessageContext msgContext,
                                          Method method, Object obj, Object[] argValues)
           throws Exception
   {
      log.debug("Invoke EJB: " + method);

      // these are provided by the ServerLoginHandler
      Principal principal = SecurityAssociation.getPrincipal();
      Object credential = SecurityAssociation.getCredential();

      // set the handler chain in the message context
      msgContext.setProperty(Constants.HANDLER_CHAIN, handlerChain);

      // setup the invocation
      Invocation inv = new Invocation(null, method, argValues, null, principal, credential);
      inv.setValue(InvocationKey.SOAP_MESSAGE_CONTEXT, msgContext);
      inv.setValue(InvocationKey.SOAP_MESSAGE, msgContext.getMessage());
      inv.setType(InvocationType.SERVICE_ENDPOINT);

      // invoke on the container
      Object[] invArgs = {inv};
      String[] sig = {Invocation.class.getName()};
      Object ret = server.invoke(containerName, "invoke", invArgs, sig);
      return ret;
   }

   /** Unwrap a potential MBeanException
    */
   protected void processException(Exception ex) throws AxisFault
   {
      Exception cause = ex;

      // Unwrap the MBeanException
      if (ex instanceof MBeanException && ex.getCause() instanceof Exception)
         cause = (Exception)ex.getCause();

      super.processException(cause);
   }
}