Skip to Content
Author's profile photo Bhavesh Kantilal

HCI -Integrating SalesForce (SFDC) using HCI -Part 2

Background

In Part 1 of this blog series, a Login Integration Flow was created that persisted the sessionID, serverURL and sessionTime to the Global Variables of HCI.

  • How is this Integration Flow#1 used when the IDoc from SAP arrives to HCI?
  • How can a Global Variable be read from the HCI DataStore?
  • How easy is it to add SOAP Headers to your SOAP Message?

This blog covers this and the end to end flow. Read on!

Update#1: Currently ( As of Apr 12, 2016 ) HCI does not support the ability to set the SOAP URL Dynamically in the SOAP Receiver Adapter. Hence, at this moment, while the SOAP URL is persisted into the Global Variables, this is not used anywhere. The list of parameters supported Dynamically is listed here : HCI Dynamic Parameters

Nov 3, 2016 – The limitation described is no longer applicable. You can set the URL Dynamically as described in this blog : Dynamic address in the SOAP receiver adapter of HANA Cloud Platform, Integration Services by M.Jaspers

Integration Flow#2 – Material Master Replication Process

ProcessFlow

/wp-content/uploads/2016/04/1_920414.png

Step Type Description
Integration Flow Trigger
  • The Integration Flow is triggered using a IDoc Sender Channel.
  • The authentication mode is set to Basic Authentication.
Content Modifer
  • Content Modifier sets the Header properties SAP_ApplicationID, SAP_Sender, SAP_Receiver
  • SAP_ApplicationID is set to the IDoc number XPath to enable search for the Interface with the IDoc Number
  • SAP_Sender is set to SNDPRN XPath to enable set Sending System Details
  • SAP_Receiver is set to RCVPRN XPath to enable set Receiver System Details
  • This is an optional step and can be skipped.
Mapping
  • The Message Mapping to Map the Source IDoc to the UPSERT Webservice call is triggered.
  • This blog does not go into the details of this mapping and this should be covered in the Pre-Requiste section of the Part1 of this Blog series
Content Modifier
  • Set the Property – sessionID, sessionURL, sessionTime.
  • These Properties are read from the Global Variables persisted in the IntegrationFlow#1- Login.
  • Setting these properties in this step enables these to be accessible as local variables within the Integration Flow
Script
  • The script checks if the sessionID is valid. SalesForce sessionIDs are valid for 2 hours from the time the Login is made.
  • This Groovy script checks the if currentTime is within sessionTime+2 Hours
    • If yes, property sessionValid is set to True
    • If no, property sessionValid is set to False
  • In summary, this Script checks if the session continues to be valid and sets a Flag accordingly.
Gateway
  • If Session InValid -> Branch 1
  • If Session Valid -> Branch 2 (Default Branch)

SessionInValid –

Branch 1

Request Reply

  • Make a SOAP Call to the Integration Flow#1 Login
  • This Request Reply step triggers the call to the Integration Flow #1, if the session is Invalid and a Login call has to be made to get a new sessionID.
Content Modifier
  • Set the Property – sessionID, sessionURL, sessionTime.
  • These Properties are read from the Global Variables persisted in the IntegrationFlow#1- Login.
  • Setting these properties in this step enables these to be accessible as local variables within the Integration Flow
Script
  • Add sessionID to the SOAP Header
  • Perform certain XML Namespace Manipulations – Reasons for this are as described in the Prerequisite documents of Blog 1 of this series.
Request – Reply
  • Make the SOAP call to SalesForce to perform Upsert operation

 

Content Modifier

Set the Headers to enable monitoring with the corresponding IDoc Headers. The documentation on this can be read in the blog: End2End monitoring of HCI message flow in Standard Content – Made easy

/wp-content/uploads/2016/04/2_921393.png

Mapping

Mapping from the Source IDoc to the SFDC Upsert Request. The details of the mapping are not discussed in this blog.

/wp-content/uploads/2016/04/3_921394.png

Content Modifier

Content Modifier enables to set the Property sessionID,sessionURL,sessionTime. The values for these properties are read from their corresponding Global Variables.

/wp-content/uploads/2016/04/4_921416.png

Script

The below script is used to validate if the session is valid. If session is Valid, property sessionValid is set to “True” else set to “False”

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import java.util.Date;
import java.text.SimpleDateFormat;
def Message processData(Message message) {
  def messageLog = messageLogFactory.getMessageLog(message);
  def propertyMap = message.getProperties()
  //Read sessionTime from the Properties
  String sessionTime = propertyMap.get("sessionTime");
  //Set a Log Message for Monitoring purporses
  messageLog.setStringProperty("sessionTime ", sessionTime);
  // Convert sessionTime to a Calendar Date
  Calendar cal = Calendar.getInstance();
  SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
  cal.setTime(sdf.parse(sessionTime));
  // Add 2 Hours to  sessionTime
  Calendar cal1 = Calendar.getInstance();
  SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
  cal1.setTime(sdf1.parse(sessionTime));
  cal1.add(Calendar.HOUR_OF_DAY, 2);
  // Get Current Time
  SimpleDateFormat sdfDate = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");//dd/MM/yyyy
    Date now = new Date();
  String sessionValid = "";
  // Validate if session is Valid
  if(now.before(cal1.getTime())){
  sessionValid = "true";
  }else{
  sessionValid = "false";
  }
  message.setProperty("sessionValid",sessionValid);
  messageLog.setStringProperty("sessionValid ", sessionValid);
  return message;
}






 

Parallel MultiCast & Gateway

Parallel MultiCast is used to enable have a Join Step to merge the multiple branches of the Gateway Step. If you would like to understand these step types further would suggest reading the blog : Multicast Pattern in Integration Flows (HCI-PI)

Gateway Properties are as below

  • If sessionValid = ‘False’ -> Make a Request/Reply call to HCI Integration Flow #1 – Join Step
  • If sessionValid = ‘True’ -> Default Branch as no call to SFDC for Login is required –  Join  Step

/wp-content/uploads/2016/04/5_921417.png

Request Reply

As mentioned previously, this Request Reply step uses the SOAP Adapter to make a call back to Integration Flow#1.

The SOAP Adapter treats this like any other Webservice and the Proxy-Type used has been Set to “Internet”.

 

At this moment, I am not aware of any other means to trigger an Integration Flow of HCI and hence this approach has been documented.It would be great if a inbuilt feature is provided to do this to avoid traffic over the internet.

/wp-content/uploads/2016/04/6_921418.png

/wp-content/uploads/2016/04/7_921419.png

Content Modifier

This content modifier step performs the same tasks as one of the previous content modifier steps,i.e, enables to set the Property sessionID, sessionURL, sessionTime. The values for these properties are read from their corresponding Global Variables.

/wp-content/uploads/2016/04/4_921416.png

Script

This script is used to insert SOAP Headers into your Message. In this case, we insert the sessionID into the SOAP Header.

Note: This script also performs some rudimentary XMLNamespace manipulations for SFDC to address the WSDL Incompatibilities into your PI Graphical Mapping editor. There are better ways to do this including a XSLT / Parsing Technique. This blog does not delve into the details of the same.

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.cxf.binding.soap.SoapHeader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.sap.it.api.ITApiFactory;
import com.sap.it.api.securestore.SecureStoreService;
import com.sap.it.api.securestore.UserCredential;
import groovy.util.XmlSlurper;
def Message processData(Message message) {
  def body = message.getBody();
  def messageLog = messageLogFactory.getMessageLog(message);
  // Read SessionID from the Property
  def propertyMap = message.getProperties()
    String sessionID = propertyMap.get("sessionID");
  // Perform certain XMLNamespace manipulations. Note this is a very rudimentary mode of performing the same.
  // An XSLT or a Parser are better modes to do the same.
  String payload = message.getBody(java.lang.String);
  payload = payload.replaceAll('type','xsi:type');
  payload = payload.replaceAll('xmlns:ns0="urn:enterprise.soap.sforce.com"','xmlns:ns0="urn:enterprise.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"');
  payload = payload.replaceAll('xsi:type="Product2"','xsi:type="urn1:Product2"');
  message.setBody(payload);
   // Set SOAP Heeader sessionID
   DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
   dbf.setNamespaceAware(true);
   dbf.setIgnoringElementContentWhitespace(true);
   dbf.setValidating(false);
   DocumentBuilder db = dbf.newDocumentBuilder();
   Document doc = db.newDocument();
   Element authHeader = doc.createElementNS("urn:enterprise.soap.sforce.com", "SessionHeader");
   doc.appendChild(authHeader);
   Element clientId = doc.createElement("sessionId");
   clientId.setTextContent(sessionID);
   authHeader.appendChild(clientId);
   SoapHeader header = new SoapHeader(new QName(authHeader.getNamespaceURI(), authHeader.getLocalName()), authHeader);
   List  headersList  = new ArrayList<SoapHeader>();
   headersList.add(header);
   message.setHeader("org.apache.cxf.headers.Header.list", headersList);
   messageLog.setStringProperty("SessionID ", sessionID);
   return message;
}





Request Reply

 

Now that the SOAP Message is formed with the correct SessionID in the SOAP Header, the request reply is aimed at making the SOAP Call to SFDC to perform the corresponding operation on SFDC. Unlike in the PI world, where a Do Not Use SOAP Envelope mode is required, so such mode is required in HCI as HCI natively provides a mode to manipulate the SOAP Header.

/wp-content/uploads/2016/04/8_921421.png

Testing Your Flow

  • Prior to testing this Integration Flow, make sure that the Integration Flow#1 Login has been triggered atleast once manually through SOAP UI. Reason : This Integration Flow assumes that the Global Variables sessionID, sessionURL and sessionTime exist in the SAP Data Store.
  • Trigger the IDoc from SAP.

Results

  • IDoc Triggered at 8:53:56 AM, Processing Time ~ 6 Seconds
    • This Integration Flow makes the Call to the Login Integration Flow.
    • The Logs also show the call to the Login Integration Flow ( Once )

/wp-content/uploads/2016/04/9_921647.png

  • IDoc’s triggered at 8:53:58 and 8:54:00, Procesing Time ~ 1 Second
    • This Integration Flow re-uses the SessionID as the sessionID is valid.

/wp-content/uploads/2016/04/11_921428.png

Global Data Store Updated via the Login Integration Flow

/wp-content/uploads/2016/04/16_921444.png

 

Additional Details

  • Search using IDoc# works as required. Note as the variable was declared as a Integer only Number is to be used.
  • Likewise if variables is declared as a String, the same needs to be appended with zeroes or wild card prefix is to be used.

/wp-content/uploads/2016/04/12_921435.png

/wp-content/uploads/2016/04/13_921437.png

Logs Show the sessionID Status as per the Groovy Script

 

/wp-content/uploads/2016/04/15_921439.png

Final Note

The above 2 blogs show a means to implement a Integration with SFDC. Like in the PI world, there are multiple design options for this integration requirement. The idea behind this blog was not to delve into the various other options but show users how the most common approach used for persistence of sessionID’s can be implemented using HCI.

Other patterns / models can be added into the comment sections!

Assigned Tags

      13 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Vijay Kapuganti Kumar
      Vijay Kapuganti Kumar

      Thanks bhavesh...before this blog i got stucked up at creation of the soap headers.... now i will understand from your blog and i will try ... Regards, Vijay

      Author's profile photo Harish Mistri
      Harish Mistri

      Thanks Bhavesh for sharing the information.

      Author's profile photo Former Member
      Former Member

      Thanks for the great work, did you thought about another blog dealing with the UPDATE operation on Salesforce using HCI ?

      Author's profile photo Bhavesh Kantilal
      Bhavesh Kantilal
      Blog Post Author

      Hello Mohammed Amine,

       

      Can you explain more in terms of what you mean by a UPDATE operation?

      In this blog, i have illustrated a case where the data from SAP is replicated to SFDC using UPSERT operation.

       

      Do you mean to say a case where HCI Queries SFDC, pushes the data to SAP and then updates back to SFDC? Technically its just another iFlow. Is there some challenge you are facing there?

       

      Regards,

      Bhavesh

      Author's profile photo Former Member
      Former Member

      Yes Mr Bhavesh this is almost what I need to do. Let me explain more :

      I nees to achieve two main steps :

       

      1 - Getting opportunities from Salesforce and send them to SAP ByD

      2 - Update the status for those opportunities in Salesforce


      But for testing purposes I am dealing with Account instead of Opportunity, the query is working fine, I am selecting the account Name and I want to change it in HCI and  update it back to Salesforce.


      So I will need your help to figure out who can I update the account name that I selected previously.


      I am using the enterprise WSDL which contain an update operation I have no idea about what is required before calling this update operation via a request-reply .

       

      Thanks,

      BR.

      Mohamed Amine

      Author's profile photo Former Member
      Former Member

      Dear Bhavesh, I am currently reproducing your flow, and I am getting an error after trying to login again, is it a problem with the first login flow ?


      This is the error :

       

       

      Processing exchange ID-vsa1129456-od-sap-biz-56095-1462637259951-477-6 in cxf:bean:HCI_Login_{

      Error               = org.apache.camel.InvalidPayloadException: No body available of type: org.apache.camel.component.cxf.CxfPayload on: Message: [Body is not logged]. Caused by: No type converter available to convert from type: null to the required type: org.apache.camel.component.cxf.CxfPayload with value null. Exchange[ID-vsa1129456-od-sap-biz-56095-1462637259951-477-6][Message: [Body is not logged]]. Caused by: [org.apache.camel.NoTypeConversionAvailableException - No type converter available to convert from type: null to the required type: org.apache.camel.component.cxf.CxfPayload with value null], cause: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: null to the required type: org.apache.camel.component.cxf.CxfPayload with value null

       

      Any idea about this error ?

      Thanks.

      BR.

      Mohamed Amine.

      Author's profile photo Bhavesh Kantilal
      Bhavesh Kantilal
      Blog Post Author

      Hello Mohammed Amine,

      The error seems to be because you have triggered a Login call as your session ID has expired and the Login Integration flow has failed.

      I would suggest you raise this in the SCN Forum as this could be a long topic of discussion but for a quick check this is what I would do,

       

      - Check which step is failing by enabling Trace.

      - Check what is the input to that step and if the previous step provided the right input.

      - I think somewhere the contentModifer step is incorrect where the Body is set and that is causing the issue.

       

      Regards,
      Bhavesh

      Author's profile photo Former Member
      Former Member

      It was because there was no message inside the body payload, I added some content and I got a new error :

      Cxf.EndpointAddress = https://XXXX/cxf/SF/Login

                                                                                                                            Error               = Inbound processing in endpoint at https://XXXX/cxf/SF/Login failed with message "Fault:Could not send Message.", caused by "HTTPException:HTTP response '401: Unauthorized' when communicating with https://XXXX/cxf/SF/Login"

       

      I added the tenant certificate to the systems.jks. But still getting the same error Unauthorized' when communicating with https://XXXX/cxf/SF/Login

      Author's profile photo Pragya dubey
      Pragya dubey

      Thanks a lot Bhavesh, for such a useful piece of information.

      Author's profile photo Markus Schalk
      Markus Schalk

      Dear Bhavesh,

      Excellent blog post! Really boosted my implementation within CPI!

      I added one variable called sessionSecondsValid (XPath: /loginResponse/result/userInfo/sessionSecondsValid). Great to set the validity dynamically. In our case, it's 24 hours at the moment.

      One question:

      How exactly does the IFlow behave after the join step? I can see in trace, that multiple messages reach the content modifier step. Does it just take the first arrival?

      I did not find any information about this in the documentation.

      Many thanks in advance!

      Best regards

      Markus

      Author's profile photo Anand S
      Anand S
      Dear Bhavesh,
      The blog is extremely helpful in understanding the now standard OAuth 2.0 based authentication.

      I have a requirement for OAuth 2.0 based authentication. I have to send the session_id as part of the message to get the authorization code. The authorization code is then sent to the token URL to fetch the OAuth token. What I am not able to do is to grab the cookie from the response and set it to the outgoing message. Below is the requirement. Please, can you tell me how I can achieve it.

      you need to send a HTTP POST request to Auth Server.
      Url format:

      https://{ServiceHost}:{AuthServicePort}/oam/server/auth_cred_submit

      • Parameters:
        • {ServiceHost}: this emulated service host
        • {AuthServicePort}: 443 by default

      Request headers MUST include:

      Content-Type: application/x-www-form-urlencoded
      Cookie: {CookieFromLastStep}

       

      Kind regards,

      Anand S

       

       

      Author's profile photo AHMAD RAJA
      AHMAD RAJA

      Excellent blog post! Really appreciated

      Author's profile photo Aswini Alapati
      Aswini Alapati

      @bhavesh.kantilal,

       

      Wonderful Blog. I have developed the IFLOW as mentioned in the blog but while doing the upsert operation I am getting Unknown Host exception.

       

      I have used the same URL which had been returned during Login process. Looking for any helpful inputs.

       

      Thanks in advance,

      Aswini.