Technical Articles
SAP CI – User Defined Search Made Easy and Automated
Introduction:
We often come across this very strong desire plagued by our laziness that I cannot log payloads in SAP CI but what if I was not lazy enough to implement User defined Message Search on the inbound XML for 2-3 fields in all my iFlows…..What If I could do it. Wouldn’t it make my life much easier.
But then the realization dawns on us that yes to do that we will have to first
- Create externalized properties in a content modifier with type as XPATH
- Enter the XPATH in the value for each field.
- Then individually add the xpath values to the message log as Custom headers.
Problem with this is that if you need to add a new user defined search criteria you need to modify the groovy script and the content modifier.
Imagine you are in productive environment and live and suddenly you observe there is an issue with an existing Integration, but you need to be able to see the values coming through on the run, following the steps 1 to 3 again in Dev and transporting to prod is not an option anymore.
Solution:
This can be easily solved with a two-step process.
Step 1: Add a Search Attribute list or the display header name in the Content modifier property section.
Step 2: Add a xpath list for those attributes in the content modifier property section, i.e. the xpath from where those attributes get called.
Content Modifier
Step3: Add the header names you would like and the source XPATHS in the respective properties in a # separated fashion.
Configure section
Step 4: Trust in groovy and let it do the rest.
This groovy script will parse through the Header names and the corresponding XPATHS and create Message Log Headers dynamically.
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import javax.xml.xpath.*
import javax.xml.parsers.DocumentBuilderFactory
def Message logKeys(Message message) {
def xml = message.getBody(java.lang.String) as String;
def properties = message.getProperties();
def search_attributes = properties.get("search_attributes");
def attribute_xpaths = properties.get("attribute_xpaths");
def messageLog = messageLogFactory.getMessageLog(message);
String[] search_attributes_array = search_attributes.split("#");
String[] attribute_xpaths_array = attribute_xpaths.split("#");
if(search_attributes_array.length == attribute_xpaths_array.length )
{
def xpath = XPathFactory.newInstance().newXPath()
def builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
def inputStream = new ByteArrayInputStream( xml.bytes )
def records = builder.parse(inputStream).documentElement
for(int i=0; i<attribute_xpaths_array.length; i++)
{
String temp_value;
temp_value = xpath.evaluate( attribute_xpaths_array[i], records )
if(temp_value != null)
{
messageLog.addCustomHeaderProperty(search_attributes_array[i], temp_value);
}
}
}
return message;
}
Step 5:
Post your XML towards the iFLOW
<invoice>
<invoiceHeader InvoiceNumber="59864799">
<dueDate>1st May 2023</dueDate>
<companyCode>100</companyCode>
</invoiceHeader>
</invoice>
Result:
We now have fully automated and configurable Custom Message Headers that we can search in message monitoring.
Message Monitoring
We already have a few blogs around this topic but none of them covers configurable custom Message headers hence this blog.
PS: Thank you Daniel Graversen, for suggesting the code improvements.
Hi vinay mittal - Excellent blog. As beginner, I want to know after adding parameters in the content modifier exchange section where would I go to maintain the values shown in the screenshot named "Configure section"? I want to know which step and screen is related to this. Please clarify.
You can do it from here Michael
Good One Vinay...
Hi vinay mittal, excellent blog!!
Said that, I've tried to use your code and succeed at it but with one small condition, the looked xpath needs to be only once in the entire xml otherwise it will log only the first occurrence. In normal integrations, this does not happens (as per my experience), we usually get several business objects per message (not talking about bulk but a few).
In order to fix your code I've changed the evaluate a little bit.
Please, find the new code below:
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import javax.xml.xpath.*;
import javax.xml.parsers.DocumentBuilderFactory;
def Message logKeys(Message message) {
def xml = message.getBody(java.lang.String) as String;
def properties = message.getProperties();
def search_attributes = properties.get("search_attributes");
def attribute_xpaths = properties.get("attribute_xpaths");
def messageLog = messageLogFactory.getMessageLog(message);
String[] search_attributes_array = search_attributes.split("#");
String[] attribute_xpaths_array = attribute_xpaths.split("#");
if(search_attributes_array.length == attribute_xpaths_array.length && search_attributes.size() > 0 && attribute_xpaths.size() > 0)
{
def xpath = XPathFactory.newInstance().newXPath()
def builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
def inputStream = new ByteArrayInputStream( xml.bytes )
def records = builder.parse(inputStream).documentElement
for(int i=0; i<attribute_xpaths_array.length; i++)
{
XPathExpression expr = xpath.compile( attribute_xpaths_array[i])
def nodes = expr.evaluate(records, XPathConstants.NODESET);
if(nodes != null)
{
for(int j = 0; j < nodes.getLength(); j++){
messageLog.addCustomHeaderProperty(search_attributes_array[i], nodes.item(j).getTextContent());
}
}
}
}
return message;
}
Highlighted my changes here.
Also, I've added in the condition a validation just in case it is not configured for the interface. In this way, you can put it in all iflows without the need of worrying beforehand on how it should be configured. You place it and use it when you want it.
Regards,
Roberto
Even better version of the code above is the one below that handles the xpath namespaces if any:
This is indeed Brilliant Roberto. Kudos