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

// $Id: InvokerProviderJSE.java,v 1.11.4.2 2005/04/12 16:07:48 starksm Exp $
package org.jboss.webservice.server;

// $Id: InvokerProviderJSE.java,v 1.11.4.2 2005/04/12 16:07:48 starksm Exp $

import org.jboss.axis.MessageContext;
import org.jboss.axis.providers.java.RPCInvocation;
import org.jboss.axis.providers.java.RPCProvider;
import org.jboss.logging.Logger;
import org.jboss.metadata.WebMetaData;
import org.jboss.webservice.Constants;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

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

   // maps the SEI Method to the endpoint bean Method
   private Map methodMap = new HashMap();

   /**
    * Create an instance of the service endpoint bean
    */
   protected Object makeNewServiceObject(MessageContext msgContext, String className) throws Exception
   {
      log.debug("makeNewServiceObject: class=" + className);

      String serviceEndpointBean = portComponentInfo.getPortComponentMetaData().getServiceEndpointBean();
      if (serviceEndpointBean == null)
         throw new ServiceException("Service endpoint bean class not set");

      // set the context class loader in the msgContext
      ClassLoader ctxLoader = getContextClassLoader();
      msgContext.setClassLoader(ctxLoader);

      Object obj = super.makeNewServiceObject(msgContext, serviceEndpointBean);
      return obj;
   }

   /**
    * Before and after we call the service endpoint bean, we process the handler chains.
    * <p/>
    * The handler chain implemantation may replace the RPCInvocation object in the message context
    * if it finds the the handlers have modyfied the SOAPEnvelope.
    * <p/>
    * When you change the implementation here, make sure you do the same in the ServiceEndpointInterceptor
    */
   protected Object invokeTarget(RPCInvocation invocation) throws Exception
   {
      MessageContext msgContext = invocation.getMessageContext();

      try
      {
         // Call the request handler chain
         if (handlerChain.handleRequest(msgContext) == false)
         {
            log.warn("FIXME: handlerChain.handleRequest() returned false");
            return null;
         }

         // The handler chain might have replaced the invocation object
         invocation = (RPCInvocation)msgContext.getProperty(RPCProvider.RPC_INVOCATION);
      }
      catch (Exception e)
      {
         log.error("Error processing request handler chain", e);
         throw e;
      }

      Object retObj = null;
      try
      {
         // back to the RPCInvoker, that will call the service endpoint
         retObj = super.invokeTarget(invocation);
      }
      catch (Exception e)
      {
         msgContext.setProperty(Constants.LAST_FAULT, e);
         log.error("Error from service endpoint, processing fault handler chain", e);

         // Call the fault handler chain
         if (handlerChain.handleFault(msgContext) == false)
         {
            log.warn("FIXME: handlerChain.handleFault() returned false");
            return null;
         }

         // Throw the exception from the service endpoint to axis
         throw e;
      }

      try
      {
         // Prepare the response envelope for the response handlers
         invocation.prepareResponseEnvelope(retObj);

         // Call the response handler chain
         if (handlerChain.handleResponse(msgContext) == false)
         {
            log.warn("FIXME: handlerChain.handleResponse() returned false");
            return null;
         }
      }
      catch (Exception e)
      {
         log.error("Error processing response handler chain", e);
         throw e;
      }

      return retObj;
   }

   /**
    * 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 JSE: " + method);

      // map the SEI method to the bean method
      Method beanMethod = (Method)methodMap.get(method);
      if (beanMethod == null)
      {
         beanMethod = obj.getClass().getMethod(method.getName(), method.getParameterTypes());
         methodMap.put(method, beanMethod);
      }

      // invoke the bean method
      Object retObj = beanMethod.invoke(obj, argValues);
      return retObj;
   }

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