Genivia Home Documentation
The WS-ReliableMessaging plugin

updated Tue May 21 2024 by Robert van Engelen
 
The WS-ReliableMessaging plugin

Table of Contents

WS-ReliableMessaging Setup

The material in this section relates to the WS-ReliableMessaging and WS-Addressing (2005) specifications.

To use the wsrm plugin:

  1. Run wsdl2h -b -t typemap.dat on a WSDL of a service that requires WS-ReliableMessaging and WS-Addressing headers. The typemap.dat file included in the gSOAP package is used to recognize and translate header blocks. The -b option ensures that one-way response message operations are added for duplex communications that require a client-side callback service.
  2. Run soapcpp2 -a on the header file produced by wsdl2h. To enable reliable-messaging and addressing-based service operation selection, you MUST use soapcpp2 option -a. This allows the service to dispatch methods based on the WS-Addressing action information header value (when the wsa plugin is registered).
  3. (Re-)compile and link stdsoap2.c/pp or libgsoap, (dom.c/pp when needed), plugin/wsrmapi.c, plugin/wsaapi.c, custom/duration.c, and the soapcpp2- generated source files.
  4. Use the wsrm plugin API functions described below. The wsrm plugin uses the wsa plugin to implement the WS-Addressing 2005 operations. Both must be registered. The wsrm plugin API is self-contained. There is no need to use the wsa plugin API, unless WS-Addressing-specific headers must be added to messages.
  5. For WCF compatibility and interoperability, compile wsrmapi.c with compiler flag -DWITH_WCF for enhanced WCF compatibility and use compiler flag -DWITH_WCF_SIM to enable simulated WCF features channel instance and netrm BufferRemaining.

An example wsrm client/server application can be found in samples/wsrm.

A gSOAP service definitions header file with an #import "wsrm.h" to support WS-ReliableMessaging 1.1 or #import "wsrm5.h" to support WS-ReliableMessaging 1.0 (2005). The imports are automatically generated by wsdl2h for a set of WSDLs that use WS-ReliableMessaging as per WS-Policy. The wsdl2h-generated header file should be further processed by soapcpp2 to generate the binding code. The wsrmapi.h and wsrmapi.c implement the WS-ReliableMessaging API described in this document.

A wsdl2h-generated service definitions header file might include the following imports, where the wsrm.h is mandatory to support WS-ReliableMessaging:

#import "soap12.h"
#import "wsrm.h"

The wsrm.h header file is imported from import/wsrm.h by soapcpp2. The wsrm.h import can be manually added to enable WS-ReliableMessaging when needed. The gSOAP service definitions header file is processed with soapcpp2 to generate the client-side and/or server-side binding code.

Note that the wsrm.h and the WS-ReliableMessaging-dependent wsrx.h and wsa5.h header files are located in the import directory of the gSOAP package. These files define the WS-ReliableMessaging and WS-Addressing information header elements and types. The wsrx.h header file defines the WS-ReliableMessaging CreateSequence, CloseSequence, and TerminateSequence operations, as well as an one-way SequenceAcknowledgement operation to accept acknowledgements. The soap12.h header file enables SOAP 1.2 messaging.

For developers working on protocols: the WS-ReliableMessaging header blocks in wsrm.h were generated from the WS-ReliableMessaging schema with the wsdl2h tool and WS/WS-typemap.dat as follows:

wsdl2h -cgyex -o wsrm.h -t WS/WS-typemap.dat WS/WS-ReliableMessaging.xsd

This step is not needed to use the wsrm plugin.

Note
The WS-ReliableMessaging and WS-Addressing protocols do not use SOAP-RPC encoded id-ref attributes. To remove id-ref serialization e.g. when using document/literal SOAP/XML messaging, use the runtime SOAP_XML_TREE flag or compile the source code with WITH_NOIDREF.

WS-ReliableMessaging Information Header Bindings

To associate WS-ReliableMessaging and WS-Addressing information headers with service operations, the SOAP Header struct SOAP_ENV__Header must have been declared and contain the necessary header blocks to be transported with SOAP messages. The SOAP_ENV__Header for WS-ReliableMessaging and WS-Addressing is predefined in wsrm.h and imported into the gSOAP service definitions header file (this is automatically generated by wsdl2h). For each service operation in the gSOAP service definitions header file that uses WS-ReliableMessaging and/or WS-Addressing method-header-part directives are used.

For example, the following gSOAP service definitions header file illustrates a typical import and service operation definition of operation 'example' in service namespace 'ns':

#import "wsrm.h"
//gsoap ns service method-header-part: example wsa5__MessageID
//gsoap ns service method-header-part: example wsa5__RelatesTo
//gsoap ns service method-header-part: example wsa5__From
//gsoap ns service method-header-part: example wsa5__ReplyTo
//gsoap ns service method-header-part: example wsa5__FaultTo
//gsoap ns service method-header-part: example wsa5__To
//gsoap ns service method-header-part: example wsa5__Action
//gsoap ns service method-header-part: example wsrm__Sequence
//gsoap ns service method-header-part: example wsrm__AckRequested
//gsoap ns service method-header-part: example wsrm__SequenceAcknowledgement
//gsoap ns service method-action: example urn:example/examplePort/example
//gsoap ns service method-output-action: example urn:example/examplePort/exampleResponse
int ns__example(char *in, struct ns__exampleResponse { char *out; } *);

The wsa5 information headers are defined in wsa5.h and imported by wsrm.h (both are located in the 'import' directory). Here, all of the WS-Addressing and WS-ReliableMessaging information headers are bound to the ns__example operation request and response messages.

The method action directive is important for WS-Addressing, because WS-Addressing Action information headers must be included that are unique for each operation. The soapcpp2 option -a ensures that WS-Addressing Action header blocks (and HTTP Action headers) are processed at the receiving side, which means that the service dispatcher uses the Action together with the operation name to invoke the service operation at the destination. This also means that Action headers must be properly set by the client.

Note: the SOAP_ENV__Header struct can be declared in multiple files. The soapcpp2 tool gathers all members of the structs into the "final" SOAP_ENV__Header struct used by the gSOAP engine and your application. This is convenient when service-specific header blocks are combined with WS-ReliableMessaging and WS-Addressing header blocks or when WS-Security header blocks are added by the WSSE plugin.

WS-ReliableMessaging Overview

In this section a brief overview of WS-ReliableMessaging is given. For more details please refer to the WS-ReliableMessaging protocol or tutorials on the subject. The following introduces the basic concepts of WS-ReliableMessaging from a practical point of view.

WS-ReliableMessaging is useful to improve the reliability of one-way asynchronous messaging, for unreliable data gram messaging (SOAP-over-UDP), or to improve reliable delivery of responses relayed to other destinations, such as response messages that are relayed to destinations indicated by the WS-Addressing ReplyTo header. The protocol is also useful when multiple sources are sending messages that arrive out of order or must be flagged as an incomplete message collection when messages are missing as defined by the notion of a collection of related messages. Messages delivered out-of-order are not internally reordered automatically. The application logic should be designed to be robust to out-of-order delivery effects, for example by using a vector to collect the data elements received before the data is processed. Alternatively, the server-side soap_wsrm_check_and_wait calls can be used to let the current thread wait until preceding messages have been received and processed (by other threads). For single-thread processing with keep-alive, this will cause a performance bottleneck unless the message queue plugin is used. The message queue plugin (mq.c) provides an approach to buffer inbound messages for in-order processing.

WS-ReliableMessaging is not essential to improve the reliability of request-response message exchanges between two parties over HTTP, since a successful delivery of a request message can be inferred from the fact that a response was received for the request sent.

WS-ReliableMessaging "protects" message sequences, i.e. a collection of related messages. A WS-ReliableMessaging message sequence is created after which the sequence of messages is sent. The sequence is closed and terminated by the client after the last message. Either the message sequence is complete or not, and the resulting action to discard the incomplete message sequence or not depends on the chosen behavior. Duplicate messages (e.g. resulting from resends) are always discarded.

To create a new sequence, a client (WS-RM source) requests from the server (WS-RM destination) a unique (new) message sequence identification. The server responds with the identification to be used as well as other details, such as the expiration time of the sequence and the behavior implemented when a sequence was received incomplete:

When the client terminates the sequence, it first sends a sequence close request (or a last message with the older WS-RM 1.0) and then a sequence termination request to the destination server. The sequence close informs the server how many messages should have been received. The client can still resend messages after the close, but no new messages are supposed to be send. After the optional resends, the client requests termination of the sequence. The termination will be successful depending on the behavior when messages went missing, as was listed above.

The ensure reliable delivery, the WS-ReliableMessaging protocol allows the client to resend messages. Message resends are desirable when messages are lost in transit. Since the client has limited information on delivery success (message delivery acknowledgments can get lost as well), the client may resend more messages than necessary. This could lead to message duplication. However, messages that were already received by the server are discarded.

The client may request message delivery acknowledgements from the server. The server sends message receipt acknowledgements for all the messages it has received in the sequence back to the client, usually by piggy-backing them with the response of a subsequent message exchange. When the client is informed about successful delivery it reduces the number of resends the client will attempt.

Messages in a sequence are uniquely identified by their enumeration number in the sequence. Messages may be transmitted out of order. A missing message number indicates a gap in the message sequence. Message receipt acknowledgements consist of ranges of message numbers. Acknowledgements are normally sent to the source to help identify which messages should be resend.

With the WS-Addressing protocol, message responses and fault messages can be relayed to other destinations. The ReplyTo and FaultTo WS-Addressing header blocks are used for this purpose. The WS-ReliableMessaging protocol allows message acknowledgements to be relayed. The WS-ReliableMessaging AcksTo header block is used for this purpose.

In all, there are four types of communicating peers that are visible to the source (the client):

The destination service normally serves as ReplyTo, FaultTo, and AcksTo service, which is the default scenario with request-response message exchanges. Replies, faults, and acknowledgements can also be asynchroniously transmitted in duplex mode. In that case, the WS-RM source client acts as a callback service to accept messages on a port.

The practical aspects of message sequence creation, the message exchanges, the message relays, and sequence close/termination are presented for the client side first and then for each of the four types of destination servers.

Client-side Usage

Creating, Closing, and Terminating Message Sequences

A sequence is created, closed, terminated, and cleaned-up on the client side as follows, using a 'soap' context struct (use one 'soap' context per thread):

struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap'
soap_register_plugin(soap, soap_wsa);
soap_register_plugin(soap, soap_wsrm);
const char *destination = "..."; // WS-RM destination server address
const char *source = NULL; // WS-RM source (NULL means current)
ULONG64 expires = 10000; // 10000 ms to expire (10 seconds)
const char *id = NULL; // id = NULL: generate a temp sequence ID
const char *opt_msg_id = NULL; // WS-Addressing message ID (optional)
soap_wsrm_sequence_handle seq; // a local handle to the sequence state
// Step 1: create a sequence
if (soap_wsrm_create_offer(soap, destination, source, id, expires, NoDiscard, opt_msg_id, &seq))
{
soap_wsrm_seq_free(soap, seq);
... // error creating sequence
}
// Step 2: exchange messages with WS-RM destination, request acks, receive acks, issue resends (see later)
...
// Step 3: optionally close first before terminating
if (soap_wsrm_close(soap, seq, NULL))
{
soap_wsrm_seq_free(soap, seq);
... // error closing sequence
}
// Step 4: optionally resend messages before terminating
if (soap_wsrm_nack(seq)) // any non-acks for messages sent?
soap_wsrm_resend(soap, seq, 0, 0); // resend all non-acked messages
// Step 5: terminate
if (soap_wsrm_terminate(soap, seq, NULL))
{
soap_wsrm_seq_free(soap, seq);
... // error creating sequence
}
// Step 6: cleanup
soap_wsrm_seq_free(soap, seq);

For duplex communication, the responses are sent to the WS-RM source's port, to which we need to listen via a callback service. There are two ways to do so, by polling or by running a separate callback server thread that accepts responses.

In the code below we use a polling approach to process responses sent asynchronously to the WS-RM source:

struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap'
soap_register_plugin(soap, soap_wsa);
soap_register_plugin(soap, soap_wsrm);
const char *destination = "..."; // WS-RM destination server address
const char *source = "..."; // WS-RM source address destination sends to
int source_port = ...; // port of WS-RM source address
ULONG64 expires = 10000; // 10000 ms to expire (10 seconds)
const char *id = NULL; // id = NULL: generate a temp sequence ID
const char *opt_msg_id = NULL; // WS-Addressing message ID (optional)
int retry;
soap_wsrm_sequence_handle seq; // a local handle to the sequence state
struct soap *callback = soap_new(); // callback for polling
// Step 1: create a WS-RM source port binding for the callback to poll messages
if (!soap_valid_socket(soap_bind(callback, NULL, source_port, 100)))
... // error
// Step 2: create a sequence
if (soap_wsrm_create_offer(soap, destination, source, id, expires, NoDiscard, opt_msg_id, &seq))
{
if (soap->error != 202) // Error != HTTP Accepted
{
soap_wsrm_seq_free(soap, seq);
... // error creating sequence
}
}
// poll 10 times for 1 second until the sequence created response received
for (retry = 10; retry && !soap_wsrm_seq_created(soap, seq); retry--)
if (callback_poll(callback, 1)) // 1 second poll
... // error
if (!retry)
... // error
// Step 3: exchange messages with WS-RM destination, request acks, receive acks, issue resends, and poll (see later)
...
// Step 4: optionally close first before terminating
if (soap_wsrm_close(soap, seq, NULL))
{
if (soap->error != 202) // Error != HTTP Accepted
{
soap_wsrm_seq_free(soap, seq);
... // error closing sequence
}
}
if (callback_poll(callback, -500000)) // 500 ms poll
... // error
// Step 5: optionally resend messages before terminating
if (soap_wsrm_nack(seq)) // any nacks?
{
soap_wsrm_resend(soap, seq, 0, 0); // resend all non-acked messages
if (callback_poll(callback, -100000)) // 100 ms poll
... // error
}
// Step 6: terminate
if (soap_wsrm_terminate(soap, seq, NULL))
{
if (soap->error != 202) // Error != HTTP Accepted
{
soap_wsrm_seq_free(soap, seq);
... // error creating sequence
}
}
// only needed with WS-RM 1.1 to accept terminate response
if (callback_poll(callback, -500000)) // 500 ms poll
... // error