Skip to Content
Technical Articles

SAP PI/PO – SOAPLookup SalesForce without BPM

Hello folks,

In partnership in one project with my friend, Josieudes De Sousa Claudio would like to share a nice solution without ccBPM or BPM to integrate with SalesForce.

Everything on-premise still good? hehe ๐Ÿ˜€

So basically I would like to share with you one solution that I applied doing SOAPLookup (I know that is not the best solution for security aspects but fit for the timeline of the project).

Another way to develop this solution in one call, you could use the CCBPM in case of PI dual-stack or BPM in single-stack or developer two interfaces, one to get the session id and store it in temporary table (SalesForce Token it’s available only 24hours) and another one to send the proper take with token access.

I already post one blog in the past how to connect with SalesForce with Rest dynamic URL Address:

DynamicURL – SalesForce

Similar blog developed – 10 years ago to extract the TimeStamp – made by – Prasanna Vittal

Java mapping that I used as reference – posted 10 years ago – JavaMapping

Interface Diagram:

Outbound interface โ€“ IDOC to SAP PI, the java mapping is responsible to do soap look up, buffer the sessionID, to read the sessionId via SOAP Login Channel Receiver than after that create a soap envelop message with the body (IDOC) and header (<soapenv:Header> ” + “<urn:SessionHeader>” + “<urn:sessionId>) with the details of response from SOAPLookup and send the data correctly to SalesForce web service.

The SalesForce controls number of calls for sessionID and sends data, in the java mapping we did trick point to control if and check the last time that SAP PI calls to receive the sessionid.

Integration Development Steps:

  1. Repository:

    • Imported Archives โ€“ JavaMapping
    • The trick point in operation mapping:
  2. Directory:

    • Configuration of receiver SOAP Login Channel:
    • ICO with configuration Details
    • Receiver Channel – Send Data

Process steps โ€“ Repository:

  • Imported Archives โ€“ JavaMapping:

This java mapping it’s responsible to do SOAPLookup and with the result set the reader of the next call with the IDOC and SessionID.

Also, this java mapping does a buffer in the calls because the SessionID in this project with SalesForce expire after 24hours or some limits of calls, so we could not for all interface call make the sessionID and we decide store in the java mapping.

 

The buffer of sessionID:

private void getSessionIdFromBuffer( ) {
sessionId = bufferedSessionID;
if ( trace != null)
trace.addInfo("SessionId from Buffer: " + sessionId);
}

Check the lasTimestamp call to get sessionID:

private void getSessionId( ) {

long currentTime = System.currentTimeMillis();

if ( (currentTime - lastTimestamp) > timeOut ) {
lastTimestamp = currentTime;
getSessionIdFromSFDC();
bufferedSessionID = sessionId;
} else {
getSessionIdFromBuffer();
}

}

Full java mapping code:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.sap.aii.mapping.api.AbstractTrace;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
import com.sap.aii.mapping.lookup.Channel;
import com.sap.aii.mapping.lookup.LookupException;
import com.sap.aii.mapping.lookup.LookupService;
import com.sap.aii.mapping.lookup.Payload;
import com.sap.aii.mapping.lookup.SystemAccessor;
/**
 * Java Mapping Parameter:
 * 		lookupChannel (Adapter - SOAP)
 * 		timeout (Simple Type - xsd:integer)
 */
public class SalesForceLogin extends AbstractTransformation {
	Channel lookupChannel;
	String sessionId = "";
	String prefix = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
			+ "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:con=\"xmlns:urn=urn:partner.soap.sforce.com\">"
			+ "<soapenv:Header> " + "<con:SessionHeader>" + "<con:sessionId>";
	String suffix = "</con:sessionId> </con:SessionHeader> </soapenv:Header> <soapenv:Body>";
	String envelope = "</soapenv:Body> </soapenv:Envelope>";
	String inputLine = "";
	String messageID = "";

	TransformationInput transInput = null;
	TransformationOutput transOutput = null;
	public AbstractTrace trace = getTrace();

	// Buffer do SessionID
	public static String bufferedSessionID = "";
	public static long lastTimestamp;
	public static long timeOut = 10000;
	
	public void transform(TransformationInput arg0, TransformationOutput arg1) throws StreamTransformationException {
		trace = (AbstractTrace) getTrace(); 

		timeOut = arg0.getInputParameters().getInt("timeout");	
		timeOut *= 1000; // convert to milliseconds
		trace.addInfo("Timeout: " + timeOut);
		lookupChannel = arg0.getInputParameters().getChannel("lookupChannel");	
		trace.addInfo("Channel: " + lookupChannel);
		
		messageID = arg0.getInputHeader().getMessageId(); // Message ID only available if run from PI.
		transInput = arg0;
		transOutput = arg1;
		this.execute((InputStream) arg0.getInputPayload().getInputStream(),
				(OutputStream) arg1.getOutputPayload().getOutputStream());
	}

	public void execute(InputStream in, OutputStream out) {
		try {
			getSessionId();
			copyPayload(in, out);
		} catch (Exception t) {
			t.printStackTrace();
		}
	}

	private void copyPayload(InputStream in, OutputStream out) throws StreamTransformationException {
		String c = "";
		String Newpayload = "";
		try {

			InputStreamReader inr = new InputStreamReader(in,StandardCharsets.UTF_8);
			BufferedReader reader = new BufferedReader(inr);
			String temp = "";
			while ((temp = reader.readLine()) != null) {
				c = c + temp;
			}
			int len = c.indexOf(">");
			Newpayload = c.substring(len + 1);
			
			out.write(prefix.getBytes());
			out.write(sessionId.getBytes());
			out.write(suffix.getBytes());
			out.write(Newpayload.getBytes(StandardCharsets.UTF_8));
			out.write(envelope.getBytes());
			
		} catch (IOException e) {
			throw new StreamTransformationException(e.getMessage());
		}
	}

	private void getSessionIdFromSFDC( ) {
		try {
			trace.addInfo("SessionId from LookUp: ");
			SystemAccessor accessor = null;
			accessor = LookupService.getSystemAccessor(lookupChannel);
			String loginxml = "<urn:login xmlns:urn=\"urn:partner.soap.sforce.com\"></urn:login>";
			InputStream inputStream = new ByteArrayInputStream(loginxml.getBytes());
			Payload payload = LookupService.getXmlPayload(inputStream);
			Payload SOAPOutPayload;
			SOAPOutPayload = accessor.call(payload);
			InputStream inp = SOAPOutPayload.getContent();
			
			DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = fac.newDocumentBuilder();
			Document document = builder.parse(inp);
			NodeList sessionId1 = document.getElementsByTagName("sessionId");
			Node node = sessionId1.item(0);
			if (node != null) {
				node = node.getFirstChild();
				if (node != null) {
					sessionId = node.getNodeValue();
					trace.addInfo("SessionId: " + sessionId);
				}
			}
			if (accessor != null)
				accessor.close();
		} catch (LookupException e) {
			getTrace().addDebugMessage(e.getMessage());
			e.printStackTrace();
		} catch (Exception e) {
			getTrace().addDebugMessage(e.toString());
			e.printStackTrace();
		}
		
	}

	private void getSessionIdFromBuffer( ) {
		sessionId = bufferedSessionID;
		if ( trace != null)
			trace.addInfo("SessionId from Buffer: " + sessionId);
	}
	
	private void getSessionId( ) {
		
		long currentTime = System.currentTimeMillis();
		
		if ( (currentTime - lastTimestamp) > timeOut ) {
			lastTimestamp = currentTime;
			getSessionIdFromSFDC();
			bufferedSessionID = sessionId;
		} else {
			getSessionIdFromBuffer();
		}
		
	}
	
	public static void main(String[] args) throws Exception {
		try {
			bufferedSessionID = "temp session id";
			SalesForceLogin obj = new SalesForceLogin();
			obj.getSessionIdFromBuffer();
			FileInputStream in = new FileInputStream("./input.xml");
			FileOutputStream out = new FileOutputStream("./output.xml");
			obj.copyPayload(in, out);

			System.out.println("Test Done!");
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

SoapLookup:

Channel channel = LookupService.getChannel("BUSINESS COMPONENT","COMMUNICATION CHANNEL");
SystemAccessor accessor = null;
accessor = LookupService.getSystemAccessor(channel);
String loginxml ="<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">+
   "<soapenv:Header/>"+
      "<soapenv:Body>" +
         "<urn:login>"+
             "<urn:username>"
                 + username
           + "</urn:username>"
              +<urn:password>"
               + password
               "</urn:password>"+
	  "</urn:login>"+
       "</soapenv:Body>"+
     "</soapenv:Envelope>"

Loginxml value string:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
	<urn:login>
          <urn:username>YOUR USER</urn:username>
        <urn:password>YOUR PASS</urn:password>
	</urn:login>
   </soapenv:Body>
</soapenv:Envelope>

InputStream receive the loginxml:

InputStream inputStream = new ByteArrayInputStream(loginxml.getBytes());

 

Basically, take a look in the explanation these 3 strings below the prefix, suffix and envelop build the string of soap envelop.

String prefix = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"xmlns:urn=urn:partner.soap.sforce.com\">"
+ "<soapenv:Header> " + "<urn:SessionHeader>" + "<urn:sessionId> ";
String suffix = "</urn:sessionId> </urn:SessionHeader> </soapenv:Header> <soapenv:Body>";
String envelope = "</soapenv:Body> </soapenv:Envelope>";

Writing a new payload, here in this point the code is creating a new payload and inputting the values

of sessionID and the IDOC payload as the body in the SOAP Envelop:

String suffix = “</urn:sessionId> </urn:SessionHeader> </soapenv:Header> <soapenv:Body>”;

out.write(Newpayload.getBytes()); — Is the result of the first mapping – the outputstream from theย  message mapping that will be included in the body of SOAPEnvelop string.

Result of java mapping – SOAPEnvelop:

Java code:
  /* End of remove xml version tag */
     out.write(prefix.getBytes());
     out.write(sessionId.getBytes());
     out.write(suffix.getBytes());
     out.write(Newpayload.getBytes());
     out.write(envelope.getBytes());

SOAP Envelop sample:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
	xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
	xmlns:con="xmlns:urn=urn:partner.soap.sforce.com">
	<soapenv:Header>
		<con:SessionHeader>
			<con:sessionId>temp session id</con:sessionId>
		</con:SessionHeader>
	</soapenv:Header>
	<soapenv:Body>
<!-- input.xml -->
	</soapenv:Body>
</soapenv:Envelope>

 

  • The trick point in operation mapping:

The operation mapping contains the parameter canalLookup, what it means?

Categories of Parameters in Parameterized Mapping:

  • Simple Type: Simple types can be used as either import or export parameters. Data types of parameters of Simple Type are xsd:string and xsd:integer.

 

  • Adapter: Parameter category โ€œAdapterโ€ can be only used as an Import parameter. Adapter parameters are mainly used when you are implementing a mapping lookup. For example, in a SOAP lookup scenario, you can set the SOAP communication channel name which is used for SOAP lookup function in the operation mapping.

Picture extract from the blog – Isuru FernandoBlog

In our case, it’s adapter because it will be used inside the java mapping during the runtime to execute the lookup and generate the SOAP Envelop with Header – session-id – e body from the Message Mapping that was executed before.

 

Operation Mapping detail with parameters:

maxTime

soapChannel

canalLookUp

Process steps โ€“ Directory:

  • ICO with configuration Details

Binding the parameters:

Binding the parameters – Setup in the Operation Mapping in the repository – maxTime and soapChannel

  • Configuration of receiver SOAP Login Channel:

Configuration of soapLoginRcv SOAP Channel:

 

What means this configuration module?ย 

Basically to don’t maintain a hardcode value in the java mapping and in case of user/pass expire or others situation I add the standard modules AF_Modules/PutPayloadValueBean for user and password and in this case the maintenance will be only in the adapter level instead of all need changes recompile and upload a new java mapping.

Think Easy ๐Ÿ˜‰

I hope that helps you to understand the soaplookup via java mapping with parameter in operation mapping (Channel) and the module PutPayloadValueBean.

Regards,

Viana.

 

2 Comments
You must be Logged on to comment or reply to a post.