User Guide for Apache Sandesha

This document  will guide you through the configuration of Sandesha and finally to run the sample scenarios.

Configuration

Sandesha can be downloaded as binary or source distributions. This document assumes that the user has downloaded the binary distribution, however if you have downloaded the source distribution, you can create the binaries by simply running maven goal "dist-bin".

Following steps will guide to configure Apache Sandesha on both client and server sides.

Use Sandesha in the Client Side

Following two steps will enable the usage of Sandesha in the Client Side.

There are two main ways that one can use axis to write web service client applications.

  1.  Using Generated Stubs
  2.  Using Dynamic Invocation Interface (DII)

At the moment there is no way that we can inform the apache tool (wsdl2java) to generate stubs that are Sandesha aware and hence we need to use the DII in order to use Sandesha in the client side. However if there are existing stubs, then to use Sandesha with them  the user has to manually edit the generated stubs to insert properties to the call object.

Simple Request-Response Client

To use Sandesha in the client side, user has to add few lines to the existing client. Following code fragment shows the required additions that one should make.

public static void main(String[] args) {
	try {
	Service service = new Service();
	Call call = (Call) service.createCall();
	SandeshaContext ctx = new SandeshaContext();
	ctx.initCall(call, targetUrl,
	"urn:wsrm:echoString",Constants.ClientProperties.IN_OUT);
	call.setOperationName(new QName("http://tempuri.org/", "echoString"));
	call.addParameter("Text", XMLType.XSD_STRING, ParameterMode.IN);
	call.addParameter("Seq", XMLType.XSD_STRING, ParameterMode.IN);
	call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);

	ctx.setLastMessage(call);
	String ret = (String) call.invoke(new Object[]{"Sandesha Echo 1", "abcdef"});
	System.out.println("The Response for First Messsage is :" + ret);

	ctx.endSequence();
	} catch (Exception e) { e.printStackTrace();
	}
}

Explanation on Additions :

SandeshaContext ctx = new SandeshaContext();

This is the initial step in initializing the  RMSource (Client side ReliableMessaging Endpoint). SandeshaContext is used to keep track of different sequences that the client may create throughout the its lifecycle. Client can add any number of call objects to the context.

ctx.initCall(call,targetUrl , "urn:wsrm:Ping", Constants.ClientProperties.IN_ONLY);

Using this step the client can add the call object to the SandeshaContext, with targetUrl,<wsa:Action> and the Message Exchange Pattern (MEP) as the parameters. Target URL and the action are self-explanatory but may be not the MEP. MEP informs Sandesha end point the behavior of the message exchange expected by this call object. In other words it helps SandeshaContext to understand whether the call object is used for a "Request/Response"  invocation or "Request Only" invocation.
 

ctx.setLastMessage(call);

This is to inform the RMSource that this message is the last message of a particular sequence. Meaning of this statement may not be trivial in the case of a single message scenario as above, however this is a "REQUIRED" property for RMSource  and this will add the <wsrm:LastMessage> header to the outgoing message.

ctx.endSequence();

This is the last statement relating to this invocation sequence. RMSource will wait till all the messages been acknowledged and the terminate sequence for this particular sequence is sent, before returning the control back to the invocation party (that is client). The wait time depends on the policy assertion InactivityTimeout( <wsrm:InactivityTimeout Milliseconds="60000" /> ). If this is the final sequence that is available for stopping then the context will wait till all the messages are sent and the responses (if any) are received and will stop the client side listener and clean up the Sandesha Queue.

Note : These are the minimum changes that one should do in-order to use Sandesha in the client side. There are some other additions that one can use to get more flexibility in Sandesha and we will see them in the following examples.

Simple One-Way Client

In this scenario we will look at how Sandesha can be used in a one-way messaging operation. Again the following code fragment will explain the additional code lines required. For this we use a client that will invoke a service 3 times. (This for the explanation purpose only)

public static void main(String[] args) {
	try {
	Service service = new Service();
	Call call = (Call) service.createCall();

	SandeshaContext ctx = new SandeshaContext();
	ctx.initCall(call,targetUrl ,
    	                "urn:wsrm:Ping", Constants.ClientProperties.IN_ONLY);
	call.setOperationName(new QName("http://tempuri.org/", "ping"));
	call.addParameter("arg1", XMLType.XSD_STRING, ParameterMode.IN);	
            
	call.invoke(new Object[]{"Sandesha Ping 1"});
	call.invoke(new Object[]{"Sandesha Ping 2"});
	ctx.setLastMessage(call);	
	call.invoke(new Object[]{"Sandesha Ping 3"});
	ctx.endSequence();

	} catch (Exception e) {
	   e.printStackTrace();
	}
}

As it can be seen, we need the same additions as above and the explanations are the same.

Simple One Way Client (Synchronous Acknowledgements)

In this scenario we will look at how Sandesha can be used in a one-way messaging operation using same transport channel to get acknowledgements.  For this we use a client that will invoke a service 3 times. (This for the explanation purpose only)

public static void main(String[] args) {
	try {
	Service service = new Service();
	Call call = (Call) service.createCall();

	SandeshaContext ctx = new SandeshaContext(Constants.SYNCHRONOUS);
	ctx.addNewSequeceContext(call,targetUrl ,
		"urn:wsrm:Ping", Constants.ClientProperties.IN_ONLY);

	call.setOperationName(new QName("http://tempuri.org/", "ping"));
	call.addParameter("arg1", XMLType.XSD_STRING, ParameterMode.IN);

	call.invoke(new Object[]{"Sandesha Ping 1"});
	call.invoke(new Object[]{"Sandesha Ping 2"});
	ctx.setLastMessage(call);
	call.invoke(new Object[]{"Sandesha Ping 3"});
	ctx.endSequence(call);

	} catch (Exception e) {
   		e.printStackTrace();
	}
}

The only change we need is to set the flag "sync" to "true" when we add the sequence to SandeshaContext, as shown bellow.

SandeshaContext ctx = new SandeshaContext(Constants.SYNCHRONOUS);

By these, client can inform the RMSource, not to start a separate client side listener and send the requests with "anonymous URI" in <wsa:From> and <wsrm:AcksTo>  headers. So the server will use the same transport connection to send Create Sequence Response and Acknowledgements.

Note: Please note that this can only be used for One-way operations. If the web service request has a response then Sandesha always use a separate listener to retrieve the response back.

Overriding the wsa:Addressing and Other Parameters from Client

In this section we will look at how we can override the properties specific to Sandesha from the client code itself. Sandesha provides complete flexibility to the user by allowing them to override properties that are used to set various message headers. However it is important to note that the user should set appropriate properties (depending on the client, either request-response or one way) to get the correct functionality. Following code fragment contains all the parameters that the client can set inside the client code.

ctx.setToURL("http://test.organization.com:8080/wsrm/services/rmDemos");
ctx.setFromURL("http://our.organization.org:9070/axis/services/RMService");
ctx.setReplyToURL("http://our.organization.org:9070/axis/services/RMService");
cyx.setAcksToURL("http://our.organization.org:9070/axis/services/RMService");
ctx.setFaultToURL("http://our.organization.org:9070/axis/services/RMService");
ctx.setSendOffer(true);

These properties are very useful when debugging applications. By changing them appropriately the user can route messages through TCP Monitor or some monitor. These properties are self explanatory and hence let's look at one more sample with properties to route all the messages through TCP Monitor. Let's use Request/Response type service Invocation.

Invoking More Than One Service in a Single Client

Sandesha allows users to write clients that invokes several web services. Client can add any number of sequences to the SandeshaContext and RMSouruce will handle the underline sequence creation, message delivery and sequence termination appropriately. In the following example we will inovke "echoString" operation  first and invoke "ping" operation passing the response of the "echoString" operation.

Note: Please note that in this example both "echoString" and "ping" operations are in the same service "RMInteropService" as two different operations. However these need NOT be operations of a single service.

public void testEchoPing() throws Exception {
    System.out.println("          Echo and Ping Combined Test Started");
    UUIDGen uuidGen = UUIDGenFactory.getUUIDGen(); //Can use this for continuous testing.
    String str = uuidGen.nextUUID();

    Service service = new Service();
    Call echoCall = (Call) service.createCall();

    SandeshaContext ctx = new SandeshaContext();
    //------------------------ECHO--------------------------------------------

    ctx.setAcksToURL("http://127.0.0.1:" + defaultClientPort + "/axis/services/RMService");
    ctx.setReplyToURL("http://127.0.0.1:" + defaultClientPort + "/axis/services/RMService");
    ctx.setSendOffer(true);
    ctx.initCall(echoCall, targetURL, "urn:wsrm:echoString", Constants.ClientProperties.IN_OUT);

    echoCall.setOperationName(new QName("http://tempuri.org/", "echoString"));
    echoCall.addParameter("arg1", XMLType.XSD_STRING, ParameterMode.IN);
    echoCall.addParameter("arg2", XMLType.XSD_STRING, ParameterMode.IN);
    echoCall.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);
    //----------------------ECHO------------------------------------------------

    //------------------------PING--------------------------------------------
    Service pingService = new Service();
    Call pingCall = (Call) pingService.createCall();
    SandeshaContext pingCtx = new SandeshaContext();
    pingCtx.setAcksToURL("http://127.0.0.1:" + defaultClientPort + "/axis/services/RMService");
    pingCtx.setReplyToURL("http://127.0.0.1:" + defaultClientPort + "/axis/services/RMService");

    pingCtx.initCall(pingCall, targetURL, "urn:wsrm:Ping", Constants.ClientProperties.IN_ONLY);

    pingCall.setOperationName(new QName("http://tempuri.org/", "ping"));
    pingCall.addParameter("arg2", XMLType.XSD_STRING, ParameterMode.IN);
    //----------------------PING------------------------------------------------

    String ret = (String) echoCall.invoke(new Object[]{" Echo 1 ", str});
    System.out.println("          The Response for First Messsage is  :" + ret);
    pingCall.invoke(new Object[]{ret});

    ctx.setLastMessage(echoCall);
    ret = (String) echoCall.invoke(new Object[]{" Echo 2 ", str});
    System.out.println("          The Response for Second Messsage is  :" + ret);
    pingCall.invoke(new Object[]{ret});

    pingCtx.setLastMessage(pingCall);
    pingCall.invoke(new Object[]{ret});

    RMReport echoReport = ctx.endSequence();
    RMReport pingReport = pingCtx.endSequence();

    assertEquals(echoReport.isAllAcked(), true);
    assertEquals(echoReport.getNumberOfReturnMessages(), 2);

    assertEquals(pingReport.isAllAcked(), true);
    assertEquals(pingReport.getNumberOfReturnMessages(), 0);
    testCount--;
    System.out.println("          Echo and Ping Combined Test Finished");

}

Note: Samples of all these scenarios are available in the "interop" directory. Sample of invoking two web services in a same client is available as a test case in the "test" directory.

To Use Sandesha in the Server Side

Sandesha configuration in the server side can be explained using two simple steps. (Assume that axis is configured in Tomcat Server)

Note that we have to change the provider to RMProvider and need to add two request handlers. A sample of this deploy.wsdd can be found in the sample directory named RMSampleServiceDeploy with the required bat file to deploy the service. (make sure that the classes for the MyService is copied to CATALINA_HOME/webapps/axis/WEB-INF/classes. RMProvider by default will use the RPCProvider to invoke the services, however user can specify the actual provider that is required to invoke a particular service in sandesha.properties file that can be found in Sandesha-1.0.jar.

<deployment xmlns="http://xml.apache.org/axis/wsdd/" 
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
 <service name="MyService" provider="Handler">
<requestFlow>
<handler type="java:org.apache.sandesha.ws.rm.handlers.RMServerRequestHandler"></handler>
<handler type="java:org.apache.axis.message.addressing.handler.AddressingHandler"></handler>
</requestFlow>
<parameter name="handlerClass" value="org.apache.sandesha.ws.rm.providers.RMProvider"/>
<parameter name="className" value="test.MyService"/>
<parameter name="allowedMethods" value="*"/>
<parameter name="scope" value="request"/>
</service>
</deployment>

sandesha.properties

Following section shows the typical configuration for sandesha.properties. Please see the  in each property for explanation  on that property

# This is the port in which client side listener listens.
CLIENT_LISTENER_PORT = 9090

# This is the SimpleAxisServerImpl running port. Only for testing purposes.
SIMPLE_AXIS_SERVER_PORT = 8080

# Any number of handlers can be included as shown below for the response path of the Sender. These two are mandatory.
responseHandler1 = org.apache.axis.message.addressing.handler.AddressingHandler
responseHandler2 = org.apache.sandesha.ws.rm.handlers.RMServerRequestHandler

# If there are additional handlers that needs to be included in the Senders request path
# then use the following configuration.
# requestHandler1 = package.name.DummyHandler

# These are the handlers for the Listener's request path. Listener's request path is used to
# retrieve asynchronous responses and other RM protocol messages and hence the handlers we put here
# should be the RESPONSE handlers with respect to normal invocation. These two are mandatory.
listenerRequestHandler1 = org.apache.axis.message.addressing.handler.AddressingHandler
listenerRequestHandler2 = org.apache.sandesha.ws.rm.handlers.RMServerRequestHandler

# Define the strategy for executing web service invokes.D
# This impl uses the apache axis thread pool and configures
# it to the given size.
invokeStrategy=org.apache.sandesha.server.ThreadPoolInvokeStrategy:threadPoolSize=10

# Define the invoke handler that will execute the web service invokes.
# This impl simply delegates to the handler specified by the "invoker" param.
invokeHandler=org.apache.sandesha.server.DelegateInvokeHandler:invoker=org.apache.axis.providers.java.RPCProvider

Using RMSource Inside a Server

A common usage of this scenario would be the use of Sandesha from a web service that is already deployed in a server. In this scenario the web service become the client that utilizes the  RMSource for reliable delivery of messages. According to the general configuration Sandesha will use a separate listener in the client side to "listen for" any acknowledgements or responses sent asynchronously to the RMSource. However, the server itself can act as the "Listener" for Sandesha. The only aditional requirement is that to deploy "RMService" in the same server with "RMClientProvider as the provider. This behavior can easily obtained using a deployment descriptor as shown below.

<deployment xmlns="http://xml.apache.org/axis/wsdd/" 
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="MyService" provider="java:RPC">
<parameter name="className" value="samples.userguide.example3.MyService"/>
<parameter name="allowedMethods" value="*"/>
</service>

<service name="RMService" provider="Handler">
<requestFlow>
<handler type="java:org.apache.sandesha.ws.rm.handlers.RMServerRequestHandler"></handler>
<handler type="java:org.apache.axis.message.addressing.handler.AddressingHandler"></handler>
</requestFlow>

<parameter name="handlerClass" value="org.apache.sandesha.ws.rm.providers.RMClientProvider"/>
<parameter name="className" value="org.apache.sandesha.client.RMService"/>
<parameter name="allowedMethods" value="*"/>
<parameter name="scope" value="request"/>
</service>
</deployment>
 

In this deployment descriptor "MyService" is the web service that behaves as a client for some other service. (MyService is invoking some other web service reliably using Apache Sandesha). To support asynchronous messaging for "MyService" using the same server as the Client Side Listener, the user has to deploy "RMService" with the above configurations in the same server. In addition to above, "MyService" should specify the target URL of the "RMService" when it specify the asynchronous endpoint for <wsa:ReplyTo>, <wsrm:AcksTo> , <wsa:FaultTo> etc.. For the above sample deployment above URL would be, "http://host.address:port/axis/services/RMService".

In addition to the above configuration there can be more user specific configurations for Sandesha which needs some customizations in the deployment level. E.g. Sandesha can be used in the server side, with both RMSource and RMDestination to be in the same server.(say, both the service and client are web services that are deployed in the same server) For this kind of scenario again the user needs "RMService" to be deployed with the above configuration and also the web service that acts as the service (not the client service) to be deployed with "RMProvider" as the provider. (This is similar to deploying a service to use Sandesha)

Running Samples

To test the samples automatically, execute the following ant targets provided in the "build.xml" file in the samples directory. If you have downloaded the source distribution and need to run the samples, please build the source first using the maven.

Ant command  Description
ping_sync The scenario tests the simple one way Ping operation with acknowledgements coming in same HTTP connection
ping_async The scenario tests the simple one way Ping operation with acknowledgements coming in a different  HTTP connection
echo_sync_ack This scenario tests the simple two way Echo String operation. Here the request and the response  come in  different HTTP Connections. Acknowledgements are coming in the response path of the request's HTTP connection
echo_async_ack This scenario tests the simple two way Echo String operation. Here the request Acknowledgement and the response  come in  different HTTP Connections.

In all these scenarios the service RMSampleService is deployed in a SimpleAxisServer using the ant script and the results will be shown in TCP monitors.

In addition Sandesha-samples.jar that contains all the samples can be found in the samples directory.

If you need a project to be created for Eclipse or Intellij IDEA use the maven commands

maven eclipse

maven idea