Package org.any_openeai_enterprise.gateways.brg

Source Code of org.any_openeai_enterprise.gateways.brg.BannerRequestCommand

/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/examples/org/any_openeai_enterprise/gateways/brg/BannerRequestCommand.java,v $
$Revision: 1.3 $
*******************************************************************************/

/**********************************************************************
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.any_openeai_enterprise.gateways.brg;

import javax.jms.*;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.output.XMLOutputter;
import java.util.*;

import org.openeai.config.*;
import org.openeai.jms.consumer.*;
import org.openeai.jms.consumer.commands.*;
import org.openeai.dbpool.*;
import org.openeai.moa.*;
import org.openeai.moa.objects.testsuite.*;
import org.any_openeai_enterprise.gateways.brg.RequestLogicExecutor;

/**
* This command is the base command for all Request message consumption that
* the might be sending to Banner.  This gateway can be used
* to process requests that either aren't supported by SCT's message support or
* to fulfill a need that isn't provided YET.  It can also be used as
* an example or pattern for exposing any systems' business API via messaging.  It is a
* "general purpose" gateway that can be extended as that message support is
* identified.
* <P>
* The gateway is extended by implementing additional 'RequestLogicExecutor' classes
* that support the requests being sent to the gateway.
* <P>
* Currently, it consumes requests of the following type:
* <ul>
* <li>com.sct.Person/BasicPerson/1.0
* </ul>
* <P>
* <B>Gateway Configuration</B>
* <P>
* These are the configuration parameters required by this gateway. 
* They are specified in the AnyOpenEAIEnterprise.xml Deployment Descriptor included
* with the sample enterprise. 
* This gateway is identified in the Deployment
* Descriptor as <b>org.any_openeai_enterprise.BannerGateway</b>.
* <P>
* Configuration for a gateway can be broken into two pieces:
* <ul>
* <li>Configuring the consumer to connect to a topic or queue and telling it what commands it executes.
* <li>Configuring the command(s) executed by that consumer.
* </ul>
* <P>
* <b>Configuring the Consumer</b>
* <P>
* The items listed below can be found in the 'Configuration' Element associated to
* the edu.uillinois.aits.BannerRequestGateway gateway in the deployment descriptor. 
* This Configuration element becomes the AppConfig
* object associated to this gateway <b>(remember, all OpenEAI gateways are started through
* the org.openeai.jms.consumer.MessageConsumerClient class)</b>.
* <P>
* The consumer's Configuration is fairly simple.  It contains a LoggerConfig object that specifies
* how the log4j logger will behave and it contains a ConsumerConfigs element.  Below is a description
* of the Consumer configuration.  Note, not all configurable items are mentioned but that
* information can be reviewed by looking at the core OpenEAI API Javadoc.
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Configuration Object Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>BannerP2PConsumer1</TD>
* <TD>yes</TD>
* <TD>An OpenEAI PointToPointConsumer object configured to consume JMS messages off
* the 'cn=BannerGatewayQueue' queue.  When the EnterpriseRequestProxy
* forwards requests to that queue, this consumer will consume the message.  Then it will
* exeute this command which will determine what business logic needs to be executed (if any)
* and execute that logic and return the resulting message to the requesting application.
* <P>
* The Consumer's configuration specifies that it will connect to the broker
* and be ready to consume messages when it is initialized ('startOnInitialization=true') use the
* InitialContextFactory 'org.exolab.jms.jndi.rmi.RmiJndiInitialContextFactory' to retrieve the JMS
* administered objects from the directory server normally location specified by the ProviderURL element. 
* Then it will connect to the JMS Provider using the 'cn=BannerConsumerQCF' JMS QueueConnectionFactory
* administered object(s).
* <P>
* It will execute the Commands specified (this command) in a ThreadPool and it will
* detect when the ThreadPool is busy and only add another Command execution to
* the ThreadPool when the ThreadPool has
* available threads to process the transaction (checkBeforeProcessing=true).</TD>
* </TR>
* </TABLE>
* <P>
* <b>Configuring the Command(s) executed by the Consumer</b>
* <P>
* The BannerRequestCommand command (this class) will NOT validate
* the JDOM Document created from the JMS Message that's passed to it (inboundXmlValidation=false). 
* If you'd like it to validate incomming XML, you can change this attribute to true. 
* Additionally, it will not write the XML Document to a file (writeToFile=false) when it receives it.  As stated
* before, this command is the 'absolute' command associated to this consumer.  So, no matter what "COMMAND_NAME" is
* associated to the JMS Message as it is consumed by the consumer, this command will be executed.  The
* BannerRequestCommand (CommandName) is implemented by the
* org.any_openeai_enterprise.messaging.gateways.BannerRequestCommand class
* (CommandClass, which is this class) and
* it is a 'requestMessage' Command (type=requestMessage).  This means, this command
* will return a reply to the requesting application.  Specifically, it will return
* the reply generated by the RequestLogicExecutor associated to the messageObject
* in the request it consumes (e.g. - BasicPerson).
* <P>
* The items listed below can be found in the 'Configuration' Element associated to the
* BannerRequestCommand in the deployment descriptor.  This Configuration element becomes the AppConfig
* object associated to this Command that is used by the command when it is executed.
* <P>
* They are broken apart by Configuration object type.  This command 
* will return CoreMessaging/Generic-Response-Reply messages if it ever has any problems performing the business logic,
* it will use Message Objects to operate on the data contained in the message consumed, it will
* use a DbConnectionPool to retrieve and/or persist data and it will use several general Properties object. 
* Therefore, there are four categories of configuration objects used: 
* MessageObjectConfigs, DbConnectionPoolConfigs and PropertyConfigs.
* <P>
* <P>
* <b>MessageObjectConfigs</b>
* <P>
* These configuration objects are used to configure all Message objects required by this command. 
* <P>
* The objects are used to operate on the data supplied in the sync messages as well
* as to retrieve and/or persist the relevant information to the database.
* <P>
* As new request consumption requirements are identified, there will be additional
* message objects that must be listed here in order to effectively operate on the
* messages consumed.
* <P>
* Currently, this gateway only provides support for BasicPerson-Query,
* BasicPerson-Create, BasicPerson-Update and BasicPerson-Delete request messages
* so it is the only object needed.  The TestId object is
* simply listed so we can use the OpenEAI TestSuite application to send request
* messages to this gateway and it can log information about the TestId included
* in that message (to correlate that message back to the test suite application).  This
* is how we'll verify that this gateway is performing it's function correctly.
* <P>
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Configuration Object Type</TH>
* <TH>Configuration Object Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>DbConnectionPoolConfigs</TD>
* <TD>DbPool</TD>
* <TD>yes</TD>
* <TD>This is an OpenEAI org.openeai.dbpool.EnterpriseConnectionPool object.  The command will 
* retrieve a pre-established database connection from the pool and execute SQL statements. 
* The connections in the pool are connected to Banner.</TD>
* </TR>
* </TABLE>
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Configuration Object Type</TH>
* <TH>Configuration Object Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>PropertyConfig</TD>
* <TD>GeneralProperties</TD>
* <TD>yes</TD>
* <TD>These are 'general' properties that will be associated to this Command.  These
* can be properties used by this command or they can be properties used by any of
* the RequestLogicExecutor classes that this command may execute.</TD>
* </TR>
* </TABLE>
* <P>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Configuration Object Type</TH>
* <TH>Configuration Object Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>PropertyConfig</TD>
* <TD>MessageMappings</TD>
* <TD>yes</TD>
* <TD>These are mappings between message objects and RequestLogicExecutor implementations. 
* The BasicPersonRequestImpl class is currently the only one listed because that's the only
* support we have to provide at this time.  There are three ways a message object can be
* associated to a specific RequestLogicExecutor implementation.  These are all based on
* naming standards that can be derived from data included in the message consumed
* by the gateway.  They are:
* <ul>
* <li>messageObject + messageRelease + messageAction (e.g. - BasicPerson.v1_0.Query)
* <li>messageObject + messageRelease (e.g. - BasicPerson.v1_0)
* <li>messageObject (e.g. - BasicPerson)
* </ul>
* These names can be used as PropertyNames and the PropertyValue associated to them
* should be the name of the RequestLogicExecutor implementation that should be used
* when a message of the appropriate type is consumed.  For example:
* <P>
* If a BasicPerson-Query-Request message is consumed with a messageRelease=1.0 is
* consumed, the messageObject attribute in the message will be 'BasicPerson', the
* messageRelease attribute will be '1.0' and the messageAction will be 'Query'.
* So, if in the MessageMappings properties there exists a PropertyName of
* 'BasicPerson.v1_0.Query' with a PropertyValue of
* 'org.any_openeai_enterprise.messaging.gateways.executors.v1_0.BasicPersonQueryImpl' that
* class will be instantiated, configured and executed to perform the Query functionality required. 
* The reason there are different ways this mapping can be made is to give developers more
* flexibility as to the granularity of the request logic implementations they wish to
* provide.  For example, you may want to simply put all support for all BasicPerson requests
* into one class alone.  Or, you may want to break it apart by action or by release etc.
* </TD>
* </TR>
* </TABLE>
* <TABLE BORDER=2 CELLPADDING=5 CELLSPACING=2>
* <TR>
* <TH>Configuration Object Type</TH>
* <TH>Configuration Object Name</TH>
* <TH>Required</TH>
* <TH>Description</TH>
* </TR>
* <TR HALIGN="left" VALIGN="top">
* <TD>PropertyConfig</TD>
* <TD>BasicPersonProperties</TD>
* <TD>yes</TD>
* <TD>Properties associated specifically to the BasicPersonRequestImpl class.  That class
* will also get a copy of the AppConfig associated to the Command but this allows developers
* to separate properties specifically associated to a particular implementation which
* will hopefully help make documentation and configuration easier to understand.</TD>
* </TR>
* </TABLE>
* <P>
* @author Tod Jackson (tod@openeai.org)
**/
public class BannerRequestCommand 
extends RequestCommandImpl
implements RequestCommand {

  private HashMap m_executorMappings = new HashMap();
  protected final static String REQUEST_MESSAGE_TYPE = "Request";

  public BannerRequestCommand(CommandConfig cConfig) throws InstantiationException {
    super(cConfig);
   
    try {
      // default properties associated to this command (there could be others named differently).
      PropertyConfig pConfig = (PropertyConfig)getAppConfig().getObject("GeneralProperties");
      setProperties(pConfig.getProperties());

    }
    catch (Exception e) {
      logger.fatal("Error initializing 'BannerRequestCommand' command.");
      logger.fatal(e.getMessage(), e);
      throw new InstantiationException(e.getMessage());
    }

    try {
      // default properties associated to this command (there could be others named differently).
      PropertyConfig pConfig = (PropertyConfig)getAppConfig().getObject("MessageMappings");
      Properties props = pConfig.getProperties();
      Enumeration keys = props.keys();
      while (keys.hasMoreElements()) {
        String key = (String)keys.nextElement();
        String executorClassName = props.getProperty(key);
        String executorName = key.toLowerCase();
        logger.info("Adding executor: " + executorName + "-" + executorClassName);
        m_executorMappings.put(executorName, executorClassName);
      }
    }
    catch (Exception e) {
      logger.fatal("Error initializing 'BannerRequestCommand' command.");
      logger.fatal(e.getMessage(), e);
      throw new InstantiationException(e.getMessage());
    }
  }
 
  public final Message execute(int messageNumber, Message aMessage) throws CommandException {

    String errMessage = "";
   
    // parse the text from the JMS message into a JDOM document.
    Document inDoc = null;
    try {
      inDoc = initializeInput(messageNumber, aMessage);
    }
    catch (Exception e) {
      String errMsg = "Exception occurred processing input message in BannerRequestCommand.  Exception: " + e.getMessage();
      throw new CommandException(errMsg, e);
    }

    // retrieve text portion of message passed in
    TextMessage msg = (TextMessage)aMessage;
    try {
      msg.clearBody()// So we don't have to do it later...
    }
    catch (JMSException e) {
      throw new CommandException(e.getMessage(), e);
    }

    // Get Control area from XML document
    Element eControlArea = getControlArea(inDoc.getRootElement());

    // Get Control Area (protocol) properties from message consumed.
    String msgAction = eControlArea.getAttribute(MESSAGE_ACTION).getValue();
    String msgCategory = eControlArea.getAttribute(MESSAGE_CATEGORY).getValue();
    String msgType     = eControlArea.getAttribute(MESSAGE_TYPE).getValue();
    String msgRelease = eControlArea.getAttribute(MESSAGE_RELEASE).getValue();
    String msgObject = eControlArea.getAttribute(MESSAGE_OBJECT).getValue();
    String msgObjectName = msgObject + "." + generateRelease(msgRelease);

    TestId testId = extractTestId(TEST_ID, eControlArea);
   
    // get the appropriate primed reply dodument.
    Document replyDoc = (Document)getReplyDoc(msgAction, msgObject, msgRelease).clone();

    if (msgType.equalsIgnoreCase(REQUEST_MESSAGE_TYPE) == false) {
      errMessage = "The BannerRequestGateway doesn't support " +
        msgType + " messages.  Since this gateway only supports request " +
        "messages that expect a reply.  Please check that the sending " +
        "application is sending the appropriate type of messages and " +
        "'Primed' XML documens associated to the messages objects being " +
        "used by the application are correct.";

      logger.fatal(errMessage);

      ArrayList errors = logErrors("BRG-1001", errMessage, inDoc);
       String replyContents = buildReplyDocumentWithErrors(eControlArea, replyDoc, errors);
      return getMessage(msg, replyContents);
    }

    // if the message object isn't one we're interested in return an error
    // look for a mapping name in the following formats:
    // - msgObject + release + action (e.g - BasicPerson.v1_0.Query)
    // - msgObject + release (e.g. - BasicPerson.v1_0)
    // - msgObject (e.g. - BasicPerson)
    // If none of these exist, it's an error
    String mappingName = null;
    if (m_executorMappings.containsKey(msgObjectName.toLowerCase() + "." + msgAction.toLowerCase()) == false) {
      if (m_executorMappings.containsKey(msgObjectName.toLowerCase()) == false) {
        if (m_executorMappings.containsKey(msgObject.toLowerCase()) == false) {
          errMessage = "The BannerRequestGateway doesn't support " +
            msgCategory + "/" +
            msgObject + "-" +
            msgAction + "-" +
            msgType + " version " + msgRelease + " messages.";

          logger.fatal(errMessage);

          ArrayList errors = logErrors("BRG-1002", errMessage, inDoc);
          String replyContents = buildReplyDocumentWithErrors(eControlArea, replyDoc, errors);
          return getMessage(msg, replyContents);
        }
        else {
          mappingName = msgObject.toLowerCase();
        }
      }
      else {
        mappingName = msgObjectName.toLowerCase();
      }
    }
    else {
      mappingName = msgObjectName.toLowerCase() + "." + msgAction.toLowerCase();
    }

    // Get the message object (e.g. BasicPerson, LightweightPerson etc.) element out of the document.
    // This will exist in the DataArea portion of the document.  We know this because
    // The OpenEAI Message Protocol says so.

    String dataAreaChild = "";
    Element eMessageObject = null;
    if (msgAction.equalsIgnoreCase(QUERY_ACTION)) {
      // since there is only ever one query object (so far), this should be okay.       
      java.util.List lChildren = inDoc.getRootElement().getChild(DATA_AREA).getChildren();
      for (int i=0; i<lChildren.size(); i++) {
        eMessageObject = (Element)lChildren.get(i);
      }
    }
    else {
      // determine where the message object is in the Document base on messageAction.
      dataAreaChild = NEW_DATA;    // Default, this will work for Create and Update
      if (msgAction.equalsIgnoreCase(DELETE_ACTION)) {
        dataAreaChild = DELETE_DATA;
      }
      eMessageObject = inDoc.getRootElement().
        getChild(DATA_AREA).
        getChild(dataAreaChild).
        getChild(msgObject);
    }

    if (eMessageObject == null) {
      // Error!
      errMessage = "Could not find a " + msgObject + " element at " +
        inDoc.getRootElement().getName() + "/" + DATA_AREA + "/" + dataAreaChild + "/" + msgObject +
        " in the " + msgObject + "-" + msgAction + "-" + msgAction + " Document passed in";

      ArrayList errors = logErrors("BRG-1005", errMessage, inDoc);
       String replyContents = buildReplyDocumentWithErrors(eControlArea, replyDoc, errors);
      return getMessage(msg, replyContents);
    }

    // if the msgAction is Update, we'll need to get the baseline object out of the document as well.
    Element eBaselineMessageObject = null;
    if (msgAction.equalsIgnoreCase(UPDATE_ACTION)) {
      // we need to create a baseline XEO also so we have to get the baseline element...
      eBaselineMessageObject = inDoc.getRootElement().
                      getChild(DATA_AREA).
                      getChild(BASELINE_DATA).
                      getChild(msgObject);

      if (eBaselineMessageObject == null) {
        // Error!
        errMessage = "Could not find a " + msgObject + " + element at " +
          DATA_AREA + "/" + BASELINE_DATA + "/" + msgObject +
          " in the "+msgObject+"-"+msgAction+"-" + msgAction + " Document passed in";

        ArrayList errors = logErrors("BRG-1010", errMessage, inDoc);
         String replyContents = buildReplyDocumentWithErrors(eControlArea, replyDoc, errors);
        return getMessage(msg, replyContents);
      }
    }

    // now, based on the message object name (and version?) execute the specific
    // business logic associated to that object...
    logger.info("Retrieving request logic executor for mapping name: '" + mappingName + "'.");
    String executorClassName = (String)m_executorMappings.get(mappingName);
    String executorName = msgObject;
    try {
      // instantiate that class
      logger.info("Instantiating executor: " + executorClassName);
      RequestLogicExecutor executor = (RequestLogicExecutor)Class.forName(executorClassName).newInstance();         
      try {
        PropertyConfig pConfig = (PropertyConfig)getAppConfig().getObject(msgObject + "Properties");
        executor.setProperties(pConfig.getProperties());
      }
      catch (Exception e) {
        logger.warn("No executor specific properties found for the " + executorName +
          " executor.  Using Command's 'GeneralProperties' properties.");
        executor.setProperties(getProperties());
      }
      executor.setExecutorAppConfig(getAppConfig());
      executor.setMessageObject(eMessageObject);
      executor.setBaseline(eBaselineMessageObject);
      executor.setMessageAction(msgAction);
      executor.setMessageObjectName(msgObject);
      executor.setMessageRelease(msgRelease);
      executor.setTestId(testId);
      executor.execute(replyDoc);
      String replyContents = buildReplyDocument(eControlArea, replyDoc);
      return getMessage(msg, replyContents);
    }
    catch (Exception e) {
      errMessage =
        "Exception occurred executing the business logic associated " +
        "to the " + msgObject + "-" + msgAction + "-" + msgType + " message consumed.  This error " +
        "occurred in the " + executorClassName + " class.  Exception was " + e.getMessage();

      logger.fatal(errMessage, e);

      ArrayList errors = logErrors("BRG-1015", errMessage, e, inDoc);
       String replyContents = buildReplyDocumentWithErrors(eControlArea, replyDoc, errors);
      return getMessage(msg, replyContents);
    }
  }
 
  /**
  * Based on the 'messageAction' associated to the request we determine which 'PrimedXmlDocument' associated
  * to the message object being operated on should be returned.  This is specified in the Command's
  * deployment descriptor on each message object.
  * <P>
  * For example, our command supports com.any-erp-vendor.Person/BasicPerson/1.0/Query-Request
  * and com.any-erp-vendor.Person/BasicPerson/1.0/Create-Request.  We know
  * based on the OpenEAI Message Protocol that the response to a com.any-erp-vendor.Person/BasicPerson/1.0/Query-Request is
  * com.any-erp-vendor.Person/BasicPerson/1.0/Provide-Reply.  We also know that the
  * response to a com.any-erp-vendor.Person/BasicPerson/1.0/Create-Request is a
  * org.openeai.CoreMessaging/1.0/Response-Reply with the appropriate status,
  * action and error information filled in.
  * <P>
  * Because of all this knowledge, we configure the message objects used by this Command to have the appropriate
  * 'PrimedXmlDocument' entries that allows us to simply get the appropriate reply document from the object
  * based on the action.  If the Action is 'Query' we'll be returning the 'primed' Provide document associated to the
  * object.  If the action is anything else, we'll be returning the 'primed' generic Response document associated to
  * the object.
  **/
  final protected Document getReplyDoc(String msgAction, String msgObject, String msgRelease) throws CommandException {

    try {
      XmlEnterpriseObject xeo = (XmlEnterpriseObject)getAppConfig().getObject(msgObject + "." + generateRelease(msgRelease));
      Document rDoc = null;
      if (msgAction.equalsIgnoreCase(QUERY_ACTION)) {
        rDoc = xeo.getProvideDoc();
      }
      else {
        rDoc = xeo.getResponseDoc();
      }

      if (rDoc == null) {
        logger.fatal("Could not find a reply document for the '" + msgObject + "-" + msgAction + "' request.");
        throw new CommandException("Could not find an appropriate reply document for the '" + msgObject + "-" + msgAction + " request.");
      }
      else {
        logger.info("Found a reply document for the '" + msgObject + "-" + msgAction + "' request.");
      }
      return rDoc;
    }
    catch (Exception e) {
      logger.fatal(e.getMessage(), e);
      throw new CommandException(e.getMessage(), e);
    }
  }

/* 
  final private ArrayList logErrors(String errNumber, String errMessage, Throwable e, Document inDoc) {
    logger.fatal(errMessage, e);
    logger.fatal("Message sent in is: \n" + getMessageBody(inDoc));
    ArrayList errors = new ArrayList();
    errors.add(buildError("application", errNumber, errMessage));
    return errors;
  }
 
  final private ArrayList logErrors(String errNumber, String errMessage, Document inDoc) {
    logger.fatal(errMessage);
    logger.fatal("Message sent in is: \n" + getMessageBody(inDoc));
    ArrayList errors = new ArrayList();
    errors.add(buildError("application", errNumber, errMessage));
    return errors;
  }
*/
}
TOP

Related Classes of org.any_openeai_enterprise.gateways.brg.BannerRequestCommand

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.