Additional Blog Posts by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Read only
Former Member
878 Views
5 Comments
0 Likes

The ReQuIrEmEnT

The business requirement is to read data from a database table of an external system, map and route in XI, and send to an SAP system (as IDoc, but this is really meaningless).

As you can see, what is done it's simply to explode the content of the xmlfield node, so that in the mapping that takes place afterwards in the XI Mapping Runtime, nodes under xmlfield can be treated as real nodes rather than a single string, which is not convenient.



The SoLuTiOn

An advanced mapping program (such as Java, ABAP or XSLT) could have been used to achieve the result, but unfortunately that means writing a different mapping program for each interface, as both source document (basically database table fields) and target document could have different requirements, and it's hard to pass easily maintainable parameters to a mapping program.





The solution is thus packaged into an additional module, developed as Enteprise Java Bean, that is added in the module chain of the JDBC Sender Adapter – before the standard localejbs/CallSapAdapter - and which support its own parameters.

(Once again, I won't cover here the details of writing adapter modules... I've already given detailed info in other blogs o'mine, and more you'll find in this wonderful SAP howto).





Take the code below and put it in an EJB (see recommendations above). Sorry for some Italian here and there... Exercise for you: can you guess the meaning? 😛




package com.guarneri.xi.afw.modules;

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

import com.sap.engine.lib.xml.util.DOMSerializer;

// XML manipulation imports

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.*;

// Other imports

import java.util.Date;

import java.util.Hashtable;

import java.io.*;

import org.apache.commons.lang.StringEscapeUtils;

/**

  • @ejbHome <{com.guarneri.xi.afw.modules.JDBCxmlHome}>

  • @ejbLocal <{com.guarneri.xi.afw.modules.JDBCxmlLocal}>

  • @ejbLocalHome <{com.guarneri.xi.afw.modules.JDBCxmlLocalHome}>

  • @ejbRemote <{com.guarneri.xi.afw.modules.JDBCxml}>

  • @stateless

  • @transactionType Container

*/

// Module name to use in the adapter:

// localejbs/sap.com/com.guarneri.xi.afw.modules/JDBCxmlBean

public class JDBCxmlBean implements SessionBean {

     private final String auditStr = "guarneri.com/JDBCxmlBean - ";

     // Channel Module Parameters     

          private String srcXmlField = "DES_MSG", // Source XML Field to be read (main node)

          srcXmlEscaped = "true", // Source XML field is HTML escaped? If false, CDATA section should be used

          srcKeyField = "SEQ_MSG", // Source key field that will be inserted in each extracted XML

          trgNs = "", // Target message namespace 

     trgRootElement = "resultset"; // Target document root element (e.g. message type)

     AuditMessageKey amk = null; // Needed in order to write out on the message audit log

     private int processedRows = 0;

     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

          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");

          // Read of module parameters

          // if (mp!=null) {

          if (mpget("source.XmlField") != null)

               srcXmlField = mpget("source.XmlField");

          if (mpget("source.XmlEscaped") != null)

               srcXmlEscaped = mpget("source.XmlEscaped");

          if (mpget("source.KeyField") != null)

               srcKeyField = mpget("source.KeyField");

          if (mpget("target.namespace") != null)

               trgNs = mpget("target.namespace");

          if (mpget("target.RootElement") != null)

               trgRootElement = mpget("target.RootElement");

          // } else {

          //      Audit.addAuditLogEntry(amk,AuditLogStatus.WARNING, auditStr + "Module parameters could not be found");

          // }

          // Extraction of message payload

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

          // Payload manipulation

          ByteArrayOutputStream baos = new ByteArrayOutputStream();

          try {

               extractCLOB(new ByteArrayInputStream(payload), baos);

          } catch (Exception e) {

               Audit.addAuditLogEntry(amk, AuditLogStatus.ERROR, auditStr + "Error while extracting CLOB content - " + e.getMessage());

               throw mEx = new ModuleException(auditStr + "Error while extracting CLOB content - " + e.getMessage());

          }

          // New payload insertion

          try {

               XMLPayload newPayload = msg.getDocument();

               newPayload.setContent(baos.toByteArray());

               newPayload.setContentType("text/xml");

               newPayload.setVersion("1.0");

               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 - " + processedRows + " table rows " + "(execution " + (dend.getTime() - dstart.getTime()) + " ms)");

          return inputModuleData;

     }

     /**

     

  • -----------------------------------------------------

     

  • Extraction of CLOB field content read by JDBC adapter

     

  • -----------------------------------------------------

      */

     private void extractCLOB(InputStream in, OutputStream out) throws Exception {

          Document doc = null;

          Document clob = null;

          Document docOut = null;

          Element root = null;

          Exception ex = null;

          // Creation of input Document via DOM

          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

          DocumentBuilder builder = null;

          factory.setNamespaceAware(true);

          factory.setValidating(false);

          try {

               builder = factory.newDocumentBuilder();

          } catch (Exception e) {

               throw ex = new Exception("Eccezione durante creazione DocumentBuilder - " + e.getMessage());

          }

          try {

               doc = builder.parse(in);

          } catch (Exception e) {

               throw ex = new Exception("Eccezione durante parsing documento XML in ingresso - " + e.getMessage());

          }

          // Preparing output document via DOM

          docOut = builder.newDocument();

          root = (Element) docOut.createElement(trgRootElement);

          docOut.appendChild(root);

          // Main part: loop on resultset (table rows issued by JDBC Adpater) and completion of output document

          NodeList resultset = doc.getElementsByTagName("row");

          Element clobNode = null, keyNode = null;

          processedRows = resultset.getLength();

          for (int i = 0; i < resultset.getLength(); i++) {

               NodeList rowfields = resultset.item(i).getChildNodes();

               for (int j = 0; j < rowfields.getLength(); j++)

                    // Search of XML-containing element

                    if (rowfields.item(j).getNodeName().equalsIgnoreCase(srcXmlField))

                         clobNode = (Element) rowfields.item(j);

               // Search of keyfield element     

               else if (rowfields.item(j).getNodeName().equalsIgnoreCase(srcKeyField))

                    keyNode = (Element) rowfields.item(j);

               if (clobNode != null) {

                    try {

                         // XML document as string

                         String clobContent = clobNode.getChildNodes().item(0).getNodeValue();

                         // Is XML string to be unescaped?

                         if (srcXmlEscaped.equalsIgnoreCase("true") || srcXmlEscaped.equalsIgnoreCase("yes"))

                              clobContent = StringEscapeUtils.unescapeXml(clobContent.toString());

                         // String conversion to inputStream for creation of a new document via DOM

                         clob = builder.parse(new ByteArrayInputStream(clobContent.getBytes()));

                         // Add of keyfield

                         if (keyNode != null && !trgRootElement.equalsIgnoreCase("resultset"))

                              clob.getFirstChild().appendChild(clob.importNode(keyNode, true));

                    } catch (Exception e) {

                         throw ex = new Exception("Eccezione durante parsing documento XML singolo clob - " + e.getMessage());

                    }

                    root.appendChild(docOut.importNode(clob.getFirstChild(), true));

                    Node clobNewNode = clobNode.replaceChild(doc.importNode(clob.getFirstChild(), true), clobNode.getFirstChild());

               } else {

                    root.appendChild(docOut.createComment("No srcXmlField was found in the processed message"));

               }

          }

          try {

               // The original document is returned, with "exploded" xmlfield

               if (trgRootElement.equalsIgnoreCase("resultset"))

                    // out.write(doc.toString().getBytes());

                    out.write(deleteLF(doc.toString().getBytes()));

                    // serializer.serialize(doc);

               // A new document is returned, with "exploded" xmlfield and required target parameters     

               else

                    // out.write(docOut.toString().getBytes());

                    out.write(deleteLF(docOut.toString().getBytes()));

                    // serializer.serialize(doc);

          } catch (Exception e) {

               throw ex = new Exception("Eccezione durante scrittura su output stream - " + e.getMessage());

          }

     }

     private byte[] deleteLF(byte src[]) {

          byte buf[] = new byte[2 * src.length];

          int actualCount = 0;

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

               if (src[i] == 10) {

                    buf[actualCount] = 32;

                    actualCount += 1;

               } else {

                    buf[actualCount++] = src[i];

               }

          }

          byte dst[] = new byte[actualCount];

          System.arraycopy(buf, 0, dst, 0, actualCount);

          return dst;

     }

     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 {

     }

}






The module parameters are explained in the table below.




Just one thing more: if the source field it's not escaped, so it's assumed to be a CDATA node!

5 Comments