Skip to Content

Imagine you have to read with XI flat lines by means of a JMS adapter.

The content of the JMS message thus is a set of flat lines that you

must probably transform into suitable XML format (with the

localejbs/AF_Modules/MessageTransformBean module) just before sending

to Integration Server for routing, mapping etc.

Using the lastFieldsOptional parameter enables you to avoid problems if
the external application stops sending bytes as soon as no more data
are present. And now we go straight to the problem.

The problem<br />

In my case the legacy system is not only trimming the line as soon as no more data are present, but even the last field is trimmed. Let’s clarify.

REC1 is defined as FIELD1, FIELD2, FIELD3, FIELD4 with respective lengths 10,10,20,20

Suppose the legacy put in the queue something like this:

REC1FIELD1xxxxFIELD2xxxxFIELD3xxxxxxxxxxxxxx(EOL)

In this case FIELD4 is not present, but the lastFieldsOptional parameter saves us from a bad Java exception.

Now suppose the legacy put in the queue somthing like this:

REC1FIELD1xxxxFIELD2xxxxFIELD3xxxx(EOL)

Here we have a serious problem, because FIELD3 will not be read by the MessageTransformBean
module, and so will not be present in the resulting XML document. I
guess that’s because the field is too short (10) compared to the
declared length (20), and we cannot put the blame on it! So here comes the solution.

The solution

After pulling my hair for a while with appearently non-existing

additional undocumented parameters for the MessageTransformBean module,

I resolved that a good custom module could have be written and put in

the channel in order to extend the module chain. In this weblog I won’t

concentrate on the details concerning custom module development (take a

look at this howto), but I will just mention that I used the

great SAP NetWeaver Developer Studio, which allowed me to create EJB

project, EA project, properly set descriptor xml files, bundle

everything together and deploy to J2EE… everything in a bunch of

minutes!

If you are wondering +why I don’t calculate the total length of the

given record to be padded+ by summing the values of

xml.REC1.fieldFixedLengths, here is the answer: inside my module code,

only my module context is accessible through a matching instance of

ModuleContext class, so that I’m completely blind about other modules

configuration. Actually I’m pretty sure there’s a way to do it, but I

guess not without some hacking I don’t mean to do. 🙂

The code

package com.guarneri.xi.afw.modules.jms;

// Standard ejb imports

import javax.ejb.SessionBean;

import javax.ejb.SessionContext;

import javax.ejb.CreateException;

// XI specific imports

import com.sap.aii.af.mp.module.ModuleContext;

import com.sap.aii.af.mp.module.ModuleData;

import com.sap.aii.af.mp.module.ModuleException;

import com.sap.aii.af.ra.ms.api.*;

import com.sap.aii.af.service.auditlog.*;

// Other imports

import java.util.*;

/**

  • @ejbLocal <{com.benetton.xi.afw.modules.jms.JMSPadderLocal}>

  • @ejbLocalHome <{com.benetton.xi.afw.modules.jms.JMSPadderLocalHome}>

  • @stateless

*/

public class JMSPadderBean implements SessionBean {

     private final String AUDITSTR = “guarneri.com/JMSPadder – “;

     private final String MP_PREFIX = “jmspad.”;

     private final String LAST_CHAR_HEX = “lastCharHex”;

     private final String CARRIAGE_RETURN = “0C”;

     private char crlf;

     private ModuleContext mc;

     public ModuleData process(ModuleContext moduleContext, ModuleData inputModuleData) throws ModuleException {

          Object obj = null; // Handler to get Principle data

          Message msg = null; // Handler to get Message object

          Hashtable mp = null; // Module parameters

          AuditMessageKey amk = null;

          // Needed in order to write out on the message audit log

          ModuleException mEx = null;

          Date dstart = new Date();

          // Creation of basic instances

          try {

               obj = inputModuleData.getPrincipalData();

               msg = (Message) obj;

               amk = new AuditMessageKey(msg.getMessageId(), AuditDirection.OUTBOUND);

               mp = (Hashtable) inputModuleData.getSupplementalData(“module.parameters”);

               mc = moduleContext;

          } catch (Exception e) {

               Audit.addAuditLogEntry(amk, AuditLogStatus.ERROR, AUDITSTR + “Error while creating basic instances (obj,msg,amk,mp)”);

               throw mEx = new ModuleException(AUDITSTR + “Error while creating basic instances (obj,msg,amk,mp)”);

          }

          Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, AUDITSTR + “Process started”);

          // Main process –


BEGIN

          // What is the record (line) separator?

          String lastCh = mpget(MP_PREFIX + LAST_CHAR_HEX);

          if (lastCh == null)

               lastCh = CARRIAGE_RETURN;

          crlf = (char) Integer.parseInt(lastCh, 16);

          // Collect records to be processedand relevant total lengths

          Vector vctRec = new Vector();

          Vector vctRecL = new Vector();

          Enumeration mcKeys = mc.getContextDataKeys();

          while (mcKeys.hasMoreElements()) {

               String currKey = (String) mcKeys.nextElement();

               if (currKey.indexOf(LAST_CHAR_HEX) == -1 && currKey.indexOf(“module.key”) == -1) {

                    vctRec.add(currKey.substring(currKey.indexOf(“.”) + 1, currKey.length()));

                    vctRecL.add(mpget(currKey));

               }

          }

          // We have at least one record type to process

          if (vctRec.size() > 0) {

               byte[] baPayload = msg.getDocument().getContent();

               Vector lines = new Vector();

               StringBuffer line = new StringBuffer();

               try {

                    for (int i = 0; i < baPayload.length; i++) {

                         char currentChar = (char) baPayload[i];

                         if (currentChar != crlf)

                              line.append(currentChar);

                         else {

                              lines.add(line.toString());

                              line = new StringBuffer();

                         }

                    }

               } catch (Exception e1) {

                    throw new ModuleException(e1.toString() + e1.getMessage());

               }

               String strPayload = new String();

               try {

                    // And we go for padding

                    for (int i = 0; i < lines.size(); i++) {

                         String currLine = (String) lines.get(i);

                         for (int j = 0; j < vctRec.size(); j++) {

                              if (currLine.startsWith((String) vctRec.get(j))) {

                                   Integer RecL = new Integer((String) vctRecL.get(j));

                                   int npad = RecL.intValue() – currLine.length();

                                   for (int k = 0; k < npad; k++) {

                                        currLine += ” “;

                                   }

                              }

                         }

                         // Assemble back single line (padded) to full message content

                         strPayload += currLine + crlf;

                    }

               } catch (Exception e2) {

                    throw new ModuleException(e2.toString() + e2.getMessage());

               }

               // New payload insertion

               try {

                    XMLPayload newPayload = msg.getDocument();

                    newPayload.setContent(strPayload.getBytes());

                    msg.setDocument(newPayload);

                    inputModuleData.setPrincipalData(msg);

               } catch (Exception e) {

                    Audit.addAuditLogEntry(amk, AuditLogStatus.ERROR, AUDITSTR + “Error while inserting new payload”);

                    throw mEx = new ModuleException(AUDITSTR + “Error while inserting new payload”);

               }

               // Return of manipulated message

               Date dend = new Date();

               Audit.addAuditLogEntry(

                    amk,

                    AuditLogStatus.SUCCESS,

                    AUDITSTR + “Process completed ” + “(execution ” + (dend.getTime() – dstart.getTime()) + ” ms)”);

          }

          //          Main process –


END

          return inputModuleData;

     }

     private String mpget(String pname) {

          return (String) mc.getContextData(pname);

     }

     public void ejbRemove() {

     }

     public void ejbActivate() {

     }

     public void ejbPassivate() {

     }

     public void setSessionContext(SessionContext context) {

          myContext = context;

     }

     private SessionContext myContext;

     /**

     

  • Create Method.

      */

     public void ejbCreate() throws CreateException {

          // TODO : Implement

     }

}

</textarea>

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply