Last Updated on 21/02/2022 by Patryk Bandurski
SAP is a well-known company that offers many products, mainly from the ERP domain. It is a matter of time when you need to write an integration with any SAP products. In this article, we do not use any existing connectors like the Mule 4 SAP connector. We consume exposed SOAP Web Service with an asynchronous (one-way) operation. I had some difficulties in this area, mainly focusing on WS-Reliable Messaging. Below, you will find what I struggled with and how I overcame the obstacles.
WS-Reliable Messaging
Let’s start with a quick reminder of what WS-Reliable Messaging is and how it works. WS-RM is one of the WS-* specifications. It focuses on assuring the reliable delivery of the message in the distributed environment. In WS-RM specification, we have three operations:
- StartSequence – initiate reliable message delivery
- CloseSequence – close successful message delivery
- TerminateSequence – cancel the message, indicating failure
Below you can see a sequence diagram that illustrates simple successful dance:
- Initiate the message delivery with Create Sequence
- We receive in the CreateSequenceResponse unique id in Identifier element
- In steps three and four we call web service Operation with Identifier and Message Number
- In the fifth step, we call web service Operation with Identifier, Message Number, and AckRequsted header – it indicates that we would like to receive an acknowledgment. It is sent in SequenceAcknowledgement
- In the last step, if everything is successful, we close the sequence with CloseSequence operation.
WS-RM in Mule 4
To perform a reliable dance, we can use either Web Service Consumer or HTTP Request. When we pick Web Service Consumer, we need to consider that the operation needs to be selected. Still, your web service won’t explicitly have an operation like CreateSequence or CloseSequence. Of course, no need to worry it will work with these operations.
On the other hand, we can use HTTP Request and configure it to send SOAP Envelopes with the Create, Close, and Terminate Sequence. I prefer this one because I do not need to select any operation and see the difference in the Anypoint Studio canvas. As the Sequence calls are purely technical once.
Mule – SAP integration case
I want to consume asynchronous operation published by SAP. This operation accepts many objects like Business Partner and enables us to send it within one call. We already know that asynchronous operation would imply sending at least two additional calls to the same service such as Create and Close Sequence. Below you can see the call sequence.
- Initiate the reliable message delivery via CreateSequence operation. The Identifier from the response is saved for later usage.
- Send business operation with headers required by WS-RM
- Close the sequence by sending CloseSequence with the saved identifier.
This is the flow, representing the same call sequence as above.
Before implementing the service, I play with the third-party system via a tool like Postman or SopaUI. However, this time it did not work as smoothly as I expected. As the SAP service has basic authentication turned on, we couldn’t use the simple switch to send RM under the hood. Instead, I needed to create two additional requests by myself.
Create Sequence
The CreateSequence for our SAP service looks similar to the SOAP envelope below. In line 4, we specify a unique message identifier, and then in line 5, we set the full address to our SAP Web Service.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2004/08/addressing">
<wsa:Action>http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</wsa:Action>
<wsa:MessageID>uuid:cf0547b1-667d-4ce8-a34e-14e809a88661</wsa:MessageID>
<wsa:To>https://host:port/path</wsa:To>
</soapenv:Header>
<soapenv:Body xmlns:wsrm="http://docs.oasis-open.org/ws-rx/wsrm/200702">
<wsrm:CreateSequence>
<wsrm:AcksTo xmlns:wsa="http://www.w3.org/2004/08/addressing">
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous?id=ee3baeee-42fb-41e9-98e6-35988b3eb6a3</wsa:Address>
</wsrm:AcksTo>
</wsrm:CreateSequence>
</soapenv:Body>
</soapenv:Envelope>
You need to be aware that the anonymous endpoint is supported by SAP. Therefore we use in line 10, role/anonymous endpoint.
As I wrote earlier, I use the HTTP Request component. So I need to create a global configuration that will point to the SAP Web Service and pass the basic authorization. Next, I put the POST request in my flow with the following payload.
output application/xml
ns ns0 http://docs.oasis-open.org/ws-rx/wsrm/200702
ns ns1 http://www.w3.org/2004/08/addressing
ns ns2 http://schemas.xmlsoap.org/soap/envelope/
---
{
ns2#Envelope: {
ns2#Header: {
ns1#Action: "http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence",
ns1#MessageID: "uuid:$(uuid())",
ns1#To: "https://host:port/path"
},
ns2#Body: {
ns0#CreateSequence: {
ns0#AcksTo: {
ns1#Address: "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous?id=$(uuid())"
}
}
}
}
}
In the response, we receive the Identifier element that must be populated in the operation call and close/terminate request. We can save it in a variable. Here is the full path to the identifier.
payload.ns0#Envelope.ns0#Body.ns1#CreateSequenceResponse.ns1#Identifier
Business operation call
We can now call Web Service operation. Setting the body part is strictly related to your operation. So I won’t describe it here. What does matter are headers. We need to provide the identifier. Below you can see part of the SOAP Envelop that should be sent with the request.
<soapenv:Header>
<wsrm:Sequence xmlns:wsrm="http://docs.oasis-open.org/ws-rx/wsrm/200702">
<wsrm:Identifier>urn:uuid:fabcdbd8-4d20-1eda-a2af-1430e4950ce1</wsrm:Identifier>
<wsrm:MessageNumber>1</wsrm:MessageNumber>
</wsrm:Sequence>
</soapenv:Header>
In the Identifier, we place the value received from the CreateSequence. In the Web Service Consumer we can set SOAP Headers like that:
output application/xml
ns ns0 http://docs.oasis-open.org/ws-rx/wsrm/200702
---
{
headers: {
ns0#Sequence: {
ns0#Identifier: vars.identifier,
ns0#MessageNumber: vars.messageNo
}
}
}
Close Sequence
The call to close the sequence is similar to the OpenSequence. We need to provide the Identifier that we got in the first request.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2004/08/addressing">
<wsa:Action>http://schemas.xmlsoap.org/ws/2005/02/rm/CloseSequence</wsa:Action>
<wsa:MessageID>uuid:cf0547b1-667d-4ce8-a34e-14e809a88661</wsa:MessageID>
<wsa:To>https://host:port/path</wsa:To>
</soapenv:Header>
<soapenv:Body xmlns:wsrm="http://docs.oasis-open.org/ws-rx/wsrm/200702">
<wsrm:CloseSequence>
<wsrm:Identifier>urn:uuid:fabcdbd8-4d20-1eda-a2af-1430e4950ce1</wsrm:Identifier>
</wsrm:CloseSequence>
</soapenv:Body>
</soapenv:Envelope>
In the response, we will receive message acknowledgment. To call CloseSequence I use the HTTP Request component. DataWeave expression preparing the Evenlope is presented below.
output application/xml
ns ns0 http://docs.oasis-open.org/ws-rx/wsrm/200702
ns ns1 http://www.w3.org/2004/08/addressing
ns ns2 http://schemas.xmlsoap.org/soap/envelope/
---
{
ns2#Envelope: {
ns2#Header: {
ns1#Action: "http://schemas.xmlsoap.org/ws/2005/02/rm/CloseSequence",
ns1#MessageID: "uuid:$(uuid())",
ns1#To: "https://host:port/path"
},
ns2#Body: {
ns0#CloseSequence: {
ns0#Identifier: vars.identifier
}
}
}
}
Summary
The first contact with the SAP Web Service can be overwhelming, especially when you start looking at the suuuuuper long WSDL document. We need to remember that when SAP exposes the asynchronous operation by default, it requires WS Reliable Messaging usage. This means that we need to do at least two additional calls like Create/Close/Terminate Sequence. Of course, this may happen when you work with older SAP products.