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

// $Id: ServerEngine.java,v 1.8.2.3 2005/04/22 11:28:31 tdiesler Exp $

import org.jboss.axis.AxisFault;
import org.jboss.axis.Constants;
import org.jboss.axis.EngineConfiguration;
import org.jboss.axis.Handler;
import org.jboss.axis.MessageContext;
import org.jboss.axis.SimpleTargetedChain;
import org.jboss.axis.description.OperationDesc;
import org.jboss.axis.server.AxisServer;
import org.jboss.axis.soap.SOAPConstants;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;

import javax.xml.soap.SOAPException;

/**
 * The ws4ee server engine.
 * <p/>
 * Note, this instance is shared by all deployed web services.
 *
 * @author Thomas.Diesler@jboss.org
 * @since 15-April-2004
 */
public class ServerEngine extends AxisServer
{
   /** @since 4.0.2 */
   static final long serialVersionUID = 5160872757245559530L;
   // provide logging
   private static final Logger log = Logger.getLogger(ServerEngine.class);

   /**
    * Construct an AxisServer with a given configuration.
    */
   public ServerEngine(EngineConfiguration config)
   {
      super(config);
      log.debug("new ServerEngine [config=" + config + "]");
   }

   /**
    * Main routine of the AXIS server. In short we locate the appropriate
    * handler for the desired service and invoke() it.
    */
   public void invoke(final MessageContext msgContext) throws AxisFault
   {
      try
      {
         // Force deserialization
         long t1 = System.currentTimeMillis();
         msgContext.getMessage().getSOAPPart().getEnvelope();
         long t2 = System.currentTimeMillis();
         Logger.getLogger(Constants.TIME_LOG_CATEGORY).debug("Get SOAPEnvelope in " + (t2 - t1) + "ms");

         OperationDesc currOperation = msgContext.getOperation();
         if (currOperation != null && currOperation.isOneWay())
         {
            Thread th = new Thread()
            {
               public void run()
               {
                  try
                  {
                     invokeInternal(msgContext);
                  }
                  catch (AxisFault fault)
                  {
                     log.error("Asyncronous Server error: " + fault.dumpToString());
                  }
               }
            };
            th.start();

            msgContext.setResponseMessage(null);

            log.debug("Returning immediately from one-way operation: " + currOperation.getName());
            return;
         }

         // invoke the service endpoint
         invokeInternal(msgContext);
      }
      catch (AxisFault af)
      {
         log.error("Server error: " + af.dumpToString());
         throw af;
      }
      catch (SOAPException ex)
      {
         log.error("Server error", ex);
         throw AxisFault.makeFault(ex);
      }
      catch (RuntimeException ex)
      {
         log.error("Server error", ex);
         throw ex;
      }
   }

   /**
    * Do the server side handler stuff and invoke the service endpoint
    */
   private void invokeInternal(MessageContext msgContext) throws AxisFault
   {
      log.debug("invoke: " + msgContext);

      long t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0;

      if (!isRunning())
      {
         throw new AxisFault("Server.disabled",
                 Messages.getMessage("serverDisabled00"),
                 null, null);
      }

      String hName = null;
      Handler handler = null;

      // save previous context
      MessageContext previousContext = getCurrentMessageContext();

      try
      {
         // set active context
         setCurrentMessageContext(msgContext);

         // Now we do the 'real' work.  The flow is basically:
         //   Transport Specific Request Handler/Chain
         //   Global Request Handler/Chain
         //   Protocol Specific-Handler(ie. SOAP, XP)
         //     ie. For SOAP Handler:
         //           - Service Specific Request Handler/Chain
         //           - SOAP Semantic Checks
         //           - Service Specific Response Handler/Chain
         //   Global Response Handler/Chain
         //   Transport Specific Response Handler/Chain
         /**************************************************************/

         /* Process the Transport Specific Request Chain */
         /**********************************************/
         hName = msgContext.getTransportName();
         SimpleTargetedChain transportChain = null;

         log.debug("TransportHandler: " + hName);

         // time before transport handler
         t1 = System.currentTimeMillis();

         if (hName != null && (handler = getTransport(hName)) != null)
         {
            if (handler instanceof SimpleTargetedChain)
            {
               transportChain = (SimpleTargetedChain)handler;
               handler = transportChain.getRequestHandler();
               if (handler != null)
                  handler.invoke(msgContext);
            }
         }

         // time after transport handler
         t2 = System.currentTimeMillis();

         /* Process the Global Request Chain */
         /**********************************/
         if ((handler = getGlobalRequest()) != null)
            handler.invoke(msgContext);

         // time after optional parse
         t3 = System.currentTimeMillis();

         /**
          * At this point, the service should have been set by someone
          * (either the originator of the MessageContext, or one of the
          * transport or global Handlers).  If it hasn't been set, we
          * fault.
          */
         handler = msgContext.getService();
         if (handler == null)
         {
            throw new AxisFault("Server.NoService",
                    Messages.getMessage("noService05", msgContext.getTargetService()),
                    null, null);
         }

         // Ensure that if we get SOAP1.2, then reply using SOAP1.2
         if (msgContext.getSOAPConstants() != null)
         {
            SOAPConstants soapConstants = msgContext.getSOAPConstants();
            msgContext.setSOAPConstants(soapConstants);
         }

         handler.invoke(msgContext);

         // time after endpoint invocation
         t4 = System.currentTimeMillis();

         // Process the Global Response Chain
         if ((handler = getGlobalResponse()) != null)
            handler.invoke(msgContext);

         // time after global handlers
         t5 = System.currentTimeMillis();

         // Process the Transport Specific Response Chain
         if (transportChain != null)
         {
            handler = transportChain.getResponseHandler();
            if (handler != null)
               handler.invoke(msgContext);
         }

         // time after transport handlers
         t6 = System.currentTimeMillis();

         if (log.isTraceEnabled())
         {
            String serviceName = msgContext.getTargetService();
            if (msgContext.getOperation() != null)
               serviceName += "." + msgContext.getOperation().getName();

            log.trace(serviceName + " [" +
                    "trIn=" + (t2 - t1) +
                    ",glIn=" + (t3 - t2) +
                    ",invoke=" + (t4 - t3) +
                    ",glOut=" + (t5 - t4) +
                    ",trOut=" + (t6 - t5) +
                    "]");
         }
      }
      catch (AxisFault e)
      {
         throw e;
      }
      catch (Exception e)
      {
         // Should we even bother catching it ?
         throw AxisFault.makeFault(e);

      }
      finally
      {
         // restore previous state
         setCurrentMessageContext(previousContext);
      }
   }
}