Package org.openeai.implementations.gateways.transroutergateway

Source Code of org.openeai.implementations.gateways.transroutergateway.TransRouterCommand

/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/source/org/openeai/implementations/gateways/transroutergateway/TransRouterCommand.java,v $
$Revision: 1.13 $
*******************************************************************************/

/**********************************************************************
This file is part of the OpenEAI sample, reference implementation,
and deployment management suite created by Tod Jackson
(tod@openeai.org) and Steve Wheat (steve@openeai.org) at
the University of Illinois Urbana-Champaign.

Copyright (C) 2002 The OpenEAI Software Foundation

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

For specific licensing details and examples of how this software
can be used to implement integrations for your enterprise, visit
http://www.OpenEai.org/licensing.
*/

package org.openeai.implementations.gateways.transroutergateway;

import javax.jms.*;

import java.util.*;
import java.io.*;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.output.XMLOutputter;
import org.apache.log4j.*;

import org.openeai.jms.producer.*;
import org.openeai.jms.consumer.*;
import org.openeai.jms.consumer.commands.*;
import org.openeai.layouts.*;
import org.openeai.config.*;

import org.openeai.moa.*;
//import org.openeai.moa.jmsobjects.*;
import org.openeai.moa.objects.resources.*;

import org.openeai.xml.*;

/**
* OpenEAI Reference Implementation Enterprise Router.
* <P>
* The gateway consumes all synchronization messages published by authoritative
* systems and routes those messages to all interested end-points by executing
* this command.
* <P>
* <B>These are the configuration parameters associated to this command.  These are
* specified the Command's Configuration element in the deployment document associated
* to the gateway.</B>
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Property Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>ProducerConfigs</TD>
* <TD>yes</TD>
* <TD>This is the Sync-Error-Sync publisher that the Router will use when/if
* there are any errors while processing a message it consumes.  It should be
* configured accordingly.  All SyncCommand implementations require a
* PubSubProducer <B>that must be named</B> 'SyncErrorPublisher' that they use to publish Sync-Error-Sync
* message if errors occur when they're processing the message.  This is useful
* for SyncCommand implementations in particular because they're generally ran
* unattended.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>PropertyConfigs</TD>
* <TD>yes</TD>
* <TD>These are the properties that are used by the Sync-Error-Sync publisher
* they are required by all SyncCommand implementations.  The name of the PropertyConfig
* <B>must be</B> 'SyncErrorSyncProperties' and they must include the 'SyncErrorSyncPrimedDocumentUri'
* property that points to a Sync-Error-Sync primed document.</TD>
* </TR>
* </TABLE>
* <P>
* <B>The following configuration paramaters associated to the Enterprise Router command
* establish resources that will be used to route the messages consumed
* by the gateway to the appropriate end-points.</B>
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Property Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>MessagingComponents</TD>
* <TD>yes</TD>
* <TD>This is a container Element that provides a mechanism for specifying which
* MessageGateways will be routed to.  It simply contains one or more MessageGateway Elements
* which are used by this command to establish AppConfig objects associated to each end-point. 
* The AppConfig objects that get established will contain all the resources needed to
* route the messages to the end-points appropriately.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>MessageGateway</TD>
* <TD>yes</TD>
* <TD>This Element is used to specify the name of the target gateway that will be routed
* to.  It is also a container for the Configuration Element which is implemented by the AppConfig
* Java object and contains all initialized resources required to route the messages to the
* end points.  The "id" attribute is used to specify the name of the end point.  The
* Description element is used to provide a description of the end point.  There should be a
* MessageGateway Element specified for each end-point that should be routed to.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>Configuration</TD>
* <TD>yes</TD>
* <TD>This Element is implmemented by the AppConfig java object.  This is the same configuration
* Element used by all other messaging components that use an AppConfig.  This will contain the
* resources required to route to end points.  Those resources include:
* <ul>
* <li>ProducerConfigs - PubSubProducers that are connected to the end-point's destination (Topic). 
* This producer will be used to route the message.  Additional producers could be specified here
* if needed.  Additional producers could be needed by the RoutingCriteria class that gets executed
* to perform "content based" routing.  If needed, they would be specified and configured here.
* <li>MessageObjectConfigs - This would be a list of all Message Objects that the end point is interested in. 
* The router will retrieve the messageObject (e.g. - BasicPerson, BasicEmployee etc.)
* and messageRelease (e.g. - 1.0, 1.1 etc.) from the message consumed by the gateway
* and use that information to build a message object name that looks like this "BasicPerson.v1_0".
* If the end point's configuration contains a message object by that name,
* the router may route the message to that end point depending on whether or not the end-point
* has an Routing Criteria (content based routing rules) associated to it.
* <li>PropertyConfigs - These are a set of properties associated to the end point.  There
* <B>must be</B> one PropertyConfig object named 'RoutingProperties' that this command
* uses to make decisions about how/if it should route to the end point.  The Properties that
* may exist in this PropertyConfig element are:
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Property Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>routeToTarget (true|false)</TD>
* <TD>no (default=true)</TD>
* <TD>This property is used to determine if the command should route to the end-point
* at all.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>translateToTargetValues (true|false)</TD>
* <TD>no (default=false)</TD>
* <TD>If true, values in the message will be 'reverse' translated to application
* specific values for the target being routed to.  If false, the values in the
* message will be used as they were passed in (i.e. - no reverse translation).</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>provideTargetAppName (true|false)</TD>
* <TD>no (default=true)</TD>
* <TD>If true, the TargetInfo Element of the message routed by this command
* will include the name of the application that published the message.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>provideSourceControlArea (true|false)</TD>
* <TD>no (default=false)</TD>
* <TD>If true, the message that the command routes to the end-point will include
* the ControlAreaSync Element from the original message published by the authoritative
* source.  This can be useful if the end-point wants to know the originating information
* such as the time stamp when the message was originally published.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>dumpOutput (true|false)</TD>
* <TD>no (default=false)</TD>
* <TD>If true, the router will dump the contents of the message it's routing prior to
* routing it.  All commands have the ability dump the message consumed by the gateway. 
* However, this is a little different in that it's actually going to dump the message
* that results from the routing process prior to routing it.  This is generally something
* that should only be turned on during development/testing or when additional information
* is needed regarding the activities of the router.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>routingCriteriaClass.1-n</TD>
* <TD>no</TD>
* <TD>These properties are used to specify RoutingCriteriaCommand implementations that should
* be used to determine, based on content and other factors, if the message should be routed to the end point. 
* Multiple classes may be specified, using "dot" notation, or some other distinguishing technique,
* in the name of the class.  They will be executed in the order they're specified. 
* Note, they are NOT currently executed based on the number to the right of the "dot". 
* That is simply a technique used to specify multiple RoutingCriteria classes. 
* <P>
* If a routing criteria class(es) exist, this command will call the 'shouldRoute' method on the
* RoutingCriteriaCommand implementations passing the AppConfig object associated to the end-point
* and the XML Document built from the JMS Message consumed.  Then, that RoutingCriteriaCommand can
* do whatever it likes to determine if the end point is interested in this message.  This may include
* things a simple as verifying certain data items in the message or it may be something as complex
* as querying other authoritative sources for additional information that may not be supplied in the
* message consumed.  These RoutingCriteriaCommands are intended to be very specific to the end point
* in question.</TD>
* </TR>
* </TABLE>
* <P>
* </TD>
* </TR>
* </TABLE>
*
* <P>
* Sync Errors that might be published by this gateway:
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Error Number</TH>
* <TH>Error Type</TH>
* <TH>Error Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-1001</TD>
* <TD>application</TD>
* <TD>This error will be published if the command has problems converting the
* body of the JMS message into an XML Document.  The reason for the error should
* be included in the ErrorDescription element of the Sync Error.  If this error occurs
* the message <b>will not</b> be routed to any targets but a record of the failure will
* be published and the original message will have typically been logged by the original
* publishing applications LoggingProducer.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2001</TD>
* <TD>application</TD>
* <TD>This error will be published if a DataArea child element cannot be found in
* the document created from the body of the JMS message consumed.  The name of the object
* the command was looking for will be specified in the ErrorDescription.  The expected
* location of this element will be determined by the ControlAreaSync@messageAction and the
* ControlAreaSync@messageObject attributes.  For example, if the ControlArea@messageAction is 'Create'
* and the ControlAreaSync@messageObject is 'BasicPerson' the command will look for the
* DataArea/Create/BasicPerson Element that should exist in the Document.  If this element is not found,
* a Sync Error will be published and the message <b>will not</b> be routed to any targets
* but a record of the failure will be published and the original message will have typically
* been logged by the original publishing applications LoggingProducer.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2002</TD>
* <TD>application</TD>
* <TD>This error will be published if there are 'RoutingCriteria' classes associated to the component
* the Router is attempting route to and that RoutingCriteria class cannot be successfully <b>instantiated</b>
* If this occurs, the Router will publish the error an continue to the next application in the list of
* applications it's suppose to route to.  Therefore, there will be a record of this failure but it <b>will
* not</b> prohibit the message from being routed to the other destinations that might be interested in this
* message.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2003</TD>
* <TD>application</TD>
* <TD>This error will be published if there are 'RoutingCriteria' classes associated to the component
* the Router is attempting route to and that RoutingCriteria class cannot be successfully <b>executed</b>
* If this occurs, the Router will publish the error an continue to the next application in the list of
* applications it's suppose to route to.  Therefore, there will be a record of this failure but it <b>will
* not</b> prohibit the message from being routed to the other destinations that might be interested in this
* message.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2004</TD>
* <TD>application</TD>
* <TD>This error will be published if there are errors building the appopriate ControlAreaSync element that
* needs to be associated to the message being routed to the current target.
* If this occurs, the Router will publish the error an continue to the next application in the list of
* applications it's suppose to route to.  Therefore, there will be a record of this failure but it <b>will
* not</b> prohibit the message from being routed to the other destinations that might be interested in this
* message.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2005</TD>
* <TD>application</TD>
* <TD>This error will be published if the ControlAreaSync@messageAction is not
* 'Create', 'Update' or 'Delete'.  These are the only actions currently supported by this version
* of the router.    If this error occurs the message <b>will not</b> be routed
* to any targets but a record of the failure will
* be published and the original message will have typically been logged by the original
* publishing applications LoggingProducer.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2006</TD>
* <TD>application</TD>
* <TD>This error will be published if the router encounter issues trying to build
* the XML document it needs to router from the contents of the new ControlAreaSync and the
* message object it retrieved from the original message.  This is the message that it will be
* forwarding to the current target.  This message is of the same type and action of the original message
* but it includes the new ControlAreaSync with the router information in it.  It might also contain different data in the
* object included in the DataArea of the message if the router found application specific translations associated to the object
* when it serializes the object to an XML element.
* If this occurs, the Router will publish the error an continue to the next application in the list of
* applications it's suppose to route to.  Therefore, there will be a record of this failure but it <b>will
* not</b> prohibit the message from being routed to the other destinations that might be interested in this
* message.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2007</TD>
* <TD>application</TD>
* <TD>This error will be published if the 'outboundXmlValidation' property is
* set to 'true' for the router command and the message that would be routed is
* not a valid XML document.  After the router has built the new message that it needs to route, it
* will check to see if outbound XML validation is "turned on".  If it is, it
* will validate the new XML Document.  If the document is not valid, it will publish this error.
* If this occurs, the Router will publish the error an continue to the next application in the list of
* applications it's suppose to route to.  Therefore, there will be a record of this failure but it <b>will
* not</b> prohibit the message from being routed to the other destinations that might be interested in this
* message.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2008</TD>
* <TD>application</TD>
* <TD>This error will be published if the router encounters issues actually
* routing the message to the current application.  This means, if it has problems
* actually publishing the new message to the target using the PubSubProducer associated to
* that target, it will publish this error.
* If this occurs, the Router will publish the error an continue to the next application in the list of
* applications it's suppose to route to.  Therefore, there will be a record of this failure but it <b>will
* not</b> prohibit the message from being routed to the other destinations that might be interested in this
* message.</TD>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>OpenEAI_ROUTER-2009</TD>
* <TD>application</TD>
* <TD>This error will be published if the router encounters issues either building
* a Java object from the DataArea XML Element in the original Message or generating an XML Element from that
* object that will be included in the new message being routed.
* If this error occurs the message <b>will not</b> be routed
* to any additional targets but a record of the failure will
* be published and the original message will have typically been logged by the original
* publishing applications LoggingProducer.</TD>
* </TR>
* </TABLE>
* <P>
* @author      Tod Jackson (tod@openeai.org)
* @version     1.0 - 26 March 2003
* @see org.openeai.config.AppConfig
* @see org.openeai.config.LoggerConfig
* @see org.openeai.config.ConsumerConfig
* @see org.openeai.config.CommandConfig
* @see org.openeai.config.ProducerConfig
* @see org.openeai.config.MessageObjectConfig
* @see RoutingCriteriaCommand
* @see RoutingCriteriaCommand#shouldRoute(AppConfig, Document)
*/
public class TransRouterCommand
extends SyncCommandImpl
implements SyncCommand {

  private int m_messageSequence = 0;

  /**
   * Constructor
   */
  public TransRouterCommand(CommandConfig cConfig) throws InstantiationException {
    super(cConfig);

    // This is so the inherited Logger configuration (specified at the gateway level)
    // can be overriden at the command level.
    try {
      LoggerConfig lConfig = new LoggerConfig();
      lConfig = (LoggerConfig)getAppConfig().getObjectByType(lConfig.getClass().getName());
      logger = Category.getInstance(getClass().getName());
      PropertyConfigurator.configure(lConfig.getProperties());
      logger.warn("got a LoggerConfig from the command's app config..." + lConfig.getProperties());
    }
    catch (Exception e) {
      logger = org.openeai.OpenEaiObject.logger;
      logger.debug("no LoggerConfig in the command's app config...");
      logger.debug(e.getMessage(), e);
    }

    PropertyConfig pConfig = new PropertyConfig();
    try {
      pConfig = (PropertyConfig)getAppConfig().getObjectByType(pConfig.getClass().getName());
      setProperties(pConfig.getProperties());
    }
    catch (EnterpriseConfigurationObjectException e) {
      logger.fatal(e.getMessage(), e);
      throw new InstantiationException(e.getMessage());
    }

    // Add all applications listed in config file to m_apps.
    // These will be used each time execute is called to build
    // an AppConfig object for each application that must be
    // Translated/Routed to.
    // This will be a list of MessageComponent Elements.
    setMsgComponents(cConfig.getMsgComponents());
    // Go through all message component elements and create and store an app config
    // named the same as what's stored with the message component element.
    HashMap mComponents = getMsgComponents();
    Collection cValues = mComponents.values();
    Iterator it = cValues.iterator();
    while (it.hasNext()) {
      Element eComponent = null;
      try {
        eComponent = (Element)it.next();
        Element eConfig = eComponent.getChild("Configuration");
        if (eConfig != null) {
          AppConfig aTempConfig = new AppConfig();
          aTempConfig.setName(eComponent.getAttribute("id").getValue());
          aTempConfig.init(eConfig);
          addAppConfig(aTempConfig);
        }
        else {
          // Error
          String msg = "Couldn't find a 'Configuration' element for MessageComponent: " +
                       eComponent.getAttribute("id").getValue();
          logger.fatal(msg);
          throw new InstantiationException(msg);
        }
      }
      catch (Exception e) {
        logger.fatal("Error initializing AppConfig for MessageComponent: " +
                     eComponent.getAttribute("id").getValue());
        logger.fatal("Exception: " + e.getMessage());
        throw new InstantiationException(e.getMessage());
      }
    }
    logger.info("TransRouter initialized successfully.");
  }

  public void execute(int messageNumber, Message aMessage) throws CommandException {

    logger.debug("TransRouterCommand is executing...");

    Document inDoc = null;
    try {
      inDoc = initializeInput(messageNumber, aMessage);
    }
    catch (Exception e) {
      String errMessage = "Exception occurred processing input message in ConsumerCommand.  Exception: " + e.getMessage();
      ArrayList errors = new ArrayList();
      errors.add(buildError("application", "OpenEAI_ROUTER-1001", errMessage));
      publishSyncError(new Element("EmptyControlArea"), errors, e);
      throw new CommandException(e.getMessage());
    }

    Element eControlArea = getControlArea(inDoc.getRootElement());
    Element eDataArea = inDoc.getRootElement().getChild(DATA_AREA);

    String msgAction = eControlArea.getAttribute(MESSAGE_ACTION).getValue();
    String msgObject = eControlArea.getAttribute(MESSAGE_OBJECT).getValue();
    String msgRelease = eControlArea.getAttribute(MESSAGE_RELEASE).getValue();

    // need to get the messageRelease and create the appropriate package name from that...
    String msgObjectName = msgObject + "." + generateRelease(msgRelease);

    String dataAreaChild = NEW_DATA;    // Default, this will work for Create and Update
    if (msgAction.equalsIgnoreCase(DELETE_ACTION)) {
      dataAreaChild = DELETE_DATA;
    }

    // Get the DataArea element out of the document
    // This should correspond to one of our message objects.
    Element eInput = eDataArea.getChild(dataAreaChild).getChild(msgObject);

    if (eInput == null) {
      // Error!
      String errMessage = "Could not find an element at DataArea/" + dataAreaChild + "/" + msgObject +
                   "in the " + msgObject + "-" + msgAction + "-Sync Document passed in";
      logger.fatal(errMessage);
      logger.fatal("Message sent in is: \n" + getMessageBody(inDoc));

      ArrayList errors = new ArrayList();
      errors.add(buildError("application", "OpenEAI_ROUTER-2001", errMessage));
      publishSyncError(eControlArea, errors);
      return;
    }

    try {
      // For each application we're forwarding to (a list of AppConfigs)
      // "transform" the message into a version that may contain application
      // specific values for that application and forward it on to that application
      // using the producer associated to that application.
      for (int i=0; i<getAppConfigs().size(); i++) {
        Document outDoc = (Document)inDoc.clone();
        AppConfig aConfig = (AppConfig)getAppConfigs().get(i);

        logger.info("Checking routing info for application: " + aConfig.getName());

        // Get the 'RoutingProperties' associated to this target.  This will tell us
        // things about what we're suppose to do with this message before we route it.
        // Like, dump it to a file, etc.
        PropertyConfig pConfig = null;
        try {
          pConfig = (PropertyConfig)aConfig.getObject("RoutingProperties");
        }
        catch (EnterpriseConfigurationObjectException e) {
          logger.warn("No 'RoutingProperties' for Application " + aConfig.getName() + " will use defaults.");
        }
        Properties aProps = null;
        if (pConfig != null) {
          aProps = pConfig.getProperties();
        }
        else {
          aProps = new Properties();
        }
        boolean dumpOutput = new Boolean(aProps.getProperty("dumpOutput","false")).booleanValue();
        boolean provideTargetAppName = new Boolean(aProps.getProperty("provideTargetAppName","true")).booleanValue();
        boolean provideSourceControlArea = new Boolean(aProps.getProperty("provideSourceControlArea","false")).booleanValue();
        boolean routeToTarget = new Boolean(aProps.getProperty("routeToTarget","true")).booleanValue();
        boolean translateToTargetValues = new Boolean(aProps.getProperty("translateToTargetValues","false")).booleanValue();

        // Get the producer associated to the application we're forwarding to.
        PubSubProducer pubSub = new PubSubProducer();
        try {
           pubSub = (PubSubProducer)aConfig.getObjectByType(pubSub.getClass().getName());
        }
        catch (EnterpriseConfigurationObjectException e) {
          logger.info("Application " + aConfig.getName() + " doesn't have a PubSubProducer.  Continuing to next Application.");
          logger.info(e.getMessage(), e);
          continue;
        }

        ActionableEnterpriseObject anXeo = null;

        // Try to get the message object out of the AppConfig associated to tha app
        // we're forwarding to.  If the application doesn't care about the current
        // message object, just continue on to the next application in the list.
        try {
          anXeo = (ActionableEnterpriseObject)aConfig.getObject(msgObjectName);
        }
        catch (EnterpriseConfigurationObjectException e) {
          logger.info("Application " + aConfig.getName() + " doesn't care about " +
                      "objects of type " + msgObject);
          continue;
        }

        // build a list of instantiated 'RoutingCriteriaCommand' objects that will be used
        // to perform 'content based' routing for the application we're forwarding to
        // (if applicable).
        Enumeration keys = aProps.keys();
        ArrayList routingCriteriaObjects = new ArrayList();
        boolean routingCriteriaErrors = false;
        while (keys.hasMoreElements()) {
          String keyName = (String)keys.nextElement();
          if (keyName.toLowerCase().indexOf("routingcriteriaclass") != -1) {
            String className = aProps.getProperty(keyName,null);
            if (className != null && className.length() > 0) {
              try {
                RoutingCriteriaCommand rcCommand = (RoutingCriteriaCommand)Class.forName(className).newInstance();
                routingCriteriaObjects.add(rcCommand);
              }
              catch (Exception e) {
                routingCriteriaErrors = true;
                String errMsg = "Exception instantiating 'RoutingCriteriaClass' " + className +
                  " for Application: " + aConfig.getName() + "  Exception: " + e.getMessage();
                logger.fatal(errMsg);
                logger.fatal(e.getMessage(), e);

                ArrayList errors = new ArrayList();
                errors.add(buildError("application", "OpenEAI_ROUTER-2002", errMsg));
                publishSyncError(eControlArea, errors, e);
              }
            }
          }
        }

        if (routingCriteriaErrors) {
          logger.fatal("There were errors instantiating the 'RoutingCriteriaClass' (or classes) for the Application " +
            aConfig.getName() + " can't route to target without the required Routing Criteria rules.  Will continue to the next application.");
          continue;
        }
        else {
          // determine if we should route to this application based on message content (if applicable).
          boolean shouldRouteErrors = false;
          boolean shouldRoute = true;
          routeTest: for (int xx=0; xx<routingCriteriaObjects.size(); xx++) {
            RoutingCriteriaCommand rcc = (RoutingCriteriaCommand)routingCriteriaObjects.get(xx);
            try {
              if (rcc.shouldRoute(aConfig, inDoc) == false) {
                shouldRoute = false;
                break routeTest;
              }
            }
            catch (Exception e) {
              shouldRouteErrors = true;
              String errMsg = "Exception executing 'RoutingCriteriaClass' " + rcc.getClass().getName() +
                " for Application: " + aConfig.getName() + "  Exception: " + e.getMessage();
              logger.fatal(errMsg);
              logger.fatal(e.getMessage(), e);

              ArrayList errors = new ArrayList();
              errors.add(buildError("application", "OpenEAI_ROUTER-2003", errMsg));
              publishSyncError(eControlArea, errors, e);
            }
          }
          if (shouldRouteErrors) {
            logger.fatal("There were errors executing the 'RoutingCriteriaClass' (or classes) for the Application " +
              aConfig.getName() + " can't route to target without the required Routing Criteria rules.  Will continue to the next application.");
            continue;
          }
          if (shouldRoute == false) {
            logger.info("Application " + aConfig.getName() +
              " doesn't want to be routed to based on the contents of the " +
              msgObject + "-" + msgAction + " message.  Will continue to the next application.");
            continue;
          }
        }

        // process and route the message to the target application...
        // if we get here, we know the application is interested in the message we've consumed
        // and there was nothing in the message content that prohibited it from being
        // routed.
        if (anXeo != null) {
          // Now, build the object from the XML passed in.
          // Populate the object with enterprise values
          logger.info("Message Number: " + messageNumber + " - Building a " + msgObject +
                      " message object for Gateway " + aConfig.getName());
          anXeo.getXmlEnterpriseObject().buildObjectFromInput(eInput);
          logger.debug("Built object out of the input.");

          // Now build an element containing application specific values
          // for the current application (if applicable).
         
          // 4/28/2004 TJ - only do this if the translateToTargetValues property is
          // true.  If this property is false, we won't do the 'reverse' translation
          // we'll just route the element from the message we consumed in its
          // current state.
          Element eOut = null;
          if (translateToTargetValues) {
            logger.debug("Creating an XML Element out of the " + anXeo.getClass().getName() +
                        " for application " + aConfig.getName());
            eOut = (Element)anXeo.getXmlEnterpriseObject().buildOutputFromObject(aConfig.getName());
          }
          else {
            logger.info("NOT translating to target specific values.");
            eOut = (Element)eInput.clone();
          }

          // 4/28/2004 TJ - commenting out this code because it's really not needed
          // and it will slow things down just a bit...
//          logger.debug("Built Element from object.");
//          XMLOutputter xmlOutTemp = new XMLOutputter();
//          logger.debug("XML is: \n" + xmlOutTemp.outputString(eOut));

          // Now, we need to re-populate the document with the application
          // specific information.

          // Rebuild the control area
          Element newControlArea = (Element)eControlArea.clone();
          try {
            newControlArea = buildControlArea(newControlArea, pubSub, provideTargetAppName, provideSourceControlArea, aConfig.getName());
            outDoc.getRootElement().removeChild(eControlArea.getName());
            outDoc.getRootElement().addContent(newControlArea);
          }
          catch (EnterpriseFieldException e) {
            String errMsg = "Error building control area for message I'm " +
              "forwarding to Application: " + aConfig.getName();
            logger.fatal(errMsg);
            logger.fatal(e.getMessage(), e);

            ArrayList errors = new ArrayList();
            errors.add(buildError("application", "OpenEAI_ROUTER-2004", errMsg));
            publishSyncError(eControlArea, errors, e);

            continue;
          }

          try {
          if (msgAction.equalsIgnoreCase(CREATE_ACTION)) {

            // Rebuild the data area
            outDoc.getRootElement().removeChild(DATA_AREA);
            outDoc.getRootElement().addContent(new Element(DATA_AREA));
            outDoc.getRootElement().getChild(DATA_AREA).addContent(new Element(dataAreaChild));

            outDoc.getRootElement().
              getChild(DATA_AREA).
              getChild(dataAreaChild).
              addContent(eOut);

          }
          else if (msgAction.equalsIgnoreCase(DELETE_ACTION)) {
            // Rebuild the data area
            outDoc.getRootElement().removeChild(DATA_AREA);
            outDoc.getRootElement().addContent(new Element(DATA_AREA));
            outDoc.getRootElement().getChild(DATA_AREA).addContent(new Element(dataAreaChild));

            Element eDeleteAction = (Element)inDoc.getRootElement().
              getChild(DATA_AREA).
              getChild(dataAreaChild).
              getChild("DeleteAction").clone();

            outDoc.getRootElement().
              getChild(DATA_AREA).
              getChild(dataAreaChild).
              addContent(eDeleteAction);

            outDoc.getRootElement().
              getChild(DATA_AREA).
              getChild(dataAreaChild).
              addContent(eOut);
          }
          else if (msgAction.equalsIgnoreCase(UPDATE_ACTION)) {
            Element eBaseline = outDoc.getRootElement().
                                getChild(DATA_AREA).
                                getChild("BaselineData").
                                getChild(msgObject);

            ActionableEnterpriseObject anXeoBaseline =
            (ActionableEnterpriseObject)aConfig.getObject(msgObjectName);

            // Rebuild the data area
            outDoc.getRootElement().removeChild(DATA_AREA);
            outDoc.getRootElement().addContent(new Element(DATA_AREA));
            outDoc.getRootElement().getChild(DATA_AREA).addContent(new Element(dataAreaChild));
            outDoc.getRootElement().getChild(DATA_AREA).addContent(new Element("BaselineData"));

            anXeoBaseline.getXmlEnterpriseObject().buildObjectFromInput(eBaseline);
            Element eBaselineOut = null;
            if (translateToTargetValues) {
              logger.debug("Creating a Baseline XML Element out of the " + anXeo.getClass().getName() +
                          " for application " + aConfig.getName());
              eBaselineOut = (Element)anXeoBaseline.getXmlEnterpriseObject().buildOutputFromObject(aConfig.getName());
            }
            else {
              logger.info("NOT translating BaselineData to target specific values.");
              eBaselineOut = (Element)eBaseline.clone();
            }

            // New data
            outDoc.getRootElement().
              getChild(DATA_AREA).
              getChild(dataAreaChild).
              addContent(eOut);

            // Baseline data
            outDoc.getRootElement().
              getChild(DATA_AREA).
              getChild("BaselineData").
              addContent(eBaselineOut);
          }
          else {
            // Error
            String errMsg = "Invalid message action: " + msgAction + " can't continue processing this messagin.";
            logger.fatal(errMsg);

            ArrayList errors = new ArrayList();
            errors.add(buildError("system", "OpenEAI_ROUTER-2005", errMsg));
            publishSyncError(eControlArea, errors);
            return;
          }
          }
          catch (Exception e) {
            String errMsg = "Error building document for message I'm " +
              "forwarding to Application: " + aConfig.getName();
            logger.fatal(errMsg);
            logger.fatal(e.getMessage(), e);

            ArrayList errors = new ArrayList();
            errors.add(buildError("application", "OpenEAI_ROUTER-2006", errMsg));
            publishSyncError(eControlArea, errors, e);
            continue;
          }

          if (getOutboundXmlValidation()) {
            XmlValidator xmlValidator = new XmlValidator();
            boolean isValid = true;
            synchronized (outDoc) {
              isValid = xmlValidator.isValid(outDoc);
            }
            if (isValid == false) {
              String errorMsg = outDoc.getRootElement().getName() +
                " document is not valid after TransRoute modifications.  Can't forward message.";
              logger.fatal(errorMsg);
              XMLOutputter xmlOut = new XMLOutputter();
              logger.fatal("Document content is: \n" + xmlOut.outputString(outDoc));

              ArrayList errors = new ArrayList();
              errors.add(buildError("system", "OpenEAI_ROUTER-2007", errorMsg));
              publishSyncError(eControlArea, errors);
              continue;
            }
          }

          if (dumpOutput) {
            String dumpDir = getMessageDumpDirectory();
            if (dumpDir.lastIndexOf("/") != dumpDir.length() - 1) {
              dumpDir = dumpDir + "/";
            }
            dumpDir = dumpDir + "output/" + aConfig.getName() + "/";
            try {
              writeMessageToFile(msgObject, outDoc, dumpDir);
            }
            catch (IOException e) {
              logger.fatal("Exception occurred writing output message to file.  Processing will continue.  Exception: " + e.getMessage());
            }
          }

          // if routing is disabled for this application, we're just going to continue here.
          // we're waiting until here to make this decision so we can let it "dump" the output document
          // that would be routed without actually routing it...
          if (routeToTarget == false) {
            logger.info("Routing is 'disabled' to target " + aConfig.getName() + " continuing to next target.");
            continue;
          }

          // Now, take the "transformed" message and forward it on to the application
          // using the pubsub producer associated to that application.
          TextMessage outMessage = pubSub.createTextMessage();
//          XMLOutputter xmlOut = new XMLOutputter();
          try {
//            outMessage.setText(xmlOut.outputString(outDoc));
//            outMessage.setStringProperty(MessageProducer.COMMAND_NAME, anXeo.getCommandName());
//            outMessage.setStringProperty(MessageProducer.MESSAGE_ID, getMessageId(newControlArea));
//            pubSub.publishMessage(outMessage);
            MessageId msgId = getMessageId(newControlArea);
            anXeo.setMessageId(msgId);
            pubSub.publishMessage(anXeo, outDoc);
            logger.info("Message Number: " + messageNumber + " - Routed " + msgObject +
              " message to Gateway: " + aConfig.getName());
          }
          catch (Exception e) {
            logger.fatal(e.getMessage(), e);
            ArrayList errors = new ArrayList();
            errors.add(buildError("system", "OpenEAI_ROUTER-2008", "Exception occurred routing " +
              msgObject + "/" + msgAction + " message to gateway " + aConfig.getName() ));
            publishSyncError(eControlArea, errors, e);
          }
        }
        else {
          // Should never get here!
          logger.debug("Could not find a " + msgObjectName +
            " in the AppConfig for " + aConfig.getName());
        }
      }
    }
    catch (Exception e) {
      String errMessage = "Exception occurred building the object or building the output.  Exception: " + e.getMessage();
      logger.fatal(e.getMessage(), e);
      ArrayList errors = new ArrayList();
      errors.add(buildError("system", "OpenEAI_ROUTER-2009", errMessage));
      publishSyncError(eControlArea, errors, e);
      return;
    }
  }

  /*
  private String getMessageId(Element controlArea) {
    Element eMsgId = controlArea.getChild("Sender").getChild("MessageId");

    if (eMsgId != null) {
      MessageId msgId = new MessageId();
      try {
        msgId.buildObjectFromInput(eMsgId);
      }
      catch (Exception e) {
        logger.fatal("Error building message id from XML.  Exception: " + e.getMessage());
        return null;
      }
      return msgId.toString();
    }
    else {
      return null;
    }
  }
  */

  private MessageId getMessageId(Element controlArea) {
    Element eMsgId = controlArea.getChild("Sender").getChild("MessageId");

    if (eMsgId != null) {
      MessageId msgId = new MessageId();
      try {
        msgId.buildObjectFromInput(eMsgId);
      }
      catch (Exception e) {
        logger.fatal("Error building message id from XML.  Exception: " + e.getMessage());
        return null;
      }
      return msgId;
    }
    else {
      return null;
    }
  }

  private synchronized Element buildControlArea(Element controlArea,
    PubSubProducer pubSub, boolean provideTargetAppName,
    boolean provideSourceControlArea, String targetAppName) throws EnterpriseFieldException {

    // We will:
    // - the the message id and authentication elements out of the sender's control area
    // - use the authentication information in the message we're forwarding on
    // - create new message id information from the producer passed in and our app id (TransRoute)

    // Add the current ControlAreaSync element passed in to the SourceInfo
    // element of the control area we're building.  That new sourceinfo element
    // will be added later (at the bottom of this method) when all the other
    // new elements are added.
    Element eSourceInfo = controlArea.getChild("SourceInfo");
    boolean replaceSourceInfo = false;
    if (eSourceInfo == null) {
      eSourceInfo = new Element("SourceInfo");
    }
    else {
      replaceSourceInfo = true;
    }
    if (provideSourceControlArea) {
      Element sourceInfoControlArea = (Element)controlArea.clone();
      eSourceInfo.addContent(sourceInfoControlArea);
      replaceSourceInfo = true;
    }
    controlArea.removeChild("SourceInfo");

    // Set the sender element
    Sender oldSender = new Sender();
    Element eInputSender = controlArea.getChild("Sender");
    try {
      oldSender.buildObjectFromInput(eInputSender);
    }
    catch (EnterpriseLayoutException e) {
      throw new EnterpriseFieldException(e.getMessage());
    }
    controlArea.removeChild("Sender");

    Authentication oldAuth = oldSender.getAuthentication();

    MessageId newMsgId = new MessageId();
    newMsgId.setSenderAppId("EnterpriseTransRouter");
    if (pubSub.getProducerId(null) == null) {
      newMsgId.setProducerId("EmptyProducerId");
    }
    else {
      newMsgId.setProducerId(pubSub.getProducerId(null).getId());
    }

    // Since a producer's message sequence gets reset everytime
    // it's retrieved from AppConfig, we have to maintain our
    // own message sequence number in this command
    // so the messages we forward on to other gateways
    // will have a unique message id in their control area.
    m_messageSequence++;
    newMsgId.setMessageSeq(Integer.toString(m_messageSequence));

    Sender newSender = new Sender();
    if (oldSender.getTestId() != null) {
      newSender.setTestId(oldSender.getTestId());
    }
    newSender.setAuthentication(oldAuth);
    newSender.setMessageId(newMsgId);

    Element eNewSender = null;
    try {
      eNewSender = (Element)newSender.buildOutputFromObject();
    }
    catch (EnterpriseLayoutException e) {
      throw new EnterpriseFieldException(e.getMessage());
    }

    // Set the datetime element
    controlArea.removeChild("Datetime");
    Datetime dt = new Datetime();
    Element eDatetime = null;
    try {
      eDatetime = (Element)dt.buildOutputFromObject();
    }
    catch (EnterpriseLayoutException e) {
      throw new EnterpriseFieldException(e.getMessage());
    }

    if (replaceSourceInfo) {
      controlArea.addContent(eSourceInfo);
    }

    // If the target we're routing to wants it, we're going to provide the name of that
    // target app so we can know more about these messages if they have problems and get
    // logged via EnterpriseSyncErrorLogger
    Element eTargetInfoTemp = null;
    Element eTargetInfo = null;
    eTargetInfoTemp = controlArea.getChild("TargetInfo");
    controlArea.removeChild("TargetInfo");
    if (provideTargetAppName) {
      if (eTargetInfoTemp == null) {
        eTargetInfo = new Element("TargetInfo");
      }
      else {
        eTargetInfo = (Element)eTargetInfoTemp.clone();
      }
      eTargetInfo.removeChild("TargetAppName");
      Element eTargetAppName = new Element("TargetAppName");
      eTargetAppName.setText(targetAppName);
      eTargetInfo.addContent(eTargetAppName);
      controlArea.addContent(eTargetInfo);
    }
    else if (eTargetInfoTemp != null) {
      // have to add the original one back in...
      eTargetInfo = (Element)eTargetInfoTemp.clone();
      controlArea.addContent(eTargetInfo);
    }

    controlArea.addContent(eNewSender);
    controlArea.addContent(eDatetime);

    return controlArea;
  }
}
TOP

Related Classes of org.openeai.implementations.gateways.transroutergateway.TransRouterCommand

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.