Technical Articles
Send cXML Message with multiple attachments to Ariba Network as MIME Multipart
Requirement:
Recently I had a requirement to send cXML message with multiple attachments to Ariba network as MIME multipart. Below is the detailed requirement.
Overview:
The source is a zip file with xml and attachments (pdf, word, excel, txt). I had to create a cXML file from xml and send cXML message (attachments filenames need to be part of “Comments” segment in cXML) with encoded attachments to Ariba network as MIME multipart. The challenge here was the attachments count is not same in each source zip file. This was not achievable using standard MIME Multipart encoder.
Solution:
As the attachments count is not fixed, a recursive local process is designed to create MIME multipart for each file and in the end, all are gathered and sent to Ariba Network. To achieve this, I have used 3 processes:
- Integration Process: Main integration process-P1
- Local integration process : Store Files – P2
- Local integration process: Mapping & MIME – P3
Integration Artifact Details: Process: Main integration process – P1
Step1: Groovy script: Declare the HashMap and array attribute Filename1
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
//Declare HashMap , array attribute Filename1 and attachments properties
HashMap PayloadHashMap = new HashMap<String,String>();
def Filename1 = []
def Attachment =""
def SourceFilename = message.getHeaders().get("CamelFileName")
message.setProperty("PayloadHashMap", PayloadHashMap)
message.setProperty("Filename1", Filename1)
message.setHeader("SAP_ApplicationID",SourceFilename)
message.setProperty("Attachment",Attachment)
return message;
}
Step3: Base64 Encoder: Encodes unzipped file
Step 4: Process Call To store Files
Step 5: Gather all the MIME files
Step 6: Content modifier: Add Content Type to Ariba Multipart
Source value: multipart/related; boundary=”110446448544001″;type=”text/xml”;start=”<4c07a241-26c8-11e5-95bb-e06995c17823_031@test.com>”
Boundary value is a separator for cXML and encoded attachments. I have used a random number.
Step 7: Content Modifier: Adding Attachments
Step 8: HTTPs Call: Ariba_HTTP_Request Reply
Ariba Shared secrete credentials are sent as part of cXML
Step 9: Content Modifier: Capture Ariba response
Integration Artifact Details: Process: Store Files – P2
Step 1: Groovy Script: Storing Files in HashMap
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
String body = message.getBody(java.lang.String) as String
def map = message.getProperties();
def Filename1 = message.getProperties().get("Filename1")
def PayloadHashMap = map.get("PayloadHashMap");
def filename = message.getHeaders().get("CamelFileName")
//Store filename and body in HashMap, Filename in array attribute Filename1
PayloadHashMap.put(filename,body)
Filename1.push(filename)
message.setProperty("PayloadHashMap", PayloadHashMap)
message.setProperty("Filename1", Filename1)
return message;
}
Step 2: Loop process call: Mapping & MIME Process Call
Max number of Iterations =10 ( max number of attachments expected in zip file)
Integration Artifact Details: Process: Mapping & MIME – P3
Step 1: Groovy script: Fetch the body of 1st entry in HashMap table
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
def map = message.getProperties();
def PayloadHashMap = map.get("PayloadHashMap");
//Fetch body of the 1st entry of HashMap
def Filename = message.getProperties().get("Filename1")[0]
def PayloadNew = "";
PayloadNew = PayloadHashMap.get(Filename);
message.setBody(PayloadNew);
message.setProperty("PayloadNew", PayloadNew);
return message;
}
Step 2: Content Modifier: Capture the message ID and current timestamp required in cXML
Message ID and current timestamp values are used in mapping.
Step 3: Router: checks if file is empty
${property.PayloadNew} = null or ${property.PayloadNew.trim().length()} = ‘0’ or ${property.PayloadNew.trim()} = ‘””‘
Step 4: Groovy script: Capture Data: Delete 1st entry of HashMap and array attribute Filename1; Capture file attachment details
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import groovy.xml.MarkupBuilder
def Message processData(Message message) {
def map = message.getProperties();
def PayloadHashMap = map.get("PayloadHashMap");
def Filename = message.getProperties().get("Filename1")[0]
def PayloadNew = PayloadHashMap.get(Filename);
// Delete 1st entry of Hashmap and array attribute Filename1;
def packages = message.getProperties().get("Filename1").reverse() as Stack
packages.pop()
packages = packages.reverse() as Stack
message.setProperty("Filename1", packages)
PayloadHashMap.remove(Filename);
// Capture file attachment details
def Attachment = message.getProperties().get("Attachment")
def writer = new StringWriter();
def builder = new MarkupBuilder(writer);
def urlFilename = Filename.substring(Filename.indexOf('/')+1);
urlFilename = 'cid:'+urlFilename + '@test.com'
def FileType = Filename.substring(Filename.indexOf('.')+1);
if (FileType != 'xml')
{
builder.Attachment
{
if (FileType != 'xml')
'URL'(urlFilename)
}
}
Attachment = Attachment + writer.toString();
message.setProperty("Attachment", Attachment);
message.setProperty("PayloadNew", PayloadNew);
message.setProperty("PayloadHashMap", PayloadHashMap);
return message;
}
Step 5: Router: Checks if file is xml
Step 6: Base64 Decoder
Step 7: Content Modifier: Ariba Header Parameters
Pass the Ariba header parameters required in cXML to connect to Ariba Network
Step 8: Message mapping: Map_xml_to_cXML
Message mapping to convert xml to cXML
Step 9: Content Modifier: Capture Header Comments
Store the “Comments” segment details from cXML, these details will be replaced by attachments details later in main process.
Step 10: XSLT Code: Handling xml:lang in cXML
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<xsl:text disable-output-escaping="yes">
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.055/InvoiceDetail.dtd">
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!--Replaces “lang” with “xml:lang” in cXML-->
<xsl:template match="@lang">
<xsl:attribute name="xml:lang">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Replaces “lang” with “xml:lang” in cXML
Step 11: Content Modifier: Added ContentType and ContentID
Step 12: Content Modifier: Capture cXML Payload
Step 13: Groovy script: Capture Data : Common script for Step 4 and step 13
Step 14: Groovy script: Check File Type Extension
“ContentType” fetched from value mapping table.
import com.sap.gateway.ip.core.customdev.util.Message
import java.util.HashMap
import com.sap.it.api.ITApiFactory
import com.sap.it.api.mapping.ValueMappingApi
def Message processData(Message message) {
//Rerieve Input Data
def body = message.getBody(java.lang.String) as String;
//Declare value mapping table
def valuemap = ITApiFactory.getApi(ValueMappingApi.class, null);
def SAgency = "InvoiceSystem";
def SIdentifier = "FileExtension";
def TAgency = "Ariba";
def TIdentifier = "ContentType";
def CamelFileName = message.getHeaders().get("CamelFileName")
def Key = CamelFileName.substring(CamelFileName.indexOf(".")+1 , CamelFileName.length());
def filename = CamelFileName.substring(CamelFileName.indexOf("/")+1 , CamelFileName.length());
//Fetch value mapping table details
def ContentType = valuemap.getMappedValue(SAgency,SIdentifier,Key,TAgency,TIdentifier);
message.setProperty("ContentType", ContentType)
message.setProperty("filename", filename)
return message;
}
Value mapping table:
Testing:
Zip file placed in source SFTP:
MIME with 5 attachments sent to Ariba:
Ariba response in CPI:
Conclusion:
We saw how to send cXML message with multiple attachments to Ariba Network.
If the xml is missing in the zip file, message status will be successful in CPI with HTTP 500 error response from Ariba. If zip file has only xml, then cXML will be sent to Ariba. Messages status will be successful in CPI with HTTP 200 response from Ariba.