Skip to Content
Technical Articles

Cloud Integration – Accessing and Setting SOAP Headers in an Integration Flow

A SOAP messages consists of SOAP headers and a SOAP body wrapped by a SOAP envelope. The SOAP body can be accessed and modified in the integration flow. With the 14-April-2019 release it is also possible to access SOAP headers received by a sender channel and to set SOAP headers to be sent to a receiver system.

This blog describes how SOAP headers can be processed in an integration flow.

Example Integration Flow

The example shows how SOAP headers can be set in the request and in the response message and how they can be evaluated within the integration flow. I use one integration process (Integration Process I) to build a message that contains an additional SOAP header. This process is started by a SOAP message that is received by a SOAP sender channel. Thus, you could use a tool like SOAPUI to create a SOAP message with an arbitrary SOAP body and send it to this channel. Another possibility to start a scenario would be to use a timer step and a content modifier to set an XML payload in the integration flow.

A second integration flow (Integration Process II) receives the request message with the added SOAP header, evaluates the content and adds another SOAP header for the response that is sent back to the starting Integration Process I. The response header is also evaluated.

Instead of using two integration processes one can also use two separate integration flows.

There are three new methods available in the Message interface to process SOAP headers:

  • Method getSoapHeaders: This method returns a list (type ArrayList) of SOAP headers represented by objects of type SoapHeader. This list is filled after a message was received by channel.
  • Method setSoapHeaders: This method can be used to set SOAP headers in a request or response message before the message is sent. The types used are the same as in setSoapHeaders: instances of SoapHeader in an ArrayList object are passed as parameter.
  • Method clearSoapHeaders: This method can be used to remove Soap headers that were received. Therefore, they will not be propagated to avoid undesired consequences. No parameter needs to be set, and the method returns no result.

Prerequisite

Due to the complex objects (a SOAP header is based on XML) the SOAP header processing can only be done in a script step. Therefore, some basic knowledge about Groovy script and how to create a script step in an integration flow is required to understand all aspects of this blog.

Integration Process I

A SOAP sender which could be a SOAP UI client or any other tool/system that can send SOAP messages sends a message to Integration Process I. Now I want to add a SOAP header to send the original payload including the added header to the SOAP receiver ‘Receiver’. Be aware that I use a Request-Reply call to the receiver system. The reason is I want to evaluate the response returned by the receiver system. For simplicity I use a receiver channel without WSDL configuration.

Let us assume I want or I must send a message identifier to the receiver system via SOAP header. I use the SOAP header that is defined for the SAP RM protocol. I have added the script step ‘SetRequestSoapHeader’ to add this header via Groovy script:

import com.sap.gateway.ip.core.customdev.util.Message;
import javax.xml.namespace.QName;
import com.sap.gateway.ip.core.customdev.util.SoapHeader;

def Message processData(Message message) {
    //Body 
       def headers = new ArrayList();
       def xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><messageId xmlns=\"http://www.sap.com/webas/640/soap/features/messageId/\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">urn:uuid:4CF78C4F-7395-9312-E100-00000A4286B3</messageId>";
       def header = new SoapHeader(new QName("http://www.sap.com/webas/640/soap/features/messageId/", "messageId"), xml, false, "");
       headers.add(header);
       message.setSoapHeaders(headers);
       return message;
} 

The goal is to create an instance of class com.sap.gateway.ip.core.customdev.util.SoapHeader. I use the constructor of the class that allows passing the header as string. For simplicity I use a constant string where the messageId is set. A variable messageId could be set using string operations. Be aware that you must mask double quotes (‘”’) as shown in this example. If the operations are more complex to obtain the header it can be beneficial to use a DOM parser/renderer to create the header and set an element instance instead of the string instance. The qualified name that identifies the type of the header is set by creating an instance of javax.xml.namespace.QName. The value for the SAP RM message id header is set. MustUnderstand is set to false. That means that if the receiver does not know the header he can ignore it. If I set MustUnderstand to true in our scenario  I must declare the header in the WSDL of the sender channel of Integration Process II. Actor is set to an empty string. In the integration flow Actor is not evaluated by the channel and can therefore be freely used for your own purposes. The header object is added to an array list and the method setSoapHeaders of the message interface is called. That is all. You can add other headers by using the same method at any place in your Integration Flow before you send the message.

Before I now move on to Integration Process II to see how this header is evaluated, let me continue this process and show how the response SOAP header is processed. As already mentioned I use a Request-Reply call to contact the receiver system. That means I can continue modelling the flow after calling the receiver channel and process the response.

The script step GetResponseSoapHeader is doing that:

import com.sap.gateway.ip.core.customdev.util.Message;
import com.sap.gateway.ip.core.customdev.util.SoapHeader;


def Message processData(Message message) {
       def headers = new ArrayList();
       headers = message.getSoapHeaders();
       if(headers.size !=1){
           throw new Exception("no SOAP header found");
       }
       def header = headers.get(0);
       qname=header.getQname();
       if(!qname.getNamespaceURI().equals("http://www.sap.com/webas/640/soap/features/messageId/")){
           throw new Exception("wrong namespace");
       }
       if(!qname.getLocalPart().equals("messageId")){
           throw new Exception("wrong name");
       }
       message.setHeader("SapMessageIdEx", header.getElement().getTextContent());
       message.clearSoapHeaders();
       return message;
} 

The method getSoapHeaders of the message interface returns the headers in an array list. First a few checks are executed. There should be exactly one SOAP header (headers.size()!=1) and then the qualified name (qname) should be the expected one, name and namespace must match. I use again the same header for the response as for the request. That means I receive a message id. The header representation in the com.sap.gateway.ip.core.customdev.util.SoapHeader instance is now an Element instance. The method getTextContent of Element returns a string representation of the header content and in this case the message id. For more complex header structures, the Element is just the starting point to a complex DOM tree and the DOM parser API must be used to get the desired information. As an example, I set the integration flow header SapMessageIdEx from the result. As a final step I delete the SOAP header received by calling the method clearSoapHeaders of the message interface. As a result, the header will not be propagated back to the sender.

Integration Process II

The message is received via SOAP sender channel. No WSDL is configured here. As a first step the SOAP header is evaluated in the script step GetRequestSoapHeader.

import com.sap.gateway.ip.core.customdev.util.Message;
import com.sap.gateway.ip.core.customdev.util.SoapHeader;


def Message processData(Message message) {
       def headers = new ArrayList();
       headers = message.getSoapHeaders();
       if(headers.size !=1){
           throw new Exception("no SOAP header found");
       }
       def header = headers.get(0);
       qname=header.getQname();
       if(!qname.getNamespaceURI().equals("http://www.sap.com/webas/640/soap/features/messageId/")){
           throw new Exception("wrong namespace");
       }
       if(!qname.getLocalPart().equals("messageId")){
           throw new Exception("wrong name");
       }
       message.setHeader("SapMessageIdEx", header.getElement().getTextContent());
       return message;
} 

In fact, the same steps as in the script step GetResponseSoapHeader were executed. It is checked whether the correct header was received, then the header is processed and afterwards the header is deleted. If the header is not deleted it will be sent back as part of the response in this scenario and Integration Process I will receive two message id headers.

The script step SetResponseSoapHeader sets a message id SOAP header that contains a different message id.

import com.sap.gateway.ip.core.customdev.util.Message;
import com.sap.gateway.ip.core.customdev.util.SoapHeader;
import javax.xml.namespace.QName;
def Message processData(Message message) {
       message.clearSoapHeaders();
       def headers = new ArrayList();
       def xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><messageId xmlns=\"http://www.sap.com/webas/640/soap/features/messageId/\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">urn:uuid:4CF78C4F-7395-9312-E100-00000A4286B4</messageId>";
       def header = new SoapHeader(new QName("http://www.sap.com/webas/640/soap/features/messageId/", "messageId"), xml, false, "");
       headers.add(header);
       message.setSoapHeaders(headers);
       return message;
} 

This step is very similar to the script SetRequestSoapHeader.

Execution

To see how the integration flow is executed, I enable MPL log level Trace and check the message content at the receiver channel:

When I now switch to the message content I see 3 messages. The first one is the original message:

The second one is the message after the additional SOAP header was added:

And the third message is the response message sent by Integration Process II:

Summary

You have learned how to set a SOAP header in a SOAP request and access it in an receiver integration flow using script steps. I showed the same for the SOAP response. The result of the change can be visualized using the existing MPL.

Please share feedback in comments

3 Comments
You must be Logged on to comment or reply to a post.
  • Hi Joerg,

    Thanks for the information! Waiting for the tenant to get the update 🙂

    cause: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
    script1__Script.groovy: 3: unable to resolve class com.sap.gateway.ip.core.customdev.util.SoapHeader

    Regards,

    Fatih

  • Hi Joerg Kessle,

     

    Any actualization for this scenario?.

    I have currently this error using XSLT Mapping

    “java.lang.IllegalArgumentException: The PayLoad elements cannot fit with the message parts of the BindingOperation. Please check the BindingOperation and PayLoadMessage.”

    Regards