The Server Labs Blog Rotating Header Image

JMS

SCA Async/Conversational services Part 1: Internals of Tuscany SCA

Sometime ago I wrote about developing applications with SCA on a JBI-based infrastructure, using simple SCA services for that.

I’m coming back again with two separate SCA blog posts discussing the usage of more complex services, asynchronous and conversational services:

  • In this post, I provide an example client-server project that implements a conversational and asynchronous service in Tuscany SCA, digging into the internals of the implementation for handling these services using Web Services and JMS bindings. I found this information quite difficult to find.
  • The second post looks into the situation where the usage of a Tuscany SCA runtime is not possible in the client side, but we still need a way to make use of these type of services from a more standard web service client. For the specific case of a web service binding, and knowing the internals of Tuscany I will use a standard jax-ws client to consume a Tuscany SCA conversational and asynchronous service.

Both posts use a common example project, a Counter service that notifies to the client each time the count is updated (every second) via an asynchronous callback. The service is conversational and allows clients to stop the count and restart it through separate independent service calls. Also, multiple clients can be run in parallel, each one with its own counter service instance. Version 1.6 of Tuscany SCA is used in this sample.

The source code of the sample counter service for this post can be found here.

SCA asynchronous and conversational service definition

Let’s refresh how to define a service in Tuscany SCA that is both conversational and defines a callback interface for asynchronous calls to the client. Good references for this are the SCA specifications, the Tuscany SCA documentation and the sample projects included in the distribution. However, while I could find several callback projects on the Tuscany SCA samples there are none that exercise conversations.

Below, you find the interface definition of the SCA service in Java for the CounterService (counter-callback-ws-service maven project).

I use the @Callback and @Conversational annotations to define our service. For the @Callback annotation we need also to define the callback interface class. Therefore, for our counter service we have two interfaces, one for the service which implementation is done in the server side, and another one for the callback which implementation is done in the client side.

/**
 * The remote service that will be invoked by the client
 */
@Remotable
@Callback(CounterServiceCallback.class)
@Conversational
public interface CounterService {

    @OneWay
    void startCounting(int maxcount);
    
    @OneWay
    void stopCounting();

    @OneWay
    void restartCounting();    
    
    @OneWay
    void shutdownCounting();     
}

The Callback interface is as simple as:

/**
 * The callback interface for {@link CounterService}.
 */
@Remotable
public interface CounterServiceCallback {

    void receiveCount(int count, int end);
}

The server side implementation of the service looks like:

/**
 * This class implements CounterService and uses a callback.
 */
@Service(CounterService.class)
@Scope("CONVERSATION")
public class CounterServiceImpl implements CounterService {
	@ConversationID
	protected String conversationID;

	/**
	 * The setter used by the runtime to set the callback reference
	 * 
	 * @param counterServiceCallback
	 */
	@Callback
	public void setMyServiceCallback(CounterServiceCallback counterServiceCallback) {
		System.out.println("setCounterServiceCallback on thread "
				+ Thread.currentThread());
		this.counterServiceCallback = counterServiceCallback;
	}

On the implementation, the @Service SCA annotation is used to identify the SCA service being implemented and the @Scope which specifies a visibility and lifecycle contract an implementation has with the SCA runtime. Possible values of @Scope are STATELESS, REQUEST, CONVERSATIONAL and COMPOSITE. For this specific service we need to use CONVERSATIONAL, instructing Tuscany that conversation state must be kept in order to correlate interactions between a client and the service. For a description of the rest of scopes, refer to the SCA Java Annotations and API reference specification.

Last, we use the @Callback annotation to instruct Tuscany SCA where to inject the Callback reference to be used by the service.

The server side SCA composite is very similar than the Tuscany SCA official callback-ws-client and you can find it under the “src/main/resources/counterws.composite“. The binding used for the sample is a Web Service binding.

Tuscany SCA Counter Service Client

On the client side (counter-callback-ws-client-SCA maven project) we need to define a client interface that makes use of the counter service. To make it simple, the interface has a method that returns the reference to the Counter Service and an additional helper method to get the current count recorded in the client based on the service callbacks. As in the server side, we have to make sure to define the interface as @Conversational and the implementation with @Scope(“CONVERSATION”) so Tuscany populates the service call with conversation information.

Also, to simplify the sample code, the shared interfaces between client and server (i.e. CounterService and CounterServiceCallback) are included in both projects separately. Ideally, these interfaces would be on a separate interfaces library used by both projects.

/**
 * The client component interface.
 */
@Conversational
public interface CounterServiceClient {
    
    public CounterService getCounterService();
    
    public int getCount();
    
}

The client composite is as listed below. It defines the reference to the counter Service and the associated callback. The url defined in the callback is used by Tuscany SCA in the client side to start the listening callback web service:



    
        
        
	        
	        
	        
	            
            
        
    

For the testing, a unit test is setup (WebServiceSCAClientTest.java), exercising the counter service, callbacks and conversations.

Running the counter service sample project

To run the counter service sample, extract the source code and:

  • Run the server side, executing the run-server.sh script in the counter-callback-ws-service directory. The server component should start and wait for client requests.
    ...
    Nov 8, 2010 4:11:23 PM org.apache.coyote.http11.Http11Protocol start
    INFO: Starting Coyote HTTP/1.1 on http-8086
    Callback server started (press enter to shutdown)
    Nov 8, 2010 4:11:23 PM org.apache.tuscany.sca.http.tomcat.TomcatServer addServletMapping
    INFO: Added Servlet mapping: http://localhost:8086/CounterServiceComponent
    
  • Go to the client project, counter-callback-ws-client-SCA and execute the tests with:
    mvn clean test
    
  • On the client side you should see how the client starts the counting service, receives the callbacks and how the counting is stopped and later restarted, continuing the count where it was left.
    ...
    Nov 8, 2010 4:11:28 PM org.apache.tuscany.sca.http.tomcat.TomcatServer addServletMapping
    INFO: Added Servlet mapping: http://localhost:1999/callback
    Starting Count and waiting 5 seconds for counts...
    CounterServiceCallback --- Received Count: 0 out of 30
    CounterServiceCallback --- Received Count: 1 out of 30
    CounterServiceCallback --- Received Count: 2 out of 30
    CounterServiceCallback --- Received Count: 3 out of 30
    CounterServiceCallback --- Received Count: 4 out of 30
    Stopping Count and waiting 5 seconds for no counts...
    Restarting Count and waiting 5 seconds for counts...
    CounterServiceCallback --- Received Count: 5 out of 30
    CounterServiceCallback --- Received Count: 6 out of 30
    CounterServiceCallback --- Received Count: 7 out of 30
    CounterServiceCallback --- Received Count: 8 out of 30
    Stopping the Client Node
    Nov 8, 2010 4:11:44 PM org.apache.tuscany.sca.node.impl.NodeImpl stop
    ...
    
  • On the server side, you should see the client call and the associated conversationID.
    ...
    INFO: Added Servlet mapping: http://jmatute-laptop:8086/CounterServiceComponent
    setMyServiceCallback on thread Thread[Thread-2,5,main]
    ConversationID = d8a05f47-064b-4832-9cca-7ad035bd36ee. Starting count
    Sleeping ...
    Sleeping ...
    Sleeping ...
    Sleeping ...
    Sleeping ...
    ConversationID = d8a05f47-064b-4832-9cca-7ad035bd36ee. Stopping count
    Sleeping ...
    Sleeping ...
    Sleeping ...
    Sleeping ...
    Sleeping ...
    ConversationID = d8a05f47-064b-4832-9cca-7ad035bd36ee. Restarting count
    Sleeping ...
    Sleeping ...
    Sleeping ...
    Sleeping ...
    Thread interrupted.
    

Having reached this point, let’s look into how Tuscany SCA is handling the conversations and callbacks and how the required information is included in the underling transport bindings (WS or JMS).

Internals of conversational and asynchronous services in Tuscany SCA

Tuscany SCA conversational services (i.e. marked with @Conversational) make use of conversation IDs in order to keep track and correctly map multiple invocations associated with a certain conversation.

SCA and Tuscany asynchronous services are similar to those defined, for instance, in BPEL where the forward and callback service calls are two complete separate service invocation calls. Therefore, Tuscany also requires additional information to know where the callback needs to be send and optionally provide a application correlation id for the callback call.

Summarising, the below table contains the information used by Tuscany SCA for conversation and callback services:

Tuscany information Description
conversationID UUID identifying the current conversation. This is used by Tuscany SCA to associate accordingly the service instance associated to that conversation.
callbackLocation The location of the callback service. This, depending on the binding used might be a URI for Web Service binding or a Queue name for JMS.
callbackID Application-specified callback ID, that is used by the application to correlate callbacks with the related application state.

The above information needs to be mapped to the specific binding and here is where no standarisation exists, making difficult to have interoperable solutions:

  • WebService Binding : for the Web Service binding, WS-Addressing headers are used in order to store the Tuscany SCA conversational/async required information. Below it is shown an example of a Tuscany Web Service conversational and asynchronous/bidrectional invocation:
    • The WS-Addressing Address contains the Callback URI to be used for the callback invocation. This is normally setup by the client to notify the server where to send the callbacks.
    • The WS-Addressing ReferenceParameters contains the other two information fields under specific Tuscany Namespace, the CallbackID and the ConversationID.
    
    
        
            
                
    http://localhost:1999/callback
    45a963da-2074-4bb2-b9ee-f721e2ec753b 309b8322-1dc2-4c51-a4db-73d65edae391
    30
  • JMS Binding : For JMS, Tuscany SCA uses JMS Message properties to store the required information. A screenshot (see below) of ActiveMQ console shows an Tuscany SCA JMS Message for conversational and async/bidrectional service.
    1. CallbackID, scaConversationID and scaCallbackQueue, Tuscany SCA propietary JMS message properties to hold the information.

    Tuscany SCA JMS Message

    Tuscany SCA JMS Message

Conclusion

In this first post I have presented a Tuscany SCA example that covers both conversational and asynchronous scenarios, not currently available in the official Tuscany SCA samples, and looked into the internals of Tuscany SCA used to handle these services. This provides the basis for the next post, where I will be using this information to extend the project code with a non-SCA JAXWS-based web service client as an approach to consume these services without a Tuscany SCA runtime.

HTML5 Websocket vs Flex Messaging

Nearly 18 months ago, I wrote an article on this blog which showed how to develop a simple flex web application using ActiveMQ as a JMS server. It has proved a popular article so there must be a fair amount of interest in this topic.

With all the hype surrounding HTML5 at the moment, I figured I’d try to update the application for the HTML5 world, at the same time seeing how easy it would be to replace Adobe Flex. I’m sure you’re all familiar with the fact that Steve Jobs won’t let Flash (and therefore Flex) onto either the iPhone or iPad, so it’s worth investigating if the alternatives (in this case HTML5 websocket) are really up to scratch.

The example shown in the article uses HTML 5 websocket, Apache ActiveMQ, the Stomp messaging protocol and (indirectly) Jetty 7. It currently only runs in Google Chrome 5 since this is the only browser with support for websocket available as of May 2010.

What is Websocket?

In short, it’s a way of opening up a firewall-friendly bi-directional communications channel with a server. The channel stays open for a long period of time, allowing the server and client web browser to interchange messages without having to do polling which consumes bandwidth and a lot of server resources.

This article describes websocket in much more depth.

Websocket forms part of the (still unapproved) HTML5 specification which all browsers will eventually have to implement.

Updating the Trader app

In order to get the best out of this section, it is probably best to have read the original Flex-JMS article.

Here is the code for the updated trader app. Instead of having a server webapp component with BlazeDS acting as a message proxy and the flex clients, we have a bunch of HTML/CSS/JS files (in the /html folder) and some code to put messages in the JMS queues published by Apache ActiveMQ (in the /server folder).

Setting up Apache ActiveMQ

Apache ActiveMQ now comes with support for Websocket (implemented behind the scenes with Jetty 7). We will use ActiveMQ both as our messaging server and web server in this example, though that is not perhaps the best production configuration.

This article explains how to configure ActiveMQ and Websocket. I will repeat the key instructions here for the sake of simplicity:

  1. Download the latest snapshot of Apache ActiveMQ 5.4 from here and unzip it somewhere on your filesystem that we will call $ACTIVEMQ_HOME
  2. Edit $ACTIVEMQ_HOME/conf/activemq.xml and change the transportConnectors section so that it looks like the example below:
            
                
                
                 
                 
            
    
  3. Start ActiveMQ by running bin/activemq from the $ACTIVEMQ_HOME directory. Go to http://localhost:8161/admin/ and log in with the username/password admin/admin to check that everything is working ok.

Configure an install the aplication

Download the trader app code and copy the /html folder to $ACTIVEMQ_HOME/webapps/demo, renaming it to /trader-app-websocket (i.e. the full path should be $ACTIVEMQ_HOME/webapps/demo/trader-app-websocket).

Edit stock-quote.js and note the following section:

    var url = 'ws://localhost:61614/stomp';
    var login = 'guest';
    var passcode = 'guest';
    destination = '/topic/stockQuoteTopic';

The url attribute referes to the URL of the ActiveMQ server. Due to a bug in Chrome running on Ubuntu 9.10, I had to put the IP address of my machine here but if you’re running on another linux flavour, OSX or windows, I would imagine that leaving it as localhost should be OK. The username and password are guest/guest which is standard for ActiveMQ.

Download the latest version of Google Chrome (I have 5.0.375.55 which was released a few days ago) and open the URL http://localhost:8161/demo/trader-app-websocket/. You should see a UI that is similar to the Flex app developed in the original article:

chrome-initial

Open up a terminal window and go to the location to which you extracted the /server part of the code download. Run the following (assumes Maven installed);

mvn clean compile exec:java -Dexec.mainClass="com.theserverlabs.flex.trader.JSONFeed"

You should see a whole bunch of stock quote information like that shown below scrolling in the terminal:

{"symbol": "XOM",
"name": "Exxon Mobile Corp",
"low": 61.317410451219146,
"high": 61.56,
"open": 61.56,
"last": 61.317410451219146,
"change": 61.317410451219146}

This Java program is the same as that used in the original post. It generates random stock price information for a variety of stocks and publishes it in the stockQuote topic in ActiveMQ. In this case, it generates JMS text messages which contain data formatted in the JSON format.

If you go back to the Chrome browser window, you should see the stock quotes update. If they don’t update, click on refresh:

chrome-stocks

This is pretty much exactly the same as how the original Flex application worked. The UI colours etc. are slightly different and I’ve not implemented the functionality for subscribing/unsubscribing from a stock price – but that was just on time grounds, not because it is difficult.

How does it work?

When the browser opens the page, it executes the following code in stock-quote.js, which subscribes to the stock quote service:

$(document).ready(function(){

    var client, destination;
    
    ... 
    
    var url = 'ws://localhost:61614/stomp';
    var login = 'guest';
    var passcode = 'guest';
    destination = '/topic/stockQuoteTopic';

    client = Stomp.client(url);
    client.connect(login, passcode, onconnect);
 
});

Here we use the library provided by Jeff Mesnil which enables us to access ActiveMQ using the Stomp protocol instead of JMS. We use it here because it is simple and cross-platform. There is no way of directly subscribing from JavaScript to a JMS server via JMS because there is no client available.

In the same block of code, you can see the code that we execute when we receive a message:


    var onconnect = function(frame) {
      
      client.subscribe(destination, function(message) {
        var quote = JSON.parse(message.body);
        $('.' + quote.symbol).replaceWith("" + 
            "" + quote.symbol + "" +  
            "" + quote.open.toFixed(2) + "" + 
            "" + quote.last.toFixed(2) + "" + 
            "" + quote.change.toFixed(2) + "" + 
            "" + quote.high.toFixed(2) + "" + 
            "" + quote.low.toFixed(2) + "" +  
            "");
      });
    };

When we receive a message, we parse it using the JSON parser that is part of the JQuery library and then we find the HTML element with class attribute equal to the symbol (e.g. if the symbol is IBM, we look for the HTML element with class=”IBM”) and replace it’s contents with the HTML table row code generated in the method. Simple really.

The rest of the code is just HTML and CSS and is not really that interesting for this article.

Conclusions

It is pretty easy to develop an application that uses Websocket – you only have to look at how little real code there is in this example. I’d say it is as easy as developing the original Flex app so from a development point of view, there’s little to chose between these technologies.

Unfortunately the only browser that currently supports Websocket is Google Chrome (and the implementation is a bit buggy). Other browsers (especially Firefox and Safari) should have this functionality soon though. One of the arguments used in support of Flash/Flex has always been the large installed base. Given that the iPhone and iPad are not part of this installed base, it is questionable as to whether this can still be used as a justification for using Flash/Flex. Sure there aren’t that many browsers that support Websocket but in 6 months time they probably all will and you won’t need any proprietary plugin to access the apps build using them. I’d definitely recommend that people developing internal corporate apps who can force their end users to use a browser with Websocket support take a look at this technology. People developing publically accessible webapps are probably going to have to wait till it is more widely implemented in browsers and provide graceful fallback in the case in which it isn’t.

I should point out that I have an iPhone and use Ubuntu 9.10 64-bit at work and I hate not being able to see content on my iPhone because it has been implemented in Flash and my entire firefox browser often crashes completely due to the 64-bit linux Flash plugin.

I’ve gone from being a fan of Flash/Flex to not being so sure about it. It’s hard to escape the feeling that HTML5 will offer much of the functionality currently offered by Flash/Flex in a short period of time. I think the future of these technologies is going to depend on Adobe innovating and offering stuff that is not possible in HTML5, otherwise there is little reason to use it.

Database storage in TIBCO EMS 5.0

Earlier this year TIBCO released a new version of their particular implementation of the JMS standard. TIBCO Enterprise Message Service 5.0 which, among others, includes some features which I personally consider very interesting.

One of them is the possibility to store persistent messages in a database. In previous versions, the only possibility was to use file based storage. In case you wanted to use fault tolerant groups of servers, this caused some difficulties as having both the messages and the shared state information in files required them to be in a shared storage device, either via hardware (SCSI and SAN, NAS or NAS with NFS) or software (Cluster Server or Clustered File System). These solutions are expensive and hard to maintain while using a database eases the configuration and leaves the hard work on the DBMS. You can even combine file and database based storage depending on the queue or topic.

Database storage is based in Hibernate, so any database server supported by Hibernate could be used to store the data. To use it, first we will need to enable it, setting the following parameters in the tibemsd.conf file:

  • dbstore_classpath: pointing to the hibernate and DB driver jar files.
  • dbstore_driver_name: specifies the jdbc driver.
  • dbstore_driver_dialect: specifies the hibernate driver dialect.
  • jre_library: associated JVM libraries.

Second, we will have to create the stores in the stores.conf file, for example:

[TSLstore]
type=dbstore
dbstore_driver_url=jdbc:mysql:thin:localhost:3306:TSLDB
dbstore_driver_username=anonymous
dbstore_driver_password=anonymous

Third, link the destinations with the appropriate store, either in the topics.conf and queues.conf files or using the command line administration tool with the setprop command, e.g.

setprop topic my.topic store=dbstore

Finally, we will need to export the EMS database tables using the EMS Schema export tool provided with EMS:

java -jar <EMS_home>/bin/tibemsd_util.jar -tibemsdconf <route_to_tibemds.conf>/tibemsd.conf -createall -export

As a postscript, let me recommend you not to install EMS as root, at least in a Solaris machine. With EMS 5.0 version it will not work unless you change the owner after the installation. This happens because once installed, if we try to run in as root, Solaris, due to security reasons, will execute the process with the user nobody4 but the folder which will contain the datastore files (if we choose file-based storage) will have write permissions set only for root user. As EMS will not be able to create the datastores, it will automatically shutdown. Moreover, even if we change the rights of that folder we will have the same problem with the configuration files, they will only have write permissions for root user which will return errors if we try to change the configuration using the command line administration tool.