SAP PI/PO : Implementing A Complex Scenario Without Netweaver BPMN
Through this demo I would like to show that majority of the complex scenarios in SAP PI/PO can be implemented without a Netweaver BPMN. However this requires good knowledge of Java Mapping.
In this demo I have used a java mapping to achieve the complex functionality instead of Netweaver BPMN. Hopefully this demo helps resources who are comfortable in java mapping.
Below is the flow of complex scenario :
Steps Implemented In Java Map:
1) Have attached the java map for your reference. I am making an effort to explain the steps I did in the java map to achieve the above functionality.
2) The first point I would like to highlight in the java map is that I have declared “transform” method as synchronized.
Normally this method will be as below :
public void transform(TransformationInput arg0, TransformationOutput arg1) throws StreamTransformationException {}
In this scenario I have added “synchronized” keyword to this method .The reason being in real time or production multiple files will be coming at the same time .So adding “synchronized” keyword ensures that all files processing is in sequence.
public synchronized void transform(TransformationInput arg0, TransformationOutput arg1) throws StreamTransformationException {}
3) Next step in the java map is to create the trace instance.This helps in debugging during development and testing in UAT.One needs to comment these lines before moving to the production.
trace = this.getTrace();
4) Next step is to create DOM parser instance.Input stream is passed as parameter for the Dom parser.
//create instance of document builder factory
DocumentBuilderFactory domBuilderFactory=DocumentBuilderFactory.newInstance();
//from document builder factory build dom builder.
DocumentBuilder domBuilder=domBuilderFactory.newDocumentBuilder();
//from document builder,parse the input stream
Document dom=domBuilder.parse(arg0.getInputPayload().getInputStream());
5)Next step is to set the value of class variable “ isSales=NO” . After reading each file ,based on the file type this value will be set.
6)Next step is to call the method “traversingXML” . To this method dom instance is passed as parameter.This method is recursive method i.e., calls itself until the entire xml payload is read.
String result=traversingXML(dom);
The objective of this method is to determine the type of file :
6.1) Sales file will contain node names as “RetailTransaction” or “WN:BSCMTransaction” or “ControlTransaction”. Once these node names are found set the variable isSales=”YES”,msgType=”SALES”.
if(nodeName.equals(“RetailTransaction”)||nodeName.equals(“WN:BSCMTransaction”)||nodeName.equals(“ControlTransaction”)){
isSales=”YES”; msgType=”SALES”; }
6.2) SOQ file will contain node names as “SOQ” Once these node names are found set the variable isSales=”NO”,msgType=”SOQ”.
else if(nodeName.equals(“SOQ”)){
isSales=”NO”;
msgType=”SOQ”;
}
6.3) EODConfirmation file will contain node names as “EODConfirmation.Once these node names are found set the variable isSales=”NO”,msgType=”inventorycount”.
else if(nodeName.equals(“EODConfirmation”)){
isSales=”NO”;
msgType=”inventorycount”;
}
6.4) Other files contain nodevalues as “goodsreceipt” or “poreturn” or etc… .
7) Now the file is determined ,next step is to place the file in appropriate folder:
Here dynamic configuration parameters are used.
If the file is sales file then directory is set as “poslogsales”
If the file not sales ,then the transaction in file is checked for duplicity.This is done by calling RFC.
//RFC call to check the Transaction duplicity.
String returnValue = performRFCLookup(bDate, fileName, bTime, seuquenceNo, storeNo, workstationId,SERVICE,CHANNEL_NAME,messageType);
If the RFC returns “4” then it means transaction is duplicate and file is placed in duplicate folder.
else if(returnValue.equals(“4”)){
directory=”/”+posDir+”/”+dupDir; }
If the RFC returns “0” then it means transaction is not duplicate and file is placed in respective folder.
if(returnValue.equals(“0”)){
if(msgType.equals(“goodsreceipt”)){
directory=”/”+posDir+”/goodsreceipt”;
} }
Configuration Screenshot:
Below is the complete Java Code :
package iris_call_rfc_removeduplicatefiles;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.DynamicConfiguration;
import com.sap.aii.mapping.api.DynamicConfigurationKey;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
import com.sap.aii.mapping.lookup.LookupService;
public class RemoveDuplicateFiles_Through_RFC_Call_JavaMap extends AbstractTransformation {
public StringBuffer endResult = new StringBuffer();
public String isSales=””;
public String msgType=””;
public String msgFct=””;
public String inputFileName=””;
// Enable tracing
private static com.sap.aii.mapping.api.AbstractTrace trace = null;
public synchronized void transform(TransformationInput arg0, TransformationOutput arg1)
throws StreamTransformationException {
try
{
trace = this.getTrace();
//create instance of document builder factory
DocumentBuilderFactory domBuilderFactory=DocumentBuilderFactory.newInstance();
//from document builder factory build dom builder.
DocumentBuilder domBuilder=domBuilderFactory.newDocumentBuilder();
//from document builder,parse the input stream
Document dom=domBuilder.parse(arg0.getInputPayload().getInputStream());
isSales=”NO”;
String result=traversingXML(dom);
String posDir=(String)arg0.getInputParameters().getString(“posDir”);
String newDir=(String)arg0.getInputParameters().getString(“newDir”);
String dupDir=(String)arg0.getInputParameters().getString(“dupDir”);
String SERVICE = (String)arg0.getInputParameters().getString(“SERVICE”); // Name of service defined in XI configuration
String CHANNEL_NAME = (String)arg0.getInputParameters().getString(“CHANNEL_NAME”); ; // Name of communication channel defined for service
DynamicConfiguration conf = arg0.getDynamicConfiguration();
DynamicConfigurationKey KEY_FILENAME =
DynamicConfigurationKey.create(“http://sap.com/xi/XI/System/File”,”FileName”);
DynamicConfigurationKey KEY_DIRECTORY =
DynamicConfigurationKey.create(“http://sap.com/xi/XI/System/File”,”Directory”);
DynamicConfigurationKey KEY_SourceFileTimeStamp =
DynamicConfigurationKey.create(“http://sap.com/xi/XI/System/File”,”SourceFileTimestamp”);
inputFileName=conf.get(KEY_FILENAME);
String SourceFileTimeStamp=conf.get(KEY_SourceFileTimeStamp);
String directory=””;
if(msgType.equals(“goodsreceipt”))
{
isSales=”NO”;
}
if(isSales.equals(“YES”)){
//directory= posDir+”/”+newDir;
if(msgType.equals(“SALES”)){
directory=”/”+posDir+”/poslogsales”;
inputFileName=convertTimeTewelveHoursBack(SourceFileTimeStamp)+”_”+inputFileName;
conf.put(KEY_FILENAME,inputFileName );
}
}else{
String fileName=inputFileName;
NodeList transactionList= dom.getElementsByTagName(“Transaction”);
for(int k=0;k<transactionList.getLength();k++){
String businessDate= “”;
businessDate=((Element)transactionList.item(k)).getElementsByTagName(“BeginDateTime”).item(0).getFirstChild().getTextContent();
String obj[]=businessDate.split(“T”);
String bDate=obj[0].replaceAll(“-“, “”).trim();
String bTime=obj[1].replaceAll(“:”, “”).trim();
String messageType= msgType;
String seuquenceNo= ((Element)transactionList.item(k)).getElementsByTagName(“SequenceNumber”).item(0).getFirstChild().getTextContent();
String storeNo= ((Element)transactionList.item(k)).getElementsByTagName(“RetailStoreID”).item(0).getFirstChild().getTextContent();
String workstationId= ((Element)transactionList.item(k)).getElementsByTagName(“WorkstationID”).item(0).getFirstChild().getTextContent();
trace.addWarning(bDate+”*”+fileName+”*”+bTime+”*”+seuquenceNo+”*”+storeNo+”*”+workstationId+”*”+SERVICE+”*”+CHANNEL_NAME+”*”+messageType);
//RFC call to check the Transaction duplicity.
String returnValue = performRFCLookup(bDate, fileName, bTime, seuquenceNo, storeNo, workstationId,SERVICE,CHANNEL_NAME,messageType);
if(returnValue.equals(“0”)){
//directory= posDir+”/”+newDir;
if(msgType.equals(“goodsreceipt”)){
directory=”/”+posDir+”/goodsreceipt”;
}else if(msgType.equals(“poreturn”)){
directory=”/”+posDir+”/poreturn”;
}else if(msgType.equals(“storetransfer”)){
directory=”/”+posDir+”/storetransfer”;
}else if(msgType.equals(“sloctosloctransfer”)){
directory=”/”+posDir+”/sloctosloctransfer”;
}else if(msgType.equals(“dailyadjustments”)){
directory=”/”+posDir+”/dailyadjustments”;
}else if(msgType.equals(“stocktake”)){
directory=”/”+posDir+”/stocktake”;
}else if(msgType.equals(“Nonconsumableorder”)){
directory=”/”+posDir+”/nonconsumableorder”;
}else if(msgType.equals(“consumableorder”)){
directory=”/”+posDir+”/consumableorder”;
}else if(msgType.equals(“SOQ”)){
directory=”/”+posDir+”/SOQ”;
}else if(msgType.equals(“inventorycount”)){
directory=”/”+posDir+”/inventorycount”;
}
}else if(returnValue.equals(“4″)){
directory=”/”+posDir+”/”+dupDir;
}
}
}
conf.put(KEY_DIRECTORY,directory );
//arg1.getOutputPayload().getOutputStream().write(result.getBytes());
//arg1.getOutputPayload().getOutputStream().write(result.getBytes());
byte[] b = new byte[arg0.getInputPayload().getInputStream().available()];
arg0.getInputPayload().getInputStream().read(b);
arg1.getOutputPayload().getOutputStream().write(b);
}catch(Exception e){
}
}
private synchronized String performRFCLookup(String bDate,String fileName,String bTime,String seuquenceNo,String storeNo,String workstationId,String SERVICE1,String CHANNEL_NAME1,String messageType)
{
//String result=”4”;
String result1=new String();
String SERVICE = SERVICE1; // Name of service defined in XI configuration
String CHANNEL_NAME = CHANNEL_NAME1; // Name of communication channel defined for service
String SAP_RFC_NAMESPACE = “urn:sap-com:document:sap:rfc:functions”, // Namespace for SAP RFC definitions
FUNCTION_MODULE = “Z_FM_DUPLICATE_CHECK”, // Name of the function module called
VALUE_NOT_FOUND = “”; // Default return value in case something goes wrong
// Create document builder to create DOM XML document
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
factory.setNamespaceAware(false);
factory.setValidating(false);
try {
// Create XML document using document builder
builder = factory.newDocumentBuilder();
} catch (Exception e) {
trace.addWarning(“Error creating DocumentBuilder – ” +
e.getMessage());
// return null;
}
trace.addWarning(“*message type is*”+messageType);
String rfcXML = “<?xml version=\”1.0\” encoding=\”UTF-8\”?>” +
“<ns0:Z_FM_DUPLICATE_CHECK xmlns:ns0=\”urn:sap-com:document:sap:rfc:functions\”>” +
“<IV_BDATE>”+bDate+”<IV_BDATE/>” +
“<IV_BTIME>”+bTime+”<IV_BTIME/>” +
“<IV_WERKS>”+storeNo+”<IV_WERKS/>”+
“<IV_WSTID>”+workstationId+”<IV_WSTID/>”+
“<IV_SEQNO>” +seuquenceNo+”<IV_SEQNO/>” +
“<IV_MESTYP>”+messageType+”<IV_MESTYP/>” +
“<IV_FILEN>”+fileName+”<IV_FILEN/>” +
“</ns0:Z_FM_DUPLICATE_CHECK>”;
// Prepare and perform RFC Lookup …
com.sap.aii.mapping.lookup.RfcAccessor accessor;
com.sap.aii.mapping.lookup.Payload result=null;
try {
// Determine a communication channel (Business system + Communication channel)
com.sap.aii.mapping.lookup.Channel channel = LookupService.getChannel(SERVICE, CHANNEL_NAME);
// Get a RFC accessor for the channel.
accessor = LookupService.getRfcAccessor(channel);
// Create an XML input stream that represents the RFC request message.
java.io.InputStream is = new java.io.ByteArrayInputStream(rfcXML.getBytes());
// Create the XML Payload
com.sap.aii.mapping.lookup.XmlPayload payload = LookupService.getXmlPayload(is);
// Execute the lookup.
result = accessor.call(payload);
accessor.close();
if (result == null) {
trace.addWarning(“result of RFC call is null”);
}
} catch (com.sap.aii.mapping.lookup.LookupException e) {
trace.addWarning(“Error during lookup – ” + e);
}
// Parsing RFC Response Document
Document docResponse = null;
java.io.InputStream in = result.getContent();
String returnValue = VALUE_NOT_FOUND;
NodeList poItems = null;
try {
docResponse = builder.parse(in);
if (docResponse == null) {
trace.addWarning(“docResponse is null”);
}
NodeList transactionList= docResponse.getElementsByTagName(“rfc:Z_FM_DUPLICATE_CHECK.Response”);
trace.addWarning(“success1″);
String endResult=””;
for(int k=0;k<transactionList.getLength();k++){
String EV_SUBRC= ((Element)transactionList.item(k)).getElementsByTagName(“EV_SUBRC”).item(0).getFirstChild().getTextContent();
trace.addWarning(“success2″+EV_SUBRC);
result1=EV_SUBRC;
}
}
catch (Exception e) {
trace.addWarning(“Error when parsing RFC Response – ” + e.getMessage());
}
trace.addWarning(“result1 is – ” + result1);
return result1;
}
private String convertTimeTewelveHoursBack(String dateToConvert){
String output=””;
try
{
dateToConvert=dateToConvert.replaceAll(“T”, “”);
dateToConvert=dateToConvert.replaceAll(“Z”, “”);
DateFormat gmtFormat = new SimpleDateFormat(“yyyyMMddHHmmss”);
Date date = gmtFormat.parse(dateToConvert);
//TimeZone gmtTime = TimeZone.getTimeZone(“UTC”);
//gmtFormat.setTimeZone(gmtTime);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.HOUR,+8);
Date tewelveHourBack = cal.getTime();
DateFormat gmtFormat1 = new SimpleDateFormat(“yyyyMMdd_HHmmss”);
output=gmtFormat1.format(tewelveHourBack);
}catch(Exception e){
e.printStackTrace();
}
return output;
}
private String traversingXML(Node node) {
NodeList children=node.getChildNodes();
NamedNodeMap attributes;
Node attrnode;
for(int j=0;j<children.getLength();j++){
Node child=children.item(j);
short childType=child.getNodeType();
String attrName=”NA”;
if(childType==Node.ELEMENT_NODE){
String nodeName=child.getNodeName();
StringBuffer tempNodeName=new StringBuffer();
tempNodeName.append(“<“+nodeName);
NamedNodeMap attr=child.getAttributes();
for(int k=0;k<attr.getLength();k++){
Node attrvalue=attr.item(k);
attrvalue.getNodeName();
attrvalue.getNodeValue();
tempNodeName.append(” “+attrvalue.getNodeName()+”=”+”\””+attrvalue.getNodeValue()+”\””);
}
tempNodeName.append(“>”);
endResult.append(tempNodeName);
traversingXML(child);
endResult.append(“</”+nodeName+”>”);
if(nodeName.equals(“RetailTransaction”)||nodeName.equals(“WN:BSCMTransaction”)||nodeName.equals(“ControlTransaction”)){
isSales=”YES”;
msgType=”SALES”;
}else if(nodeName.equals(“SOQ”)){
isSales=”NO”;
msgType=”SOQ”;
}else if(nodeName.equals(“EODConfirmation”)){
isSales=”NO”;
msgType=”inventorycount”;
}
}else if(childType==Node.TEXT_NODE){
String nodeValue=child.getNodeValue();
nodeValue=nodeValue.replaceAll(“&”, “&”);
nodeValue=nodeValue.replaceAll(“<“, “<”);
endResult.append(nodeValue);
if(nodeValue.equals(“goodsreceipt”)){
isSales=”NO”;
msgType=”goodsreceipt”;
}else if(nodeValue.equals(“PGR”)||nodeValue.equals(“WPO”)){
isSales=”YES”;
msgFct=nodeValue;
}
else if(nodeValue.equals(“poreturn”)){
isSales=”NO”;
msgType=”poreturn”;
}else if(nodeValue.equals(“storetransfer”)){
isSales=”NO”;
msgType=”storetransfer”;
}else if(nodeValue.equals(“sloctosloctransfer”)){
isSales=”NO”;
msgType=”sloctosloctransfer”;
}else if(nodeValue.equals(“dailyadjustments”)){
isSales=”NO”;
msgType=”dailyadjustments”;
}else if(nodeValue.equals(“stocktake”)){
isSales=”NO”;
msgType=”stocktake”;
}else if(nodeValue.equals(“Nonconsumableorder”)){
isSales=”NO”;
msgType=”Nonconsumableorder”;
}else if(nodeValue.equals(“consumableorder”)){
isSales=”NO”;
msgType=”consumableorder”;
}
}
}
return endResult.toString();
}
}
I have always been a fan of the "KISS" principle. Hence even though your blog demonstrates a work around, the implementation would have been much more simple and maintainable if it was done using the BPMN capabilities.
Keep blogging!
Hi Shabarish ,
Thanks for your comments. I worked with you in Celestica project.You were in Canada and I was with IBM Hyderabad. It was during the migration of interfaces from Extricity middleware tool to SAP PI. As it was an old tool it was zipping using .tar format .Hope you remember me 😉
While Using SAP PI, during the execution.. RFC returns “4” then it means
RFC returns “4” then it means transaction is duplicate and file is placed in duplicate folder.
else if ( returnValue.equls(“4”)){
{
directory=”/”+posDir+”/”+dupDir;
}
It is giving error. Unable to trace. Help me.
Hi Bhanu ,
Sorry for the late reply.Here the directory name I am getting from parameteised values.
If you are getting error then you can hard code the line.
For example - directory ="/duplicate";
Hi Varun!
You mentioned about using “synchronized” keyword in your blog. Wouldn’t you please explain, how preventing parallel access to the same method within one running instance of mapping class would impact processing of another instances of that class?
Let’s imagine we have the instance myObj of class myClass with synchronized method called “transform”. When other objects, say Obj2 and Obj3, try to call myObj1.transform() method, Obj3 can’t get access to that method until its execution for Obj2 is finished.
In our case new instance of myClass is created for every new message. So, mapping runtime calls myObj1.transform(), myObj2.transform() and so on. Thus no synchronization takes place between different instances.
Regards, Evgeniy.
Hi Evgeniy Kolmakov ,
Sorry for the late reply as I was stuck with work commitments,I did not login to sap community recently.
Coming to your question how synchronized help me in this scenario.
Hope this explanation answers your query.