/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.webservice.server;

// $Id: ServiceEndpointInterceptor.java,v 1.4.4.2 2005/03/02 14:32:32 tdiesler Exp $

import org.jboss.axis.providers.java.RPCInvocation;
import org.jboss.axis.providers.java.RPCProvider;
import org.jboss.ejb.plugins.AbstractInterceptor;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.InvocationKey;
import org.jboss.webservice.Constants;

import javax.xml.rpc.handler.HandlerChain;
import javax.xml.rpc.handler.soap.SOAPMessageContext;

/**
 * This Interceptor does the ws4ee handler processing.
 * <p/>
 * According to the ws4ee spec the handler logic must be invoked after the container
 * applied method level security to the invocation. I don't think we can use Axis handlers
 * for ws4ee handler processing.
 *
 * @author Thomas.Diesler@jboss.org
 * @version $Revision: 1.4.4.2 $
 */
public class ServiceEndpointInterceptor
        extends AbstractInterceptor
{
   // Interceptor implementation --------------------------------------
   
   /**
    * 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 InvokerProviderJSE
    */
   public Object invoke(final Invocation mi) throws Exception
   {

      // It's not for us
      SOAPMessageContext msgContext = (SOAPMessageContext)mi.getPayloadValue(InvocationKey.SOAP_MESSAGE_CONTEXT);
      if (msgContext == null)
      {
         return getNext().invoke(mi);
      }

      // Handlers need to be Tx. Therefore we must invoke the handler chain after the TransactionInterceptor.
      // Invoking the ws4ee handlers from an axis handler would be far too early in the invocation path.

      HandlerChain handlerChain = (HandlerChain)msgContext.getProperty(Constants.HANDLER_CHAIN);
      if (handlerChain == null)
         throw new IllegalStateException("Cannot obtain handler chain from msg context");

      // Get the invocation object from the message context
      RPCInvocation invocation = (RPCInvocation)msgContext.getProperty(RPCProvider.RPC_INVOCATION);
      if (invocation == null)
         throw new IllegalStateException("Cannot obtain RPCInvocation from message context");

      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
         RPCInvocation invAfterRequestHandler = (RPCInvocation)msgContext.getProperty(RPCProvider.RPC_INVOCATION);

         // Replace the arguments in the method invocation
         if (invocation.equals(invAfterRequestHandler) == false)
         {
            Object[] args = invAfterRequestHandler.getArgValues();
            if (args.length != mi.getArguments().length)
               throw new IllegalArgumentException("Invalid argument list in RPCInvocation");

            for (int i = 0; i < args.length; i++)
            {
               Class miClass = mi.getArguments()[i].getClass();
               Class rpcClass = args[i].getClass();
               if (miClass.isAssignableFrom(rpcClass) == false)
                  throw new IllegalArgumentException("RPCInvocation argument cannot be assigned: " + miClass.getName() + " != " + rpcClass.getName());

               mi.getArguments()[i] = args[i];
            }
         }
      }
      catch (Exception e)
      {
         log.error("Error processing request handler chain", e);
         throw e;
      }

      Object resObj = null;
      try
      {
         // Call the next interceptor in the chain
         resObj = getNext().invoke(mi);
      }
      catch (Exception e)
      {
         msgContext.setProperty(Constants.LAST_FAULT, e);
         log.error("Error from service endpoint, processing fault handler chain", e);

         // Call the response 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(resObj);

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

         // There is no way we can get a new resObj from the response SOAPEnvelope.
         // Sucker is the response handler that modifies the response value.
         // The RPCInvocation will ignore another call to prepareResponseEnvelope
         // because this could potentially overwrite what the handlers did.
      }
      catch (Exception e)
      {
         log.error("Error processing response handler chain", e);
         throw e;
      }

      return resObj;
   }
}