Sterling Managed File Transfer

Sterling Managed File Transfer

Come for answers, stay for best practices. All we're missing is you.

 View Only

Immediate Mailbox Message Routing in B2Bi 5.2.5 and SFG 2.2.5

By Tanvi Kakodkar posted Thu January 23, 2020 09:05 AM

  
Originally updated on July 6, 2015 by VinceTkac

Immediate Mailbox Message Routing in B2Bi 5.2.5 and SFG 2.2.5

Authors: Vince Tkac, B2B Architect, IBM and Philip Ross, Software Engineer, IBM

The 5.2.5 release included some under publicized user exits for mailbox.  A user exit is a way for customers/partners to link in custom code during certain pre-defined operations.  The new user exits in 5.2.5 are hooked into mailbox message add, delete and extract.  These user exits provide the ability to audit, validate file names, examine file content or refuse a message/transfer when messages are added, deleted or extracted from a mailbox.

Mailbox message routing today is done on a schedule.  Every minute (by default), a BP runs and issues a join of the mailbox message table with the mailbox routing rules table to get a list of messages that need to be routed (typically messages that arrived in the last 60 seconds).  This join isn't a cheap operation and the scheduler can only run a BP as often as every minute (there is a sub-minute BP that runs the routing rule every ten seconds but this still leaves a delay in processing). 

The mailbox message add user exit can also be used to trigger custom java code when a message arrives.  The java code can launch a BP just like the mailbox routing system does by calling the InitialWorkFlowContext.start() method.

To accomplish this, create a class that implements com.sterlingcommerce.woodstock.userexit.services.mailbox.interfaces.IMailboxUserExit_OnMessageAdd.  Let’s start by just having the user exit print a message so we can see it is being called.

package com.sterlingcommerce.woodstock.mailbox.userexit;
import java.util.List;
import java.util.Map;
import com.sterlingcommerce.woodstock.userexit.services.mailbox.interfaces.IMailboxUserExit_OnMessageAdd;
public class MailboxSyncUserExit implements IMailboxUserExit_OnMessageAdd {
   public boolean onMessageAdd(Map<String, Object> arg0, Map<String, Object> arg1) throws Exception {
      String name = (String) arg0.get(IMailboxUserExit_OnMessageAdd.KEY_MESSAGE_NAME);
      System.out.println("MyUserExit: received " + name);
      return true;  // Go ahead with add.
   }
}

Once we have the class written, compile it, add it to a jar and put that jar in the dynamicclasspath.cfg file so the system will find it.

Create a mailbox user exit xml file to tell the system about the user exit.  The files goes in install/properties/userexit and should be named MailboxUserExits.xml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="MailboxUserExitStore" class="com.sterlingcommerce.woodstock.userexit.services.mailbox.MailboxUserExitStore">
    <property name="userExits">
      <map>
        <entry key="com.sterlingcommerce.woodstock.userexit.services.mailbox.interfaces.IMailboxUserExit_OnMessageAdd">
          <ref bean="com.sterlingcommerce.woodstock.userexit.services.mailbox.interfaces.IMailboxUserExit_OnMessageAdd"/>
        </entry>
      </map>
    </property>
  </bean>
  <bean id="com.sterlingcommerce.woodstock.userexit.services.mailbox.interfaces.IMailboxUserExit_OnMessageAdd" class="com.sterlingcommerce.woodstock.userexit.services.mailbox.MailboxUserExit">
    <property name="implementations">
      <list>
        <value>com.sterlingcommerce.woodstock.mailbox.userexit.MailboxSyncUserExit</value>
      </list>
    </property>
    <property name="generalParameters">
      <props>
        <prop key="return.on.exception">false</prop>
        <prop key="pool.size">5</prop>
        <prop key="maximum.queue.length">10000</prop>
        <prop key="wait.time">10</prop>
        <prop key="execution.threshold.time">600000</prop>
      </props>
    </property>
  </bean>
</beans>

Start the system up and ftp a file into a mailbox.  You should immediately see the message from System.out.println in noapp.log.* in the install/logs directory.

Now that we have verified the user exit is being called, edit the user exit java code to call the BP and pass in the mailbox message ID. 


The modified user exit looks like:

package com.sterlingcommerce.woodstock.mailbox.userexit;

import java.util.List;
import java.util.Map;
import com.sterlingcommerce.woodstock.userexit.services.mailbox.interfaces.IMailboxUserExit_OnMessageAdd;
import com.sterlingcommerce.woodstock.workflow.InitialWorkFlowContext;
import com.sterlingcommerce.woodstock.workflow.InitialWorkFlowContextException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class MailboxSyncUserExit implements IMailboxUserExit_OnMessageAdd {
   public boolean onMessageAdd(Map<String, Object> arg0, Map<String, Object> arg1) throws Exception {
      try {
         return onMessageAddWrapped(arg0, arg1);
      } catch (Exception e) { e.printStackTrace(); }
      return(false);
   }

   private boolean onMessageAddWrapped(Map<String, Object> arg0, Map<String, Object> arg1) throws Exception {
      String name = (String) arg0.get(IMailboxUserExit_OnMessageAdd.KEY_MESSAGE_NAME);
      Long messageId     = (Long)loadPropObj("messageId", arg0, true);
      String mailboxPath = loadProp("mailboxPath", arg0, true);
      String userId      = loadProp("userId", arg0, true);
      String messageName = loadProp("messageName", arg0, true);
      String documentId  = loadProp("documentId", arg0, true);
      if(!mailboxPath.equals("/producer")) return true;
      InitialWorkFlowContext iwfc = new InitialWorkFlowContext();
      StringBuffer message = new StringBuffer("<Root>\n");
      message.append("<RoutingRequest>\n");
      message.append("\t<RoutingRequest>\n");
      message.append("\t\t<MessageId>" + messageId + "</MessageId>\n");
      message.append("\t</RoutingRequest>\n");
      message.append("</RoutingRequest>\n");
      message.append("</Root>\n");
      Element root = parseXmlString(message.toString());
      NodeList nodes = root.getChildNodes();
      for( int i=0; i<nodes.getLength(); i++) {
          Node node = nodes.item(i);
          if (node.getNodeType() == Node.ELEMENT_NODE)
              iwfc.addContentElement("", node, true);
      }
      iwfc.setWorkFlowName("mailboxImmediateRoute");
      iwfc.setInitiatorName("MyUserExit");
      iwfc.setUserToken("admin");
      iwfc.start();
      System.out.println("MyUserExit: received " + name);
      return true;  // Go ahead with add.
   }

   private String loadProp(String name, Map<String, Object> inargs, boolean required) {
      String str = (String) inargs.get(name);
      if ( (str == null) || str.isEmpty() ) {
         if (required) { throw new IllegalArgumentException("Expected parameter not specified " + name); }
      }
      return(str);
   }

   private Object loadPropObj(String name, Map<String, Object> inargs, boolean required) {
      Object obj = inargs.get(name);
      if ( (obj == null) ) {
         if (required) { throw new IllegalArgumentException("Expected parameter not specified " + name); }
      }
      return(obj);
   }

    private Element parseXmlString(String xml) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
            ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes("UTF-8"));
            org.w3c.dom.Document documentEl = documentBuilder.parse(bais);
            Element root = documentEl.getDocumentElement();
            return root;
        } catch (ParserConfigurationException pce) {
            throw new RuntimeException("Error setting up XML parser", pce);
        } catch (UnsupportedEncodingException uee) {
            throw new RuntimeException("Error setting up XML parser", uee);
        } catch (IOException ie) {
            throw new RuntimeException("Error parsing XML.", ie);
        } catch (SAXException se) {
            throw new RuntimeException("Error parsing XML.", se);
        }
    }
}

 

The target BP looks like:

<process name="mailboxImmediateRoute">
    <rule name="More Messages?">
        <condition>CurrentMessage &lt;= count(RoutingRequest/RoutingRequest/MessageId )</condition>
    </rule>
    <rule name="Started?">
        <condition>CurrentMessage &gt;= 1</condition>
    </rule>
    <sequence name="Outer Sequence">
        <choice name="ContinueOrStart">
            <select>
                <case ref="Started?" negative="true" activity="StartCount"/>
                <case ref="Started?" activity="IncrementCount"/>
            </select>
            <sequence name="StartCount">
                <assign to="CurrentMessage">1</assign>
            </sequence>
            <sequence name="IncrementCount">
                <assign to="CurrentMessage" from="CurrentMessage + 1"/>
            </sequence>
        </choice>
        <sequence name="Message Loop">
            <choice name="MoreMessages">
                <select>
                    <case ref="More Messages?" activity="Route Message"/>
                    <case ref="More Messages?" negative="true" activity="End Sequence"/>
                </select>
                <sequence name="Route Message">
                   
                    <operation name="Invoke SubProcess">
                        <participant name="InvokeSubProcessService" />
                        <output message="InvokeSubProcessServiceTypeInputMessage">
                            <assign to="INVOKE_MODE">ASYNC</assign>
                            <assign to="WFD_NAME">FileGatewayMailboxRouteArrivedFile</assign>
                        </output>
                        <input message="inmsg">
                            <assign to="." from="*" />
                        </input>
                    </operation>
                    <operation name="Release Service">
                        <participant name="ReleaseService" />
                        <output message="ReleaseServiceTypeInputMessage">
                            <assign to="." from="*" />
                            <assign to="TARGET">INVOKE_ID_LIST</assign>
                        </output>
                        <input message="inmsg">
                            <assign to="." from="*" />
                        </input>
                    </operation>
                    <assign to="CurrentMessage" from="CurrentMessage + 1"></assign>
                    <repeat name="NextMessage" ref="MoreMessages"/>
                </sequence>
                <sequence name="End Sequence">
                    <assign to="FinishedRouting">true</assign>
                </sequence>
            </choice>
        </sequence>
        <onFault>
            <sequence name="RepeatProcessing">
                <operation>
                    <participant name="WaitService"/>
                    <output message="WaitServiceInputMessage">
                        <assign to="WAIT_INTERVAL" from="1"/>
                        <assign to="." from="*"></assign>
                    </output>
                    <input message="inmsg">
                        <assign to="." from="*"></assign>
                    </input>
                </operation>
                <repeat ref="Outer Sequence"/>
            </sequence>
        </onFault>
    </sequence>
</process>


The BP is responsible for extracting the mailbox message by calling the mailbox extract begin service.

Do not associate an automatic routing rule with files in mailboxes where this user exit is being used or there can be a race condition between the immediate routing and scheduled routing.

The user exit can be further modified to put a "route" message onto an external JMS queue and have listeners in various JVMs pull from that queue.  This would allow load balancing as well as immediate routing.

Sample Files

 


#DataExchange
#IBMSterlingB2BIntegratorandIBMSterlingFileGatewayDevelopers
0 comments
15 views

Permalink