Package org.jboss.soa.esb.listeners.message

Source Code of org.jboss.soa.esb.listeners.message.ActionProcessingPipeline

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.soa.esb.listeners.message;

import java.net.URISyntaxException;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.crypto.SealedObject;
import javax.security.auth.Subject;
import javax.xml.validation.Schema;

import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.services.security.SecurityContextPropagator;
import org.jboss.internal.soa.esb.services.security.SecurityContextPropagatorFactory;
import org.jboss.internal.soa.esb.util.XMLHelper;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.actions.ActionLifecycle;
import org.jboss.soa.esb.actions.ActionPipelineProcessor;
import org.jboss.soa.esb.actions.ActionProcessingFaultException;
import org.jboss.soa.esb.actions.BeanConfiguredAction;
import org.jboss.soa.esb.addressing.Call;
import org.jboss.soa.esb.addressing.EPR;
import org.jboss.soa.esb.addressing.MalformedEPRException;
import org.jboss.soa.esb.addressing.eprs.LogicalEPR;
import org.jboss.soa.esb.addressing.util.DefaultFaultTo;
import org.jboss.soa.esb.addressing.util.DefaultReplyTo;
import org.jboss.soa.esb.client.ServiceInvoker;
import org.jboss.soa.esb.common.Environment;
import org.jboss.soa.esb.couriers.Courier;
import org.jboss.soa.esb.couriers.CourierException;
import org.jboss.soa.esb.couriers.CourierFactory;
import org.jboss.soa.esb.couriers.CourierUtil;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.lifecycle.LifecycleResourceException;
import org.jboss.soa.esb.listeners.ListenerTagNames;
import org.jboss.soa.esb.listeners.message.errors.Factory;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.MessagePayloadProxy;
import org.jboss.soa.esb.message.Properties;
import org.jboss.soa.esb.services.persistence.MessageStore;
import org.jboss.soa.esb.services.security.PublicCryptoUtil;
import org.jboss.soa.esb.services.security.SecurityConfig;
import org.jboss.soa.esb.services.security.SecurityConfigUtil;
import org.jboss.soa.esb.services.security.SecurityContext;
import org.jboss.soa.esb.services.security.SecurityService;
import org.jboss.soa.esb.services.security.SecurityServiceException;
import org.jboss.soa.esb.services.security.SecurityServiceFactory;
import org.jboss.soa.esb.services.security.auth.AuthenticationRequest;
import org.jboss.soa.esb.services.security.auth.AuthenticationRequestImpl;
import org.jboss.soa.esb.util.ClassUtil;
import org.xml.sax.SAXException;

/**
* Action Processing Pipeline. <p/> Runs a list of action classes on a message
*
* @author <a
*         href="mailto:schifest@heuristica.com.ar">schifest@heuristica.com.ar</a>
* @author kevin
* @author <a href="mailto:dbevenius@jboss.com">Daniel Bevenius</a>
* @since Version 4.0
*/
public class ActionProcessingPipeline
{
  /**
   * The logger instance.
   */
  private final static Logger LOGGER = Logger .getLogger(ActionProcessingPipeline.class);

  /**
   * The processors.
   */
  private final ActionPipelineProcessor[] processors;

  /**
   * The active flag.
   */
  private final AtomicBoolean active = new AtomicBoolean(false);

  /**
   * The request XSD.
   */
  private final Schema requestSchema ;
  /**
   * The response XSD.
   */
  private final Schema responseSchema ;
  /**
   * The request payload proxy.
   */
  private MessagePayloadProxy requestPayloadProxy ;
  /**
   * The response payload proxy.
   */
  private MessagePayloadProxy responsePayloadProxy ;

  /**
   *
   */
  private final ServiceMessageCounter serviceMessageCounter;

  /**
   * The transactional flag.
   */
  private boolean transactional ;

  /**
   * The flag indicating an action pipeline for a one way MEP.
   */
  private final boolean oneWay ;

    /**
     * The flag indicating whether we are using implicit or explicit processing.
     */
  private final boolean defaultProcessing ;

  /**
   * ESB Service Security configuration information.
   */
  private SecurityConfig securityConf;

  /**
   * The {@link SecurityContextPropagator} is use.
   * This can be configured either globally in jbossesb-properites.xml or
   * per-service in jboss-esb.xml.
   */
    private SecurityContextPropagator securityContextPropagator;

    private String serviceName;

  /**
   * public constructor
   *
   * @param config The pipeline configuration.
   */
  public ActionProcessingPipeline(final ConfigTree config) throws ConfigurationException
  {
    if (config == null)
    {
      throw new IllegalArgumentException( "Configuration needed for action classes");
    }

    final String mep = config.getAttribute(ListenerTagNames.MEP_ATTRIBUTE_TAG) ;
    final boolean oneWay ;
    final boolean defaultProcessing ;
    if (mep == null)
    {
        oneWay = false ;
        defaultProcessing = true ;
    }
    else if (ListenerTagNames.MEP_ONE_WAY.equals(mep))
    {
        oneWay = true ;
        defaultProcessing = false ;
    }
    else if (ListenerTagNames.MEP_REQUEST_RESPONSE.equals(mep))
    {
        oneWay = false ;
        defaultProcessing = false ;
    }
    else
    {
        throw new ConfigurationException("Unrecognised action MEP: " + mep) ;
    }

    final boolean validate = config.getBooleanAttribute(ListenerTagNames.VALIDATE_ATTRIBUTE_TAG, false) ;
    if (validate)
    {
      final String inXsd = config.getAttribute(ListenerTagNames.IN_XSD_ATTRIBUTE_TAG) ;
      try
      {
        requestSchema = (inXsd == null ? null : XMLHelper.getSchema(inXsd)) ;
      }
      catch (final SAXException saxe)
      {
        throw new ConfigurationException("Failed to parse the request schema: " + inXsd, saxe) ;
      }
      final String outXsd = config.getAttribute(ListenerTagNames.OUT_XSD_ATTRIBUTE_TAG) ;
      try
      {
        responseSchema = (outXsd == null ? null : XMLHelper.getSchema(outXsd));
      }
      catch (final SAXException saxe)
      {
        throw new ConfigurationException("Failed to parse the response schema: " + outXsd, saxe) ;
      }
      requestPayloadProxy = new MessagePayloadProxy(config.getAttribute(ListenerTagNames.REQUEST_LOCATION_TAG), null) ;
      responsePayloadProxy = new MessagePayloadProxy(config.getAttribute(ListenerTagNames.RESPONSE_LOCATION_TAG), null) ;
    }
    else
    {
      requestSchema = null ;
      responseSchema = null ;
    }
    if (LOGGER.isDebugEnabled())
    {
        LOGGER.debug("Using mep: " + mep + ", oneWay: " + oneWay + ", defaultProcessing: " + defaultProcessing) ;
    }
                this.oneWay = oneWay ;
                this.defaultProcessing = defaultProcessing ;

    final ConfigTree[] actionList = config
        .getChildren(ListenerTagNames.ACTION_ELEMENT_TAG);

    if ((actionList == null) || (actionList.length == 0))
    {
      throw new ConfigurationException("No actions in list");
    }

    final ArrayList<ActionPipelineProcessor> processorList = new ArrayList<ActionPipelineProcessor>();

    try
    {
      serviceMessageCounter = ServiceMessageCounterLifecycleResource.getServiceMessageCounter(config);
    }
    catch (final LifecycleResourceException lre)
    {
      throw new ConfigurationException("Failed to obtain the service message counter", lre);
    }

    for (final ConfigTree actionConfig : actionList)
    {
      final String actionClassTag = actionConfig
           .getAttribute(ListenerTagNames.ACTION_CLASS_TAG);
      if (LOGGER.isDebugEnabled())
      {
        LOGGER.debug("Registering action class " + actionClassTag);
      }
      final Class<?> actionClass;
      try
      {
        actionClass = ClassUtil.forName(actionClassTag, getClass());
      }
      catch (final ClassNotFoundException cnfe)
      {
        throw new ConfigurationException("Could not load action class "
            + actionClassTag);
      }

      final ActionPipelineProcessor processor;
      if (BeanConfiguredAction.class.isAssignableFrom(actionClass))
      {
        if (LOGGER.isDebugEnabled())
        {
          LOGGER.debug("Using bean configured action processor for "
              + actionClassTag);
        }
        processor = new BeanConfigActionProcessor(actionConfig,
            actionClass);
      }
      else if (ActionPipelineProcessor.class
          .isAssignableFrom(actionClass))
      {
        final ActionPipelineProcessor currentProcessor = (ActionPipelineProcessor) ActionProcessorMethodInfo
            .getActionClassInstance(actionConfig, actionClass);
        if (ActionProcessorMethodInfo.checkOverridden(actionConfig))
        {
          if (LOGGER.isDebugEnabled())
          {
            LOGGER
                .debug("Using overridden action pipeline processor for "
                    + actionClassTag);
          }
          processor = new OverriddenActionPipelineProcessor(
              actionConfig, currentProcessor);
        }
        else
        {
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("Using normal action pipeline processor for " + actionClassTag);
          }
          processor = currentProcessor;
        }
      }
      else if (ActionLifecycle.class.isAssignableFrom(actionClass))
      {
        if (LOGGER.isDebugEnabled())
        {
          LOGGER.debug("Using overridden action lifecycle processor for " + actionClassTag);
        }
        final ActionLifecycle currentLifecycle = (ActionLifecycle) ActionProcessorMethodInfo
            .getActionClassInstance(actionConfig, actionClass);
        processor = new OverriddenActionLifecycleProcessor(
            actionConfig, currentLifecycle);
      }
      else if (BeanContainerAction.isAnnotatedActionClass(actionClass))
      {
        if (LOGGER.isDebugEnabled())
        {
          LOGGER.debug("Using BeanContainerAction for Annotated Action processor for " + actionClassTag);
        }
        try {
          processor = new BeanContainerAction(actionClass.newInstance(), actionConfig);         
        } catch (InstantiationException e) {
          throw new ConfigurationException("Failed to create an instance of Annotated ESB Action class '" + actionClass.getName() + "'.  Class must contain a default public constructor.", e);
        } catch (IllegalAccessException e) {
          throw new ConfigurationException("Failed to create an instance of Annotated ESB Action class '" + actionClass.getName() + "'.  Class must contain a default public constructor.", e);
        }
      }
      else
      {
        LOGGER.warn("Action class " + actionClassTag + " does not: a) implement the ActionLifecycle interface, or b) have any public method annotated with the @ProcessMethod annotation.");
        if (LOGGER.isDebugEnabled())
        {
          LOGGER.debug("Using overridden actions processor for " + actionClassTag);
        }
        processor = new OverriddenActionProcessor(actionConfig, actionClass);
      }
      processorList.add(processor);
    }
    processors = processorList.toArray(new ActionPipelineProcessor[processorList.size()]);

    ConfigTree[] securityConfigs = config.getChildren( ListenerTagNames.SECURITY_TAG );
    String securityPropagatorClass = null;
    if (securityConfigs.length > 0)
    {
      securityConf = SecurityConfigUtil.createSecurityConfig(securityConfigs[0]);
      //   Check if a security context propagator was specified in the security element.
      securityPropagatorClass = securityConf.getProperties().get(Environment.SECURITY_SERVICE_CONTEXT_PROPAGATOR_CLASS);
    }
        serviceName = config.getAttribute(ListenerTagNames.SERVICE_NAME_TAG);

    try
        {
            securityContextPropagator = securityPropagatorClass != null ?
                    SecurityContextPropagatorFactory.create(securityPropagatorClass):
                    SecurityContextPropagatorFactory.createFromConfig();
        }
    catch (final SecurityServiceException e)
        {
        final String errorMsg;
        if (securityPropagatorClass != null )
        {
           errorMsg = "Could not create an instance of class '" + securityPropagatorClass + "' which was configured for service '" +
                       serviceName + "'. Please check the value of '" + Environment.SECURITY_SERVICE_CONTEXT_PROPAGATOR_CLASS + "'" +
                       " which is a property element of the security element declared in jboss-esb.xml.";
        }
        else
        {
           errorMsg = "Could not create an instance of class the security context propagator configured in jbossesb-properties.xml" +
                       ".Please check the value of '" + Environment.SECURITY_SERVICE_CONTEXT_PROPAGATOR_CLASS + "' in jbossesb-properties.xml";
        }
        throw new ConfigurationException(errorMsg, e);
        }

    if (LOGGER.isDebugEnabled())
        {
            if (securityContextPropagator != null)
            {
                LOGGER.debug("SecurityContextPropagator in use for service '" + serviceName + "' is '" + securityContextPropagator.getClass().getName() + "'" );
            }
        }
  }

  /**
   * Handle the initialisation of the pipeline
   *
   * @throws ConfigurationException
   *             For errors during initialisation.
   */
  public void initialise() throws ConfigurationException
  {
    final int numLifecycles = processors.length;
    for (int count = 0; count < numLifecycles; count++)
    {
      final ActionLifecycle lifecycle = processors[count];
      try
      {
        lifecycle.initialise();
      }
      catch (final Exception ex)
      {
        handleDestroy(count - 1);
        throw new ConfigurationException(
            "Unexpected exception during lifecycle initialisation",
            ex);
      }
    }
    active.set(true);

  }

  /**
   * Handle the destruction of the pipeline
   */
  public void destroy()
  {
    active.set(false);
    handleDestroy(processors.length - 1);
  }

  /**
   * Process the specified message.
   *
   * @param message
   *            The current message.
   * @return true if the processing was successful, false otherwise.
   */
  public boolean process(final Message message)
  {
    long start = System.nanoTime();
    serviceMessageCounter.incrementTotalCount();

    if (active.get())
    {
      if (LOGGER.isDebugEnabled())
      {
        LOGGER.debug("pipeline process for message: "+message.getHeader());
      }

      return processPipeline(message, getSecurityContext(message));
    }
    else
    {
        final Call callDetails = createCallDetails(message);
      if (LOGGER.isDebugEnabled())
      {
        LOGGER.debug("pipeline process disabled for message: "+message.getHeader());
      }

      faultTo(callDetails, Factory.createErrorMessage(Factory.NOT_ENABLED, message, null));
      long procTime = System.nanoTime() - start;
          MessageCounterStatistics.getMessageCounterStatistics().update(new MessageStatusBean(procTime, message,
              MessageStatusBean.MESSAGE_FAILED));
          
      return false;
    }
  }

  /**
   * Processes the action pipeline.
   * <p/>
   * If the service has been enable for security authentication, it has the security element
   * in the configuration xml that is, this method will authenitcate the caller.
   * If security has not been enabled this method will not perform any authentication but
   * it will pass through a pre-existing SecurityContext.
   * <p>
   *
   * <h1>SecurityContext</h1>
   * After the caller has been successfully authenticated a SecurityContext will be created.
   * This context is valid on the current ESB node(VM) only.
   * When a SecurityContext is created a timeout is specified. This value can either be the globally
   * value specified in jbossesb-properties.xml or it can be overridden per service by specifiying the
   * same property in the security element in jboss-esb.xml:
   * The value is specified in milliseconds.
   * <pre>
   *     <security module="someModuleName">
   *         <property name="org.jboss.soa.esb.services.security.contextTimeout" value="50000"/>
   *     </security>
   * </pre>
   *
   * <h1>AuthenticationRequest</h1>
   * If a call is routed to a different ESB node then that node will re-authenticate the caller.
   * The node will descrypt the authentication request passed with the Message object
   * and try to authenticate the caller.
   *
   * <h1>SecurityContext Propagation</h1>
   * Regardless of whether the service has been configured with security or not, the security
   * context information might need to be propagated using the configured {@link SecurityContextPropagator}.
   *
   * @param message The ESB message object.
   * @param sealedSecurityContext The sealed SecurityContext which will exist if the caller has already been authenticated.
   * @return true If the action pipeline was processed successfully.
   */
  private boolean processPipeline(final Message message, final SealedObject sealedSecurityContext)
  {
    SecurityContext securityContext = null;

    if (sealedSecurityContext != null )
        {
        // Store the security context. Will be re-attached to outgoing messages regardless whether the service is secured or not.
            SecurityContext.setSecurityContext(sealedSecurityContext);
        }

    AuthenticationRequest authRequest;
    try
    {
        /*
         * Get the authentication reqeust if one exists. Note that this is needed even if
         * the current service does not require authentication. A service later down the line might
         * need to access this information, it might call an EJB that is secured for example, and
         * this way pass it through.
         */
        authRequest = getAutenticationRequest(message);

        if (isServiceSecured())
        {
            try
                {
                // Try to decrypt the security context.
                    securityContext = SecurityContext.decryptContext(sealedSecurityContext);
                }
                catch (final SecurityServiceException ignored)
                {
                    LOGGER.info("Could not decrypt the security context in Service '" + serviceName + "'. The call might have come from a different VM. Will re-authenticate if security is enabled for this service.", ignored);
                    securityContext = null;
                }

                if (LOGGER.isDebugEnabled())
                {
                    if (securityContext != null)
                    {
                        /*
                         * Might be interesting to know if a security context is often invalid as this
                         * will cause re-authentication. This might be avoidable by setting a longer
                         * timeout by overriding the default timeout(jbossesb-properties.xml) and specifying
                         * one on the security element in jboss-esb.xml.
                         */
                    LOGGER.debug(securityContext);
                    }
                }

          final SecurityService securityService = SecurityServiceFactory.getSecurityService();

          final String moduleName = securityConf.getModuleName() ;
            if (securityContext == null || !securityContext.isValid() || ((moduleName != null) && !moduleName.equals(securityContext.getDomain())))
            {
                if (authRequest == null)
                {
                    throw new SecurityServiceException("Service '" + serviceName + "' has been configured for security but no AuthenticationRequest could be located in the Message Context. Cannot authenticate without an AuthenticationRequest.");
                }

                 // No existing security context exist or it had expired. Create a new one to drive the autentication.
                securityContext = new SecurityContext(new Subject(), getSecurityContextTimeout(securityConf), moduleName);

                // Authenticate the caller
              securityService.authenticate(securityConf, securityContext, authRequest);

                // Store the encrypted security context. Will be re-attached to outgoing messages.
              SecurityContext.setSecurityContext(SecurityContext.encryptContext(securityContext));
            }

            // Check that the caller is a member of atleast one of the declared roles.
            if (!securityService.checkRolesAllowed(securityConf.getRolesAllowed(), securityContext))
            {
              throw new SecurityServiceException("Caller did not belong to any of the rolesAllowed " + securityConf.getRolesAllowed());
            }
        }
    }
    catch (final SecurityServiceException e)
    {
      LOGGER.error( "SecurityService exception : ", e);
      faultTo(createCallDetails(message), Factory.createErrorMessage(Factory.UNEXPECTED_ERROR, message, e));
      return false;
    }
    catch (final ConfigurationException e)
    {
      LOGGER.error( "SecurityService exception : ", e);
      faultTo(createCallDetails(message), Factory.createErrorMessage(Factory.UNEXPECTED_ERROR, message, e));
      return false;
    }
    finally
    {
        // Always remove the security context.
        message.getContext().removeContext(SecurityService.CONTEXT);
        message.getContext().removeContext(SecurityService.AUTH_REQUEST);
    }

    if (securityContext != null)
    {
        try
        {
            // Need to propagate the security context regardless if security was enabled for this service or not.
            propagateSecurityContext(message, securityContext, authRequest);

             return (Boolean) Subject.doAsPrivileged(securityContext.getSubject(), getPrivilegedAction(message), null);
        }
        catch (final SecurityServiceException e)
        {
          LOGGER.error( "SecurityService exception : ", e);
          faultTo(createCallDetails(message), Factory.createErrorMessage(Factory.UNEXPECTED_ERROR, message, e));
          return false;
        }
        finally
        {
            popSecurityContext(securityContext);
        }
    }
    else
    {
        return processPipeline(message);
    }
  }

  private boolean processPipeline(final Message message)
  {
    final long start = System.nanoTime();
    final Call callDetails = createCallDetails(message);

      boolean result = false ;
      String validationFailure = null ;
      if (requestSchema != null)
      {
        try
        {
          final Object input = requestPayloadProxy.getPayload(message) ;
          if ((input == null) || !XMLHelper.validate(requestSchema, input.toString()))
          {
            validationFailure = "Request validation failure: " + input ;
          }
        }
        catch (final MessageDeliverException mde)
        {
          validationFailure = mde.getMessage() ;
        }
      }
      if (validationFailure == null)
      {
        final int numProcessors = processors.length;
        final Message[] messages = new Message[numProcessors];

        Message currentMessage = message;

        for (int count = 0; count < numProcessors; count++)
        {
          final ActionPipelineProcessor processor = processors[count];
          messages[count] = currentMessage;

          final long actionStart = System.nanoTime();
          try
          {
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("executing processor " + count+ " "+processor);
            }

            currentMessage = processor.process(currentMessage);
          }
          catch (final Exception ex)
          {
            final long procTime = System.nanoTime() - actionStart;
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("Unexpected exception caught while processing the action pipeline",ex);
            }

            notifyException(count, ex, messages);

            /*
             * Is this an application specific error? If so, try to return
             * the error message to the identified recipient.
             */

            final boolean throwRuntime = transactional && (ex instanceof RuntimeException) ;

            if (ex instanceof ActionProcessingFaultException)
            {
              ActionProcessingFaultException fault = (ActionProcessingFaultException) ex;

              if (fault.getFaultMessage() == null)
              {
                faultTo(callDetails, Factory.createErrorMessage(Factory.PROCESSING_ERROR, message, ex));
              }
              else
                faultTo(callDetails, fault.getFaultMessage());
            }
            else if (!throwRuntime)
            {
              faultTo(callDetails, Factory.createErrorMessage(Factory.UNEXPECTED_ERROR, message, ex));
            }

            final long totalProcTime = System.nanoTime() - start;
            serviceMessageCounter.update(new ActionStatusBean(procTime, count, message,
              ActionStatusBean.ACTION_FAILED, serviceMessageCounter.getObjectName().toString()));
            MessageCounterStatistics.getMessageCounterStatistics().update(new MessageStatusBean(totalProcTime, message,
              MessageStatusBean.MESSAGE_FAILED));

            if (throwRuntime)
            {
              throw (RuntimeException)ex ;
            }
            return false;
          }
          final long procTime = System.nanoTime() - actionStart;
          serviceMessageCounter.update(new ActionStatusBean(procTime, count, message,
            ActionStatusBean.ACTION_SENT, serviceMessageCounter.getObjectName().toString()));

          if (currentMessage == null)
          {
            break;
          }
        }

        // Reply...
        if (!oneWay)
        {
          if (currentMessage != null)
          {
            if (responseSchema != null)
            {
              try
              {
                final Object output = responsePayloadProxy.getPayload(message) ;
                if ((output == null) || !XMLHelper.validate(responseSchema, output.toString()))
                {
                  validationFailure = "Response validation failure: " + output ;
                }
              }
              catch (final MessageDeliverException mde)
              {
                validationFailure = mde.getMessage() ;
              }
            }
            if (validationFailure == null)
            {
              replyTo(callDetails, currentMessage);
            }
          }
          else if (!defaultProcessing)
          {
            LOGGER.warn("No response message for RequestResponse mep! " + callDetails);
          }
        }

        if (validationFailure == null)
        {
          notifySuccess(messages);
          long procTime = System.nanoTime() - start;
          MessageCounterStatistics.getMessageCounterStatistics().update(new MessageStatusBean(procTime, message,
            MessageStatusBean.MESSAGE_SENT));
          result = true;
        }
      }

      if (validationFailure != null)
      {
        final MessageValidationException mve = new MessageValidationException(validationFailure) ;
        faultTo(callDetails, Factory.createErrorMessage(Factory.VALIDATION_FAILURE, message, mve));
        long procTime = System.nanoTime() - start;
        MessageCounterStatistics.getMessageCounterStatistics().update(new MessageStatusBean(procTime, message,
          MessageStatusBean.MESSAGE_FAILED));
      }
      return result ;
  }

  /**
   * Set the transactional flag for this pipeline.
   * @param transactional true if running within a transaction, false otherwise.
   */
  public void setTransactional(final boolean transactional)
  {
      this.transactional = transactional ;
  }

        /**
         * Get the transactional flag for this pipeline.
         * @return true if running within a transaction, false otherwise.
         */
  public boolean isTransactional()
  {
      return transactional ;
  }

  /**
   * Send the reply.
   *
   * @param callDetails
   *            the call details for the original request.
   * @param message
   *            the message.
   */

  private void replyTo(final Call callDetails, final Message message)
  {
    if (!DefaultReplyTo.initialiseReply(message, callDetails))
    {
        if (defaultProcessing)
        {
      LOGGER.warn("No reply to address defined for reply message! " + callDetails);
      sendToDLQ(callDetails, message, MessageType.reply) ;
        }
    }
    else
    {
      final EPR replyToEPR = message.getHeader().getCall().getTo() ;
      messageTo(replyToEPR, message, MessageType.reply);
    }
  }

  /**
   * Send the fault message to the EPR.
   *
   * @param callDetails
   *            the call details for the original request.
   * @param faultToAddress
   *            the EPR to target if one is not set in the message.
   * @param message
   *            the message.
   */

  private void faultTo(final Call callDetails, final Message message)
  {
    if (!DefaultFaultTo.initialiseReply(message, callDetails, oneWay))
    {
        if (defaultProcessing || oneWay)
        {
      LOGGER.warn("No fault address defined for fault message! " + callDetails);
      sendToDLQ(callDetails, message, MessageType.fault) ;
        }
    }
    else
    {
      final EPR faultToEPR = message.getHeader().getCall().getTo() ;
      messageTo(faultToEPR, message, MessageType.fault);
    }
  }

  /**
   * Sent the message to the DLQ service.
   * @param callDetails The original call details.
   * @param message The response message.
   * @param messageType The response type.
   */
  private void sendToDLQ(final Call callDetails, final Message message,
    final MessageType messageType)
  {
    final Properties properties = message.getProperties() ;
    properties.setProperty(MessageStore.CLASSIFICATION, MessageStore.CLASSIFICATION_DLQ);
    properties.setProperty(ActionProcessingConstants.PROPERTY_FAILURE_CALL_DETAILS, callDetails.toString()) ;
    properties.setProperty(ActionProcessingConstants.PROPERTY_FAILURE_RESPONSE_TYPE, messageType.name()) ;

    try
    {
      final ServiceInvoker serviceInvoker = new ServiceInvoker(ServiceInvoker.dlqService) ;

      serviceInvoker.deliverAsync(message) ;
    }
    catch (final MessageDeliverException mde)
    {
      LOGGER.warn("Failed to send response failure to DLQ service") ;
      LOGGER.debug("Failed to send response failure to DLQ service", mde) ;
    }
  }

    private static enum MessageType {
        reply,
        fault,
    }

    private void messageTo(EPR epr, Message message, MessageType messageType) {
        if(epr instanceof LogicalEPR) {
            try {
                ServiceInvoker invoker = ((LogicalEPR)epr).getServiceInvoker();
                invoker.deliverAsync(message);
            } catch (MessageDeliverException e) {
                LOGGER.error("Failed to send " + messageType + " to address " + epr
                        + " for message "+message.getHeader(), e);
            }
        } else {
            Courier courier = null;

            try {
                courier = CourierFactory.getCourier(epr);
                courier.deliver(message);
            } catch (final CourierException e) {
                LOGGER.error("Failed to send " + messageType + " to address " + epr
                        + " for message " + message.getHeader(), e);
            } catch (final MalformedEPRException e) {
                LOGGER.error("Failed to send " + messageType + " to address " + epr
                        + " for message " + message.getHeader(), e);
            } catch (final Throwable e) {
                LOGGER.error("Failed to send " + messageType + " to address " + epr
                        + " for message " + message.getHeader(), e);
            } finally {
                if (courier != null) {
                    CourierUtil.cleanCourier(courier);
                }
            }
        }
    }

  /**
   * Handle the destruction of the pipeline from the specified position.
   *
   * @param initialPosition
   *            The initial position to begin destruction.
   */
  private void handleDestroy(final int initialPosition)
  {
    for (int count = initialPosition; count >= 0; count--)
    {
      final ActionLifecycle lifecycle = processors[count];
      try
      {
        lifecycle.destroy();
      }
      catch (final Exception ex)
      {
        LOGGER
            .warn(
                "Unexpected exception during lifecycle destruction",
                ex);
      }
    }
  }

  /**
   * Notify the processors of an error during processing.
   *
   * @param initialPosition
   *            The position of the first processor to notify.
   * @param ex
   *            The exception which caused the failure.
   * @param messages
   *            The messages associated with successful processors.
   */
  private void notifyException(final int initialPosition, final Exception ex,
      final Message[] messages)
  {
    for (int count = initialPosition; count >= 0; count--)
    {
      final ActionPipelineProcessor processor = processors[count];
      try
      {
        processor.processException(messages[count], ex);
      }
      catch (final Exception ex2)
      {
        LOGGER
            .warn(
                "Unexpected exception notifying processor of pipeline failure",
                ex2);
      }
    }
  }

  /**
   * Notify the processors of a successful pipeline process.
   *
   * @param messages
   *            The messages associated with the processors.
   */
  private void notifySuccess(final Message[] messages)
  {
    for (int count = messages.length - 1; count >= 0; count--)
    {
      final Message message = messages[count];
      if (message != null)
      {
        final ActionPipelineProcessor processor = processors[count];
        try
        {
          processor.processSuccess(messages[count]);
        }
        catch (final Exception ex)
        {
          LOGGER
              .warn(
                  "Unexpected exception notifying processor of pipeline success",
                  ex);
        }
      }
    }
  }

    private boolean isServiceSecured()
    {
        return securityConf != null;
    }

    /**
     * Retrieves the authentication reqeust from the Message context using
     * {@link SecurityService#AUTH_REQUEST} as the key.
     * <p/>
     * This location may contain an encrypted AuthenticationRequest and if one
     * exists it will be decryped and return. If one does not exist this
     * method will return null.
     *
     * @param message The ESB Message object.
     * @return {@link AuthenticationRequest} The decrypted AuthenticationRequest or null if one did not exist.
     *
     * @throws SecurityServiceException If a problem occurs during decryption.
     */
    private AuthenticationRequest getAutenticationRequest(final Message message) throws SecurityServiceException
    {
        final byte[] encryptedAuthRequest = (byte[]) message.getContext().getContext(SecurityService.AUTH_REQUEST);
        if (encryptedAuthRequest != null)
        {
        // store the encrypted auth request. Will be re-attached to outgoing messages.
            AuthenticationRequestImpl.setEncryptedAuthRequest(encryptedAuthRequest);

            return (AuthenticationRequest) PublicCryptoUtil.INSTANCE.decrypt(encryptedAuthRequest);
        }
        return null;
    }

    private SealedObject getSecurityContext(final Message message)
    {
        return (SealedObject) message.getContext().getContext(SecurityService.CONTEXT);
    }

    private PrivilegedAction<Boolean> getPrivilegedAction(final Message message)
    {
        // the work to be performed in the context of the authenticated caller
        return new PrivilegedAction<Boolean>()
        {
            public Boolean run()
            {
                return processPipeline(message);
            }
        };
    }

    @SuppressWarnings("deprecation")
    private Call createCallDetails(final Message message)
    {
        Call callDetails;
        try
        {
            callDetails = new Call(message.getHeader().getCall());
        }
        catch (final URISyntaxException e)
        {
            LOGGER.error("Caught an URISyntaxException while calling Call's copy constructor. Will revert to using the old way using the copy method.", e);
            callDetails = new Call();
        callDetails.copy(message.getHeader().getCall()) ;
        }
        return callDetails;
    }

    /**
     * Checks if the security config has a property named 'org.jboss.soa.esb.services.security.contextTimeout'
     * specified. This will in that case be used as the SecurityContext timeout for this pipeline.
     * If not specified the value will fallback to the value specified in jbossesb-properties.xml
     *
     * @param securityConfig The security configuration for this pipeline. This maps to the security element in jboss-esb.xml
     * @return long Either the timeout value specified in the security element in jboss-esb.xml otherwise the value in jbossesb-properties.xml
     * @throws SecurityServiceException If the value specified in jbossesb-properties.xml cannot be parsed. If the value in jboss-esb.xml cannot be parsed, only a warning will be issued.
     */
    long getSecurityContextTimeout(final SecurityConfig securityConfig) throws SecurityServiceException
    {
        String timeoutStr = securityConfig.getProperties().get(Environment.SECURITY_SERVICE_CONTEXT_TIMEOUT);
        if (timeoutStr != null)
        {
            try
            {
                return Long.parseLong(timeoutStr);
            }
            catch (final NumberFormatException ignore)
            {
                LOGGER.warn("Could not parse '" + timeoutStr +"' to a long. Please make sure the the value of the property '" + Environment.SECURITY_SERVICE_CONTEXT_TIMEOUT + "' in jbossesb-xml is a valid long(ms)");
               // fallback to global configuration.
            }
        }
        return SecurityContext.getConfigurationTimeout();
    }

    /**
     * Propagates the security context by delegating to the current {@link SecurityContextPropagator}.
     * This method returns silently if a SecurityContextPropagator has not been configured to avoid
     * the overhead of decrypting the AuthenticationRequest (is needed).
     *
     * @param message The ESB message object.
     * @param context The SecurityContext.
     * @param authRequest The {@link AuthenticationRequest}.
     * @throws SecurityServiceException
     */
    private void propagateSecurityContext(final Message message, final SecurityContext context, final AuthenticationRequest authRequest) throws SecurityServiceException
    {
        if (securityContextPropagator == null)
        {
            // No need to do anything if a security context propagator was not configured.
            return;
        }

        final AuthenticationRequest request;
        if (authRequest == null)
        {
            final byte[] encryptedAuthRequest = (byte[]) message.getContext().getContext(SecurityService.AUTH_REQUEST);
            if (encryptedAuthRequest == null)
            {
               // there might not be a authentication reqeust. Just return.
               return;
            }
            request = (AuthenticationRequest) PublicCryptoUtil.INSTANCE.decrypt(encryptedAuthRequest);
        }
        else
        {
            // use the passed in authentication request.
            request = authRequest;
        }

        securityContextPropagator.pushSecurityContext(context, request.getCredentials(), securityConf);
    }

    private void popSecurityContext(final SecurityContext securityContext)
    {
        if (securityContextPropagator != null)
        {
            securityContextPropagator.popSecurityContext(securityContext, securityConf);
        }
    }
}
TOP

Related Classes of org.jboss.soa.esb.listeners.message.ActionProcessingPipeline

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.