SAP Process Orchestration

Creating a Sync integration for downloading Open Text Content with REST API from Sap PO, Using API with multipart/form-data authentication

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:

Sync Interface to get the Document from OpenText via PO 7.5

Solution Flow:

  1. SAP ECC calls a proxy to send the Document ID of the document in OTCS.
  2. 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
  3. PO Response Java Mapping receives Document as an inputstream and maps it to the content field.
  4. 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.
otcsticket and ID of the document in the http 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}