Skip to Content
Author's profile photo Sandeep Maurya

Adapter module: Set the file name from Payload using Regular Expression (Regex).

Hi friends,

I got a business requirement where SAP ECC is sending data using outbound ABAP Proxy to PI and PI is receiving the same and creating the file at FTP/SFTP server (Proxy to file scenario).  File name is supplied in the payload which is coming from Sender SAP system. To set filename in Dynamic Configuration (ASMA), I used XSLT mapping (and there were few transformations also). Variable Substitution didn’t work for me as the file name was not available until Receiver File/SFTP adapter (during transformation it was ignored, it was our business requirement)

I got to develop many interfaces which using the above mentioned flow, and I was writing the same XSLT code (to set the file name in dynamic configuration) in all the mapping so I decided to write the adapter module which perform the same task that XSLT code was doing. Once Adapter module is deployed, it can be used in any Communication Channel and any team member can reuse it just by supplying necessary configuration parameters.

This adapter module is developed using Enterprise Java Beans (EJB) 3.0. It expects the File Name XPath to be provided in adapter configuration. This XPath is used to get the file using regular expression (regex). The same implementation could be done using DOM parser too but DOM parser was performing slower for large files, so I am using regex which is better option as it is native to java.

This adapter module can be used in Sender or Receiver communication channel before “CallSapAdapter” module. I am using it in SOAP (XI3.0) ABAP Proxy Communication Channel.

AD.png

Below is the java code for adapter module: As I am using EJB3.0, it is not necessary to implement Session bean. Only Module interface is required to implement.

Method getRegexFromXPath() returns the regular expression which is used in Bean class to match the pattern and find out the file name.

package com.sap.pi.adapter.module;

import java.io.InputStream;

import java.io.PrintWriter;

import java.io.StringWriter;

import javax.annotation.PostConstruct;

import javax.annotation.PreDestroy;

import javax.ejb.Local;

import javax.ejb.LocalHome;

import javax.ejb.Remote;

import javax.ejb.RemoteHome;

import javax.ejb.Stateless;

import com.sap.aii.af.lib.mp.module.Module;

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

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

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

import com.sap.aii.af.lib.mp.module.ModuleHome;

import com.sap.aii.af.lib.mp.module.ModuleLocal;

import com.sap.aii.af.lib.mp.module.ModuleLocalHome;

import com.sap.aii.af.lib.mp.module.ModuleRemote;

import com.sap.engine.interfaces.messaging.api.Message;

import com.sap.engine.interfaces.messaging.api.MessageKey;

import com.sap.engine.interfaces.messaging.api.MessagePropertyKey;

import com.sap.engine.interfaces.messaging.api.PublicAPIAccessFactory;

import com.sap.engine.interfaces.messaging.api.XMLPayload;

import com.sap.engine.interfaces.messaging.api.auditlog.AuditAccess;

import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;

import com.sap.engine.interfaces.messaging.api.exception.MessagingException;

/**

* Session Bean implementation class FileSetProperties

*/

@Stateless(name = “FileSetPropertiesBean”)

@Local(value = { ModuleLocal.class })

@Remote(value = { ModuleRemote.class })

@LocalHome(value = ModuleLocalHome.class)

@RemoteHome(value = ModuleHome.class)

public class FileSetProperties implements Module {

      private AuditAccess audit;

      public FileSetProperties() {

      }

      @Override

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

            FunctionsLibrary functionLibrary = new FunctionsLibrary();

            Object obj = null;

            Message msg = null;

            MessageKey msgKey = null;

            String fileName = “”;

            String fileXPath = “FileXPath”;

            try {

                  obj = inputModuleData.getPrincipalData();

                  msg = (Message) obj;

                  msgKey = new MessageKey(msg.getMessageId(), msg.getMessageDirection());

                  addLog(msgKey, AuditLogStatus.SUCCESS, “***** Entering in FileSetProperties adapter module ******”);

                  MessagePropertyKey fileKey = new MessagePropertyKey(“FileName”, http://sap.com/xi/XI/System/File);

                  XMLPayload xmlPayload = msg.getDocument();

                  InputStream inputStream = xmlPayload.getInputStream();

                  String inString = functionLibrary.inputStreamToString(inputStream);

                  String regex = functionLibrary.getRegexFromXPath(moduleContext.getContextData(fileXPath));

                  addLog(msgKey, AuditLogStatus.SUCCESS, “Regular Expression: “ + regex + ” Input message parsing is done.”);

//                addLog(msgKey, AuditLogStatus.SUCCESS, “Input message parsing is done. “);

                  if (inString.matches(regex)) {

                        fileName = inString.replaceAll(regex, “$” + functionLibrary.getRegexReplacementCount());

                        addLog(msgKey, AuditLogStatus.SUCCESS, “Supplied XPath in Communication Challel is: “ + moduleContext.getContextData(fileXPath) + “. “ + “File name from payload is: “ + fileName + ” Going to set this file name in ASMA”);                  

                     

                        inputModuleData.setPrincipalData(msg);

                     

                  } else {

                        addLog(msgKey, AuditLogStatus.WARNING, “Input message does not matches with regex . “ + regex);

                     

                        fileName = functionLibrary.getDefaultFileName();

                        addLog(msgKey, AuditLogStatus.SUCCESS, “Using default file Name “ + fileName);

                  }

                  if (!“”.equals(fileName)) {

                        msg.setMessageProperty(fileKey, fileName);

                        addLog(msgKey, AuditLogStatus.SUCCESS, “File name has been set in ASMA. You can access it from Dynamic Configuration.” );

                        addLog(msgKey, AuditLogStatus.SUCCESS, “***** Exiting from FileSetProperties adapter module ******”);

                  }

               

            } catch (Exception e) {

                  // e.printStackTrace();

                  addLog(msgKey, AuditLogStatus.ERROR, e.getMessage().toString());

                  addLog(msgKey, AuditLogStatus.ERROR, “***** Error occured. Exiting from FileSetProperties adapter module ****** “);

                  try {

                        handleException(msgKey, e);

                  } catch (Exception e1) {

                  }

            }

         

            //addLog(msgKey, AuditLogStatus.SUCCESS, “***** Exiting from FileSetProperties adapter module ******”);

            return inputModuleData;

      }

      @PostConstruct

      public void initialiseResource() {

            try {

                  audit = PublicAPIAccessFactory.getPublicAPIAccess().getAuditAccess();

            } catch (MessagingException e) {

                  System.out.println(“WARNING: Audit log not available in standalone testing”);

            }

      }

      @PreDestroy

      public void ReleaseResource() {

      }

      private void addLog(MessageKey msgKey, AuditLogStatus status, String message) {

            if (audit != null) {

                  audit.addAuditLogEntry(msgKey, status, message);

            } else {

                  System.out.println(“Audit Log: “ + message);

            }

      }

      private void handleException(MessageKey msgKey, Exception e) throws Exception {

            // throw new Exception(comment + ” ” + getStackTraceAsString(e), e);

            addLog(msgKey, AuditLogStatus.ERROR, getStackTraceAsString(e));

      }

      public String getStackTraceAsString(Exception e) {

            StringWriter sw = new StringWriter();

            e.printStackTrace(new PrintWriter(sw));

            String stacktrace = sw.toString();

            return stacktrace;

      }

}

FileSetProperties class uses getRegexFromXPath()  which is written in FunctionsLibrary class.

package com.sap.pi.adapter.module;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.UnsupportedEncodingException;

import java.text.Format;

import java.text.SimpleDateFormat;

import java.util.Date;

public class FunctionsLibrary {

      int replacementCount = 3;

      public String getRegexFromXPath(String XPath) {

            StringBuffer sb = new StringBuffer();

            try {

                  if (XPath != null) {

                        XPath = XPath.replace(“//”, “”);

                        if (XPath.length() > 0) {

                              sb.append(“(?s)”);

                              String arg[] = XPath.split(“/”);

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

                                    if (arg[i].length() > 0) {

                                          sb.append(“(.*?)(“);

                                          sb.append(arg[i]).append(“)”);

                                          replacementCount = replacementCount + 2;

                                          if (i == arg.length – 1) {

                                                sb.append(“(.*?)(>)(.*?)(<)(.*?)(“).append(arg[i]).append(“>)(.*)”);

                                          }

                                    }

                              }

                        } else {

                        }

                  }                

            } catch (Exception e) {

            }

            return sb.toString();

      }

      public int getRegexReplacementCount() {

            return this.replacementCount;

      }

      public String inputStreamToString(InputStream in) {

            // read in stream into string.

            StringBuffer buf = new StringBuffer();

            try {

                  InputStreamReader isr = null;

                  // try UTF-8 conversion

                  try {

                        isr = new InputStreamReader(in, “UTF-8”);

                  } catch (UnsupportedEncodingException e) {

                        // or atleast in natural encoding

                        isr = new InputStreamReader(in);

                  }

                  int c = 0;

                  while ((c = isr.read()) != -1) {

                        buf.append((char) c);

                  }

                  in.close();

                  isr.close();

            } catch (IOException e) {

                  e.printStackTrace();

            }

            return buf.toString();

      }

      public String getDefaultFileName() {

            Format formatter = new SimpleDateFormat(“yyyyMMdd-hhmmss”);

            Date d = new Date();

            String fileName = “Unknown_” + formatter.format(d) + “.xml”;

            return fileName;

      }

      public static void main(String[] args) throws Exception {

            // TODO Auto-generated method stub

            String XPath = “//text/text1/FileName”;

            FunctionsLibrary functionLibrary = new FunctionsLibrary();

            System.out.println(functionLibrary.getRegexFromXPath(XPath));

            // System.out.println(functionLibrary.getDefaultFileName());

            System.out.println(functionLibrary.getRegexReplacementCount());

      }

}

Use FileSetPropertiesBean in ejb-j2ee-engine.xml  and assign JNDI name. JNDI name is used in Communication Channel in Module tab.

<?xml version=“1.0” encoding=“UTF-8”?>

<ejb-j2ee-engine xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xsi:noNamespaceSchemaLocation=“ejb-j2ee-engine_3_0.xsd”>

      <enterprise-beans>

            <enterprise-bean>

                  <ejb-name>FileSetPropertiesBean</ejb-name>

                  <jndi-name>FileSetProperties</jndi-name>

            </enterprise-bean>

      </enterprise-beans>

</ejb-j2ee-engine>

Edit application-j2ee-engine.xml in EAR project and use the below xml

<?xml version=“1.0” encoding=“UTF-8”?>

<application-j2ee-engine xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xsi:noNamespaceSchemaLocation=“application-j2ee-engine.xsd”>

      <reference reference-type=“hard”>

            <reference-target target-type=“service” provider-name=“sap.com”>engine.security.facade</reference-target>

      </reference>

      <reference reference-type=“hard”>

            <reference-target target-type=“library” provider-name=“sap.com”>engine.j2ee14.facade</reference-target>

      </reference>

            <reference reference-type=“hard”>

            <reference-target target-type=“service” provider-name=“sap.com”>com.sap.aii.af.svc.facade</reference-target>

      </reference>

            <reference reference-type=“hard”>

            <reference-target target-type=“interface” provider-name=“sap.com”>com.sap.aii.af.ifc.facade</reference-target>

      </reference>

            <reference reference-type=“hard”>

            <reference-target target-type=“library” provider-name=“sap.com”>com.sap.aii.af.lib.facade</reference-target>

      </reference>

            <reference reference-type=“hard”>

            <reference-target target-type=“library” provider-name=“sap.com”>com.sap.base.technology.facade</reference-target>

      </reference>

            <fail-over-enable xsi:type=“fail-over-enableType_disable”

                  mode=“disable” />

</application-j2ee-engine>

Assigned Tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Eng Swee Yeoh
      Eng Swee Yeoh

      Hi Sandeep

      Can you provide some sample test data and testing results? And also maybe elaborate on how the getRegexFromXPath works? At the moment it's hard to visualize the use case for this module and how the regex part works.

      Rgds

      Eng Swee

      Author's profile photo Sandeep Maurya
      Sandeep Maurya
      Blog Post Author

      Hi Eng Swee,

      Below is Sample XML, which is input to the Adapter module.

      <?xml version="1.0" encoding="utf-8"?>

      <Data>

        <ArticleInfo>

        <Data>Article1</Data>

        <Data>Article2</Data>

        <Data>Article3</Data>

        </ArticleInfo>

        <FileName>List_Invoice_2015.txt</FileName>

      </Data>

      In this example, we are creating file using file name List_Invoice_2015.txt.

      XPath supplied in Communication channel: //FileName

      getRegexFromXPath will return:  (?s)(.*?)(FileName)(.*?)(>)(.*?)(<)(.*?)(FileName>)(.*)

      It is regular expression to parse the input data.

      There is another method:  getRegexReplacementCount(), it gives the counter to replace the string which is used in replaceAll method.

      Output of replaceAll is fileName which we are setting in Dynamic Configuration.

      You need to use "Adapter Specific Message Attributes" and "FileName" option in FIle/SFTP adapter.

      Please let me know if you have any question.

      Thanks,

      Sandeep Maurya

      Author's profile photo Sameer Mohammed
      Sameer Mohammed

      Hi Sandeep,

      This is a great blog especially when we want to control the file name creation on the source side.

      Regards,

      Sameer

      Author's profile photo Sandeep Maurya
      Sandeep Maurya
      Blog Post Author

      Thanks Sameer.

      Author's profile photo Former Member
      Former Member

      Sandeep,

      Many thanks for sharing this information!

      Regards,

      Kirk

      Author's profile photo Sandeep Maurya
      Sandeep Maurya
      Blog Post Author

      Thanks Kirk.

      Author's profile photo Prabhu Palanisamy
      Prabhu Palanisamy

      Nice work.

      Have you looked at this -> Dynamic File Name usign adapter specific message attribute

      Author's profile photo Sandeep Maurya
      Sandeep Maurya
      Blog Post Author

      Hi Prabhu,

      You can not use this Adapter module for above mentioned thread because,

      1. Your file name is custom generated, <WareHouseNumber_Timestamp>_WO.<EXT> . This Adapter module expects file name in incoming payload and provide XPath of file name.

      2. I wont suggest you to write adapter module for specific scenario as it requires more development effort than writing java mapping.

      Follw the steps suggested by Veerendra Kumar Mamidi.

      Please let me know if you have any questions.

      Thanks,

      Sandeep Maurya