2008/09/04
4 Sep, 2008

Exposing and Invoking mashups using SOAP/POX over JMS with the WSO2 Mashup Server

  • Kieth Chapman
  • - WSO2

Introduction

The WSO2 Mashup Server is a platform for hosting mashups written in JavaScript with the E4X extension. These mashups can consume data from various sources such as feeds, HTML pages, external Web services, databases (via Data Services), Excel files (via Data Services) etc. These newly created mashups are then exposed as Web services. In addition, mashup authors may expose mashed up data as feeds or alerts in the form of instant messages or email.

In addition to http and https transports, the WSO2 Mashup Server supports several other transports. This allows mashups running on the server to be exposed over these additional transports as well as invoke external Web services over such transports. In this tutorial, we will look at how the JMS transport (or SOAP/POX via JMS to be precise, as JMS is not really a transport but an API) can be used with the Mashup Server.

Java Messaging Service (JMS)

Java Messaging Service (JMS) is a Java Message Oriented Middleware (MOM) API for sending messages between clients. JMS is defined by a specification developed under the Java Community Process as JSR 914. JMS, which is asynchronous by nature, allows components to communicate with each other in an indirect manner. Senders send messages to a topic or a queue, while components who have subscribed to that topic or queue read messages off topic/queue. Due to this specific mechanism of communication, senders need not know any details about who consumes a message, and hence, is a great way to build loosely coupled systems.

Benefits of Using JMS Transport

  • Enables point to point as well as publisher/subscribe style of messaging (topics provide publisher/subscribe style of messaging and queues provide point-to-point style  of messaging).
  • Guarantees message delivery without the need for  WSRM (Web Services Reliable Messaging).
  • Allows both asynchronous as well as synchronous message passing (although JMS is asynchronous by nature the underlying implementation in the Mashup Server enables synchronous messaging as well).
  • Ability to secure messages using WS-Security.
  • Ability to send MTOM (SOAP Message Transmission Optimization Mechanism) messages.
  • Fits existing enterprise scenarios - many enterprises already use JMS for messaging.

Disadvantages of Using JMS Transport

  • No standard protocol among JMS providers hence the two systems must share the same JMS provider.
  • May not be interoperable among Web service stacks - there is a specification and a W3C Working Group working on a standard.

Setting up a JMS provider

In order to use the JMS transport with the Mashup Server, you first need to set up a JMS Provider. There are several JMS providers available, and in this tutorial, I use Apache ActiveMQ as the JMS provider. The configurations provided in this tutorial assumes that you have a local installation of ActiveMQ (4.1.0 or higher) up and running. Apache ActiveMQ can be downloaded here. In order to run ActiveMQ simply run the activemq command from $ACTIVEMQ_HOME/bin.

Note: You need to have an active network connection in order to start ActiveMQ successfully.

Configuring the Mashup Server to Receive/Respond Messages via JMS

To enable the JMS transport, you need to uncomment the JMS transport listener, and the JMS transport sender configuration which can be found in the axis2.xml (which can be found in $MASHUP_HOME/conf). If you are using a JMS provider other than ActiveMQ, the JMS transport listener configuration should be updated to reflect your environment. Once uncommented, the default configuration for the JMS transport listener should be as follows:

    <transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">

        <parameter name="myTopicConnectionFactory">

            <parameter name="java.naming.factory.initial">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>

            <parameter name="java.naming.provider.url">tcp://localhost:61616</parameter>

            <parameter name="transport.jms.ConnectionFactoryJNDIName">TopicConnectionFactory</parameter>

        </parameter>



        <parameter name="myQueueConnectionFactory">

            <parameter name="java.naming.factory.initial">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>

            <parameter name="java.naming.provider.url">tcp://localhost:61616</parameter>

            <parameter name="transport.jms.ConnectionFactoryJNDIName">QueueConnectionFactory</parameter>

        </parameter>



        <parameter name="default">

            <parameter name="java.naming.factory.initial">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>

            <parameter name="java.naming.provider.url">tcp://localhost:61616</parameter>

            <parameter name="transport.jms.ConnectionFactoryJNDIName">QueueConnectionFactory</parameter>

        </parameter>

    </transportReceiver>



The configuration for the JMS transport sender should be:

    <transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender"/>

The JMS transport receiver configuration allows you to define the default connection factory (named as "default") for use by mashups using the JMS transport (for now, having the default configuration will suffice - the reason why we have other connection factories is for future use; please refer the section on future directions for more details.) In addition to above parameters, you can also specify the parameters "java.naming.security.principal" and "java.naming.security.credentials", if required, in order to access the actual connection factory.

Depending on your JMS implementation, you will need to make available all required libraries, so that the Mashup Server can communicate with the JMS Provider. This example assumes you are using an ActiveMQ instance, hence it is required to make the following JAR files available: activeio-core-3.0.0-incubator.jar, activemq-core-4.1.1.jar, geronimo-j2ee-management_1.0_spec-1.0.jar at a minimum (The versions of these jars may vary depending on the ActiveMQ version you use. These required jars can be found in the lib directory of ActiveMQ). Simply drop the above jar files into $MASHUP_HOME/lib.

Now you are all set to expose your mashups over the JMS transport. Restart the Mashup Server, once above configurations are complete.

Each Service running on the Mashup Server will be listening on a JMS queue named {authorName}-{serviceName}. For example, the version service in the Mashup Server whose author is system, can be invoked by sending a message to the "system-version" queue.

Note: If you want to turn off the JMS transport for a service, you can do so through the Web console of the Mashup Server. By default, each mashup is exposed under all transports available in the system. In the next release of the Mashup Server, service authors will be able to configure transports at deployment time.

Let's configure the JMS transport in the Mashup Server and deploy a sample service so that we can invoke it over JMS. For this purpose, let's use a simple service with a echo operation as shown below. Let's call this service "echoService", and assume the author of the service is "keith".

// Instruct the Mashup Server that the echo function expects parameter whos value would be a String

echo.inputTypes="string";



// Instruct the Mashup Server that the echo function return a value which would be a String

echo.outputType="string";

function echo(param){

    // Simply log the message

    system.log("Invoked echo function.");

    return param;

}

This service has a single operation called echo, which simply accepts a string as an argument and returns that string after logging a simple message (saying that it was invoked).

Once a service is exposed over JMS it will be evident in the WSDL as well. This can be noted by taking a look at the service section in the WSDL. The service section of the WSDL 2.0 document for the above service is shown below:

<wsdl2:service name="echoService" interface="tns:serviceInterface">

    <wsdl2:endpoint name="SOAP11Endpoint" binding="tns:keith-service-SOAP11Binding"

        address="https://192.168.1.2:7762/services/keith/echoService.SOAP11Endpoint/"/>

    <wsdl2:endpoint name="SecureSOAP11Endpoint" binding="tns:keith-service-SOAP11Binding"

        address="https://192.168.1.2:7443/services/keith/echoService.SecureSOAP11Endpoint/"/>

    <wsdl2:endpoint name="SecureHTTPEndpoint" binding="tns:keith-service-HttpBinding"

        address="https://192.168.1.2:7443/services/keith/echoService.SecureHTTPEndpoint/"/>

    <wsdl2:endpoint name="HTTPEndpoint" binding="tns:keith-service-HttpBinding"

        address="https://192.168.1.2:7762/services/keith/echoService.HTTPEndpoint/"/>

    <wsdl2:endpoint name="JMSSOAP12Endpoint" binding="tns:keith-service-SOAP12Binding"

        address="jms:/keith-echoService?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&

        java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&

        java.naming.provider.url=tcp://localhost:61616"/>

    <wsdl2:endpoint name="JMSSOAP11Endpoint" binding="tns:keith-service-SOAP11Binding"

        address="jms:/keith-echoService?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&

        java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&

        java.naming.provider.url=tcp://localhost:61616"/>

    <wsdl2:endpoint name="SOAP12Endpoint" binding="tns:keith-service-SOAP12Binding"

        address="https://192.168.1.2:7762/services/keith/echoService.SOAP12Endpoint/"/>

    <wsdl2:endpoint name="SecureSOAP12Endpoint" binding="tns:keith-service-SOAP12Binding"

        address="https://192.168.1.2:7443/services/keith/echoService.SecureSOAP12Endpoint/"/>

</wsdl2:service>

The WSDL now shows two additional endpoints. JMSSOAP12Endpoint and JMSSOAP11Endpoint. Please take note of the address of a JMS endpoint. It takes the form of jms:/{authorName}-{serviceName}?transport.jms.ConnectionFactoryJNDIName={Value of transport.jms.ConnectionFactoryJNDIName}&java.naming.factory.initial={Value of java.naming.factory.initial}&java.naming.provider.url={Value of java.naming.provider.url}

Hence for the service shown above it would be jms:/keith-echoService?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&java.naming.provider.url=tcp://localhost:61616

Invoking services over the JMS transport

In addition to exposing mashups over the JMS transport the Mashup Server also enables you to invoke services over JMS. In order to do this you should configure the JMS transport listener configuration which can be found in the axis2-client.xml (the axis2-client.xml can be found in $MASHUP_HOME/conf). You will also have to uncomment the JMS transport sender configuration in axis2-client.xml. This configuration could be the same that you set up in the axis2.xml above.

Once the above is configured is set, you are all set to invoke services (and receive responses) over JMS. Even though JMS is an asynchronous transport by nature the Mashup Server enables you to do request-responce using JMS. This is handled under the covers by the Mashup Server using correlation ID's. If a user needs to perform a one way invocation (just send a request) he can do so by setting the in-only mep as an option (e.g options.mep="in-only";). We will have a look at such an example as well.

There are 3 main ways of writing webservice clients using the Mashup Server and those mechanisms can also be used to invoke services over JMS. They are:

  • Using WSRequest - the user is expected to provide complete details as to how the request should be framed.
  • Using the dynamic version of WSRequest - the Mashup Server will take care of framing the request for you, but no data-binding will be available. (Possible to invoke services that enforce WS-Security.)
  • Using a generated JavaScript stub - perhaps the easiest way to invoke a service.

Let's have a look at examples of these three ways.

Using WSRequest

When the WSRequest host object is used to invoke a service, user is expected to provide all details needed to frame the request. The following function demonstrates the use of  this. It's interesting to note, that the only difference in calling a JMS service is the endpoint EPR (Endpoint Reference or Endpoint Address):

// Use the WSRequest host onject to invoke the echoService

function invokeEchoViaJMS(){

    var request = new WSRequest();

    var options = new Array();



    // Specify the soap action so that the request can be dispatched to the correct operation

    options.action="https://services.mashup.wso2.org/echoService/ServiceInterface/echoRequest";



    // The payload that the service will be invoked with

    var payload = <echo><param>JMS</param></echo>;

    var result;

    try {

        request.open(options,"jms:/keith-echoService?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&java.naming.provider.url=tcp://localhost:61616", false);

        request.send(payload);

        result = request.responseXML;

    } catch (e) {

        system.log("Received an error" + e.toString());

        result = e.toString();

    }

    return result;

}

Using the Dynamic Version of WSRequest

You may also use the dynamic version of the WSRequest host object to invoke services over JMS. The following function demonstrates the use of this.

function invokeEchoViaJMSUsingWSDL(){

    var request = new WSRequest();



    // The payload that the service will be invoked with

    var payload = <echo><param>JMS</param></echo>;



    // Need to specify the service that we need to invoke. The WSDL may have multiple services in it

    var service = new QName("https://services.mashup.wso2.org/echoService","echoService");

    var result;

    try {



        // We instruct that we want to invoke this operation using details in the JMSSOAP12Endpoint

        request.openWSDL("https://localhost:7762/services/keith/echoService?wsdl",false, new Array(), service, "JMSSOAP12Endpoint");

        request.send("echo",payload);

        result = request.responseXML;

    } catch (e) {

        system.log("Received an error" + e.toString());

        result = e.toString();

    }

    return result;

Using a Generated JavaScript Stub

This is the easiest way to invoke a service. User needs to get a stub for the service he expects to invoke. If the service been invoked is a service running on the WSO2 Mashup Server, the stub for that service may be used. If this is an external service the stub generator in the Mashup Server may be used to generate a stub. As the stub provides dataBinding support, user is relieved of the duty of creating the payload himself. Rather, providing the values of the parameters would suffice.

In the following example, it assumes that the generated stub is saved in the resources folder of the calling mashup as stub.js.

// Include the stub of the echoService mashup that we hope to invoke

system.include("stub.js");



function invokeEchoViaStub(){

    var result;

    try {

        // Specify that we need to use one of the JMS endpoints

        echoService.endpoint = "JMSSOAP12Endpoint";

      

        // Call the echo operation with the parameter as JMS

        result = echoService.echo("JMS");

    } catch (e) {

        system.log("Received an error" + e.toString());

        result = e.toString();

    }

    return result;

}

One-Way Messaging Over the JMS transport

All above examples used a request-response style (in-out mep) invocation. Let's now have a look at an example that just sends a request (we don't care whether there is a response or not.)

function invokeEchoViaJMSOneWay(){

    var request = new WSRequest();

    var options = new Array();



    // Specify the soap action so that the request can be dispatched to the correct operation

    options.action="https://services.mashup.wso2.org/echoService/ServiceInterface/echoRequest";



    // Specify that I dont care about the responce

    options.mep="in-only";



    // The payload that the service will be invoked with

    var payload = <echo><param>JMS</param></echo>;

    request.open(options,"jms:/keith-echoService?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&java.naming.provider.url=tcp://localhost:61616", false);



    // As we are using a in-only mep there wont be a responce

    request.send(payload);

    return "Message sent";

}

Invoking a Service which is secured

The payload sent when using the JMS transport can be secured using WS-Security. For this example, let's assume that we secure the echoService using scenario 7 (Encrypt only - Username Token Authentication).  When configurating security for this service, I grant access to user "keith". A client for this example would look like the following:

function invokeEchoViaJMSWithSecurityUsingWSDL(){

    var request = new WSRequest();



    // The payload that the service will be invoked with

    var payload = <echo><param>JMS</param></echo>;



    // Need to specify the service that we need to invoke. The WSDL may have multiple services in it

    var service = new QName("https://services.mashup.wso2.org/echoService","echoService");

    var options = new Array()



    // Set the details needed by the security policy.

    options["username"] = "keith";

    options["password"] = "keith";

    options["encryptionUser"] = "keith";

    var result;

    try {



        // We instruct that we want to invoke this operation using details in the JMSSOAP12Endpoint

        request.openWSDL("https://localhost:7762/services/keith/echoService?wsdl",false, options, service, "JMSSOAP12Endpoint");

        request.send("echo",payload);

        result = request.responseXML;

    } catch (e) {

        system.log("Received an error" + e.toString());

        result = e.toString();

    }

    return result;

}

Using jconsole to Monitor Messege Delivery

jconsole is a tool shipped with the JAVA SDK. If you have added JAVA_HOME/bin to the classpath you could start jconsole by simply typing jconsole at the command prompt. JConsole could be used to monitor wether messages are delivered to a certain queue and wether messages were read off that queue. You could also monitor queues that are avaiable on the JMS provider via its support for JMA. Apache ActiveMQ has extensive support for JMX to allow you monitor and control the behavior of the broker via its JMX beans.

For example starting the Mashup Server with the echoService and the echoJMSClient would produce the following view (a queue for each service.)

jconsole

 When a message is sent to a particullar queue its EnqueueCount will be increased while reading a message from a queue would cause its DequeueCount to increase. Thus using jconsole you could monitor that the messages and delivered and read successfully.

Future directions

Currently the Mashup Server does not allow a mashup to listen on a specific queue of a specific topic, rather it listens on a queue named arthorName-ServiceName. In the next release we plan to expose the ability of setting custom queue or topics that the service should listen to.

Ability to control exposed transports at deployment time. This would allow mashup authors to expose their mashups over a single transport if they wish.

Ability to add custom JMS headers to messages sent using the WSO2 Mashup Server.

Conclusion

Exposing and invoking mashups using SOAP over JMS with the WSO2 Mashup Server is pretty simple. Exposing mashups over JMS is just a matter of adding the JMS configurations to the axis2.xml. The same goes for invoking services over JMS, the only difference when calling a service over JMS is the EPR (Endpoint Reference.)

The WSO2 Mashup Server also allows users to call secured services over the JMS transport.

Resources

Author

Keith Chapman, Senior Software Engineer, WSO2 Inc. [email protected]

 

About Author

  • Kieth Chapman
  • WSO2 Inc.