Introduction:
The purpose of this document is to develop a synchronous interface to download content from Open Text Content Server (OTCS) using REST API. I have tried to cover the overall design with code snippets for reference.
Scope:
- Rest OTCS authentication using Content-Type: multipart/form-data (Credentials as a header of the multipart body) for the token (otcsticket) generation.
- Parameterized Mapping for accessing the OTCS credentials from ICO.
- Calling OTCS authentication lookup, accessing parameters from java mapping.
- Creating DynamicConfigurationKey for rest channel Parameter from java mapping.
- OTCS session token management is out of scope.
Overall Design:
Solution Flow:
- SAP ECC calls a proxy to send the Document ID of the document in OTCS.
- PO Request java mapping receives Document ID.
- Calls OTCS – Authentication API for a token (otcsticket) via REST lookup
- Post ID and token to OTCS – Content API
- Calls OTCS – Authentication API for a token (otcsticket) via REST lookup
- PO Response Java Mapping receives Document as an inputstream and maps it to the content field.
- Base64 content Field is sent to SAP for further processing.
Rest API consumed from Open Text Content Server (OTCS):
- Authentication API – /otcs/cs.exe/api/v1/auth: API needs to be called with credentials in Content-Type: multipart/form-data section to generate a token, which is called otcsticket. Otcsticket needs to be present in the header for content API to be called.
In Content-Type: multipart/form-data, credentials need to be present, separated by a boundary.
- Content API – /otcs/cs.exe/api/v1/nodes/{ID}/content: API would return the document as a byte stream, when called with the token and ID of the document in the header.
PO Objects and code snippets:
Data Structure
Document ID for OTCS comes as DataID from SAP. The document is returned to SAP as Content.
ICO
Mapping (MM) & Operational mapping (OM)
Please take care of the above ICO parameter in
- OM-> Parameters section and Request Mapping Binding section
- MM -> Signature tab
Request Mapping with java mapping in the Attributes and Methods Section
public void transform(TransformationInput in, TransformationOutput out) throws StreamTransformationException {
try {
getTrace().addDebugMessage("***OTCS-Request-JavaMapping-Start");
//Get the mapping parameter from ICO
String paramChannel = in.getInputParameters().getString("lookupChannel");
String paramUserName = in.getInputParameters().getString("username");
String paramPassword = in.getInputParameters().getString("password");
String paramBoundary = in.getInputParameters().getString("boundary");
getTrace().addDebugMessage("***OTCS-Step1-LogPramFromICO-lookupChannel:" + paramChannel + "-username:"
+ paramUserName + "-password:" + paramPassword +"-boundary:" + paramBoundary);
//Creating multipart/form-data for OTCS authentication
String LINE_FEED = "\r\n";
String ContentDisposition = "Content-Disposition: form-data; name=\"";
String authReqFormData ="";
authReqFormData = LINE_FEED + paramBoundary + LINE_FEED + ContentDisposition + "username\"" + LINE_FEED
+ LINE_FEED + paramUserName + LINE_FEED + paramBoundary + LINE_FEED +ContentDisposition
+ "password\"" + LINE_FEED + LINE_FEED + paramPassword + LINE_FEED + paramBoundary + "–-" + LINE_FEED;
getTrace().addDebugMessage("***OTCS-Step2-multipart/form-data:" + authReqFormData);
//Read message header value for Receiver
String paramReceiver = in.getInputHeader().getReceiverService();
getTrace().addDebugMessage("***OTCS-Step3-ReceiverService:" + paramReceiver);
//Get the OTCS rest lookup Channel Object for authentication
Channel lookup_channel = LookupService.getChannel(paramReceiver, paramChannel);
//Call rest lookup channel, with multipart/form-data payload
SystemAccessor accessor = null;
accessor = LookupService.getSystemAccessor(lookup_channel);
InputStream authInputStream = new ByteArrayInputStream(authReqFormData.getBytes("UTF-8"));
Payload authPayload = LookupService.getXmlPayload(authInputStream);
Payload tokenOutPayload = null;
//Call lookup
getTrace().addDebugMessage("***OTCS-Step4-CallLookupChannel");
tokenOutPayload = accessor.call(authPayload);
//Parse for Lookup response for token
InputStream authOutputStream = tokenOutPayload.getContent();
DocumentBuilderFactory authfactory = DocumentBuilderFactory.newInstance();
DocumentBuilder authbuilder = authfactory.newDocumentBuilder();
Document authdocument = authbuilder.parse(authOutputStream);
NodeList nlticket = authdocument.getElementsByTagName("ticket");
String tokenTicket = "Empty";
Node node = nlticket.item(0);
if (node != null){
node = node.getFirstChild();
if (node != null){
tokenTicket = node.getNodeValue();
}
}
getTrace().addDebugMessage("***OTCS-Step5-TokenFromLookup:" + tokenTicket);
//Parse input stream and get DataID from SAP
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(in.getInputPayload().getInputStream());
String DataID = doc.getElementsByTagName("DataID").item(0).getTextContent();
getTrace().addDebugMessage("***OTCS-Step6-DataIDFromSAP: " + DataID);
//Create HTTP Header for rest call via setting DynamicConfiguration keys, that can be used in reciver channel
DynamicConfiguration conf = in.getDynamicConfiguration();
DynamicConfigurationKey keytokenTicket = DynamicConfigurationKey.create("http://sap.com/xi/XI/System/REST","HeadertokenTicket");
DynamicConfigurationKey keyDataID = DynamicConfigurationKey.create("http://sap.com/xi/XI/System/REST","HeaderDataID");
conf.put(keytokenTicket, tokenTicket);
conf.put(keyDataID, DataID);
String DummyPayload = "DummyPayload";
// Instantiating output stream to write at Target message
OutputStream os = out.getOutputPayload().getOutputStream();
// writing idoc to output stream
os.write(DummyPayload.getBytes("UTF-8"));
os.flush();
os.close();
getTrace().addDebugMessage("***OTCS-Request-JavaMapping-End");
}
catch (Exception e){
getTrace().addDebugMessage(e.getMessage().toString());
throw new StreamTransformationException(e.getMessage());
}
}
Response Mapping with java mapping in the Attributes and Methods Section
public void transform(TransformationInput in, TransformationOutput out) throws StreamTransformationException {
try
{
getTrace().addDebugMessage("***OTCS-Respose-JavaMapping-Start");
InputStream inputstream = in.getInputPayload().getInputStream();
OutputStream outputstream = out.getOutputPayload().getOutputStream();
//Copy Input Payload into Output xml
byte[] b = new byte[inputstream.available()];
inputstream.read(b);
//Form Output xml
String outputStart = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ns0:MT_DocContent_Res xmlns:ns0=\"urn://XXXXXXXXXXX.com/OTCS/DocDownload\"><Content>";
String outputEnd = "</Content></ns0:MT_DocContent_Res>";
outputstream.write(outputStart.getBytes("UTF-8"));
outputstream.write(b);
outputstream.write(outputEnd.getBytes("UTF-8"));
outputstream.flush();
outputstream.close();
getTrace().addDebugMessage("***OTCS-Respose-JavaMapping-End");
}
catch (Exception e)
{
getTrace().addDebugMessage(e.getMessage().toString());
throw new StreamTransformationException(e.getMessage());
}
}
Channels
We have three channels in the flow, Such as Proxy channel from SAP, Rest channels for Token Lookup, and document fetching from the OTCS.
Proxy – CC_OTCS_GetDoc_Proxy_Sender
Rest lookup channel – CC_OTCS_Rest_LookUp
- URL- http://{Server}/otcs/cs.exe/api/v1/auth
- Rest Operation- POST
- Data Format- UTF-8
- HTTP Headers, as below
Content-Type multipart/form-data; boundary=SapPO75FormBoundaryhahahahahahahaEND
Rest document fetch channel– CC_OTCS_Rest_Receiver
- URL- http:// /{Server}/otcs/cs.exe/api/v1/nodes/{ID}/content
- Variable Substitution-
- Rest Operation- GET
- Data Format- UTF-8
- HTTP Headers, as below
otcsticket {otcsticket}
ID {ID}