This blog covers some ideas and approaches on routing of messages based on a particular XPath condition in SAP CPI.
Requirement :
- Flow : SAP S4 HANA –> CPI –> 3rd party SFTP
- Source : IDOC, Target : External Definition
- IDOCs having no /ZCUSTOM_ZINVOIC02_INV/IDOC/E1EDP01/ZCUSTOM_E1EDP01_SUBITEM/ZFLAG=”A” need to be terminated in CPI and not sent to target system.
- IDOCs having mixed values i.e combination of “A” and other values can be sent but mapping needs to be considered only for ZFLAG=”A”
- Source is IDOC and target is a custom XML External definition
- Multiple IDOCs can be sent in a batch
3 approaches were explored as below :
Approach 1 :
- Use a message mapping and apply logic for checking ZFLAG value at root IDOC level. Suppress IDOCs having no Lines in IDOC having ZFLAG=A.
- Use a router in next step and check if count of IDOCs is zero.
- If count is not zero, pass it to main mapping in default route where IDOC-External Definition mapping is done (Default Route)
- If count is zero then end that batch here ( Router Condition : count(//IDOC) = 0 )
Approach 2 :
- Use a Groovy Script which keeps a count of all items having ZFLAG=”A” and sets the count as a Exchange Property
- Use a router at next step to check if Exchange Property count is greater than zero
- If count is not zero, pass it to default route where IDOC-External Definition mapping is done and sent ahead
- If count is zero, then end that IDOC in CPI
Approach 3 :
- Use a content modifier to capture all actual values of Flag in an Exchange Property and use another Exchange Property to capture the applicable value (Here it is “A”)
- Use a router in next step and check if Applicable Value Exchange Property value exists in Actual value exchange property.
- If applicable value exists in actual value list, pass it to main mapping in default route where IDOC-External Definition mapping is done
- If applicable value does not exist in actual value list, end that IDOC in CPI
All 3 approaches are covered in detail below :
Approach 1 : Filter Mapping + Router + Main Mapping Combination
Step 1 : Message Mapping which suppresses the IDOCs which do not have even one line item with ZFLAG=A
- Use a message mapping and apply filter of checking ZFLAG value at root IDOC level and at E1EDP01 segment (Rest segments can be mapped one-one)
- Suppress IDOCs having no Line item in IDOC having ZFLAG=A in mapping.
NOTE : After ifWithoutElse, a UDF is used which returns a single true, if atleast a single line item with ZFLAG=A exists in an IDOC, else, it will return false which will suppress that IDOC itself (UDF is not discussed here)
Step 2 : Conditional Routing
- Step 2.1 : If count is not zero pass it to main mapping in default route where IDOC-External Definition mapping is done (Default Route)
- Step 2.2 : If count is zero then end that batch here ( Router Condition : count(//IDOC) = 0 )
Step 3 : Split and Map IDOC to XML as applicable
Simulation Results for Positive test Case : When IDOCs in a batch contains atleast one line item with ZFLAG=A. 4 IDOCs triggered in a batch, 2 have no line items with ZFLAG=A and 2 IDOCs have mixed values
Simulation Results for Negative test Case : When none of the IDOCs in a batch have line items with ZFLAG=A
Approach 2 : Groovy + Router + Main Mapping Combination
- Step 1 : Split the IDOCs
- Step 2 : Groovy Script which keeps a count of all items having ZFLAG=”A” and sets the count as a Exchange Property-ZFLAG_Count
// Purpose of using this groovy - per requirement, if an IDOC has no Line item (E1EDP01) containing ZCUSTOM_E1EDP01_ITEM/ZFLAG=A, then no output XML for that IDOC should be created from CPI. // Below script will return the count of ZFLAG=A in Exchange property, If count > 1 then send idoc to mapping, else end such message in CPI import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import groovy.util.XmlSlurper ; import groovy.xml.XmlUtil; import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; import java.io.StringReader; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; def Message processData(Message message) { def mapProperties = message.getProperties(); //read message as string def body = message.getBody(java.lang.String) as String; //initialize variables def count=0; //Create a DocumentBuilder DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new InputSource(new StringReader(body))); doc.getDocumentElement().normalize(); //Extract the root element Element root = doc.getDocumentElement(); System.out.println("Root element " + doc.getDocumentElement().getNodeName()); NodeList nList = doc.getElementsByTagName("E1EDP01"); for (int temp = 0; temp < nList.getLength(); temp++) { Node nNode = nList.item(temp); Element eElement = (Element) nNode; if (eElement.getElementsByTagName("ZFLAG").item(0).textContent.equals("A")) count++; } message.setProperty("ZFLAG_Count", count); return message; }
- Step 3 : Use a router at next step to check if Exchange Property ZFLAG_Count
- Step 3.1 : if count is greater than zero, pass it to default route where IDOC-External Definition mapping is done and sent ahead (Default Route)
- Step 3.2 : If No, then end that IDOC in CPI (Condition : ${property.ZFLAG_Count} = ‘0’)
Figure 9 – Router Configuration
- Step 4 : Mapping IDOC-XML as applicable
Simulation Results for Positive test Case : When IDOC in a batch contains atleast one line item with ZFLAG=A. 4 IDOCs triggered in a batch, 2 have no line items with ZFLAG=A and 2 IDOCs have mixed values
Simulation Results for Negative test Case : When none of the IDOCs in a batch have line items with ZFLAG=A
Approach 3 : Content Modifier + Router + Main Mapping Combination
- Step 1 : Split the IDOCs
- Step 2 : Use a content modifier
- capture all actual values of ZFLAG in an Exchange Property (ZFLAG_ACTUAL_VALUES-string-join((//E1EDP01/ZCUSTOM_E1EDP01_SUBITEM/ZFLAG/text()),’,’)
- capture all applicable values of ZFLAG in an Exchange Property (ZFLAG_APPLICABLE_VALUES). Here this value is hardcoded to “A” as per requirement. (This can also be externalized)
NOTE : Here another exchange property – ZFLAG_COUNT is used with XPath value : count(//E1EDP01[ZCUSTOM_E1EDP01_SUBITEM/ZFLAG=”A”])(We can leverage this as an alternative to Groovy script in Approach 2 as well!)
Figure 14 – Content Modifier Exchange Properties (ZFLAG_COUNT value can be levraged instead of Approach 2 Groovy script as well!)
- Step 3 : Use a router in next step and check if Exchange Property-ZFLAG_APPLICABLE_VALUES value exists in exchange property-ZFLAG_ACTUAL_VALUES.
- Step 3.1 : If Yes, pass it to main mapping in default route where IDOC-External Definition mapping is done (Default Route)
- Step 3.2 : If No, end that IDOC in CPI (Condition : ${property.ZFLAG_ACTUAL_VALUES} not contains ${property.ZFLAG_APPLICABLE_VALUES}
- Step 4 : Mapping as applicable
Simulation Results for Positive test Case : When IDOC in a batch contains atleast one line item with ZFLAG=A. 4 IDOCs triggered in a batch, 2 have no line items with ZFLAG=A and 2 IDOCs have mixed values
Simulation Results for Negative test Case : When none of the IDOCs in a batch have line items with ZFLAG=A
Summary
Multi-dimensional approaches/ideas were shared for a requirement to bifurcate data based on repetitive XPath as explained above.
Hope this blog was informative with respect to the ideas on approaching a requirement with different ways and will be relevant to the CPI/PI/PO Developers/Consultants.
Constructive comments or feedback/suggestions if any on the above are welcome.
References
Filtering the IDoc Segment in SAP CPI | SAP Community
[SAP Cloud Platform-Integration] Content Filter in Detail | SAP Blogs
XPath Count | Mendix Documentation
Using XPath Functions (oracle.com)
Content Modifier Xpath Data Type | SAP Community
Get to know Camel’s Simple expression language in SAP Cloud Integration | SAP Blogs
Please explain Conditional router in detail in CPI | SAP Community