Talking asynchronous apps

Tutorial: Atmosphere 1.0, WebSocket Portability on the JVM

In this JAX Magazine tutorial, JeanFrancois Arcand introduces us to the Atmosphere Framework, a Java/Javascript framework which allows the creation of portable asynchronous applications using Groovy, Scala and Java.


The Atmosphere Framework ships with a JavaScript component supporting all modern browsers and several server components supporting all major Java-based WebServers. The aim of the framework is to allow a developer to write an application and let the framework discover the best communication channel between the client and the server, transparently.

For example, a developer can write an application that will use the WebSocket protocol when used with a browser or server that supports the protocol, and transparently fall back to HTTP in case the WebSocket protocol isn’t supported. For example, an Atmosphere application will work fine with Internet Explorer 6, 7, 8, and 9 using HTTP, and will use the WebSocket protocol when used with Internet Explorer 10.

To see the power of Atmosphere, let’s build a simple chat application. Let’s assume our chat application will only support a single chat room to make the logic simpler.

First, let’s write the server side component. Atmosphere supports four components:

atmosphere-runtime: the core module of Atmosphere. All other modules build on top of this one. This module exposes two simple APIs for building applications: AtmosphereHandler and Meteor. The AtmosphereHandler is a simple interface to implements, whereas the Meteor API is a class that can be retrieved or injected in Servlets based applications.

atmosphere-jersey: An extension to the Jersey REST framework. This module exposes a new set of annotations, thus exposing the Atmosphere’s runtime functionality.

atmosphere-gwt: An extension to the GWT framework.

The Server Side

For this article I will use the atmosphere-runtime to demonstrate how simple it is to write a simple asynchronous application. Let’s start with the server component using an AtmosphereHandler. The AtmosphereHandler is defined as shown in Listing 1 below.

Listing 1: AtmosphereHandler

 

public interface AtmosphereHandler {

    void onRequest(AtmosphereResource resource) throws IOException;

    void onStateChange(AtmosphereResourceEvent event) throws IOException;

    void destroy();
}

 

The onRequest method is called every time a request gets mapped to the path associated with the AtmosphereHandler. The path is defined by annotating an implementation of AtmosphereHandler.

 

@AtmosphereHandlerService(path = “/<path>”)

 

In Atmosphere, an AtmosphereResource represents a physical connection. An AtmosphereResource can be used to retrieve information about the request, execute action on the response, and more importantly be used to suspend the connection during the onRequest execution. A WebServer must know when a connection needs to stay open for future actions (e. g. for WebSockets), and also when the connection needs to be upgraded to support the protocol, for example http (streaming, long-polling, jsonp or server side events).

Figure 1: onStateChange Method

 

The onStateChange method (Figure 1 ) will be invoked by Atmosphere when:

  • a broadcast operation occurs and an action needs to be taken. A Broadcaster always initiates a broadcast operation. It can be seen as channel of communication. An application can create many communication channels and retrieve them using the BroadcasterFactory class. An AtmosphereResource is always associated with one or several Broadcaster. We can also see Broadcasters as an event queue, where you can listen and get notified every time new events get broadcasted. Broadcast can happens from an onRequest, onStateChange or anywhere on the server side.

  • The connection has been closed or timed out (no activity occurs on it).

Visually it can be seen as: finally, the destroy method will be called when Atmosphere is un-deployed or stopped.

Complicated? Fortunately for us, the framework ships with AtmosphereHandlers that can be used in almost all scenarios, which allows the developer to focus on the application logic whilst it already handles the connection lifecycle. Let’s use the OnMessage<T> AtmosphereHandler to write our application (Listing 2).

Listing 2: OnMessage<T> Atmosphere Handler

 

  @AtmosphereHandlerService(
          path="/chat",
          interceptors = {AtmosphereResourceLifecycleInterceptor.class,
                                   BroadcastOnPostAtmosphereInterceptor.class})
  public class ChatRoom extends OnMessage<String> {
  
      private final ObjectMapper mapper = new ObjectMapper();
     
       @Override
     public void onMessage(AtmosphereResponse response, String message) throws IOException {
          response.getWriter()
                     .write(mapper.writeValueAsString(mapper.readValue(message, Data.class)));
    }   
  }   

 

The main idea here is to delegate as much as possible of the connection life cycle to Atmosphere’s ready-to-use component. First, we annotate the ChatRoom class with the @AtmosphereHandlerService annotation and define the path and the interceptors. AtmosphereInterceptors can be seen as Filters that always gets invoked before and after AtmosphereHandler#onRequest. AtmosphereInterceptor is useful for manipulating the request/response, handling the life cycle, etc. For example, the suspend and broadcast (Figure 2).

Figure 2: Suspend and Broadcast

As described above, two interceptors can be used to first, suspend the request (AtmosphereResourceLifeCycleInterceptor), and then broadcast the data received on every POST (BroadcastOnPostAtmosphereInterceptor). Great, we can focus only on the application’s logic.

Now instead of writing our own complete AtmosphereHandler, we can extend the OnMessage<T> handler, which delegates the broadcast operation to the onMessage method (Line 10). For our chat application, it just means we write what we receive (Line 11). If we have 50 connected users, that means the onMessage will be called 50 times so the 50 users gets the message.

We are using JSON between the client and the server. The client sent:

 {"message":"Hello World","author":"John Doe"}

 

and the server sent back to the connected browsers

 

{"message":"Hello World","author":"John Doe","time":1348578675087}

 

On Line 11 we use the Jackson library for reading the message and write it back, augmented with the time the message got received. The Data class is just a simple POJO (Listing 3).

Listing 3: Data Class

 

   public final static class Data {

        private String message;
        private String author;
        private long time;

        public Data() {
            this("","");
        }

        public Data(String author, String message) {
            this.author = author;
            this.message = message;
            this.time = new Date().getTime();
        }

        public String getMessage() {
            return message;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        public long getTime() {
            return time;
        }

        public void setTime(long time) {
            this.time = time;
        }

    }

      

Pages

Jeanfrancois Arcand
Jeanfrancois Arcand

What do you think?

JAX Magazine - 2014 - 03 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions