Archive

Archive for May, 2009

Getting started with the Atmosphere Framework part II: Writing a REST application with Comet support

This time I will demonstrate how easy and dead simple is to write a REST application using Atmosphere annotations…with the help of Jersey!

Note: The Atmosphere Framework have evolved since the release of that blog. Please visit our web site for an updated sample and white paper

IMG_0128.JPG

In part I I’ve described how easy it is to write an asynchronous/comet based application using an AtmosphereHandler. I strongly recommend you first read part I to understand the steps needed to generate the proper layout for your application. I also assume you already know how to write REST application using Jersey. Why? Let me introduce a new module available with the Atmosphere Framework named atmosphere-core. As a reminder, in part I have introduced atmosphere-cpr (Comet Portable Runtime), which allow a developer to write an AtmosphereHandler and deploy it in any Servlet container. Under the hood, the CPR always make sure its use the native comet support available, e.g if deployed on WebLogic, use the AsyncServlet, if deployed in Grizzy/GlassFish, use the Grizzly Comet Framework etc. Yes, you write once and deploy everywhere your Comet application.

The atmosphere-core module builds on top of the CPR and Jersey! The aim of Atmosphere is to simplify the development of Comet application, and since every time I’ve used Jersey, I always found it is really easy to write powerful application…then I’ve decided to build on top of Jersey! So here we come: announcing atmosphere-core v.0.2-M1, powered by atmosphere-cpr and Jersey (including all functionality supported by Jersey!).

Like it part I, let’s first define our META-INF/atmosphere.xml:


<atmosphere-handlers>
    <atmosphere-handler context-root="/resources" class-name="org.atmosphere.handler.ReflectorServletProcessor">
        <property name="servletClass" value="com.sun.jersey.spi.container.servlet.ServletContainer"/>
    </atmosphere-handler>
</atmosphere-handlers>

If we compare with part I, this time the class-name we are using is a special AtmosphereHandler called ReflectorServletProcessor (RSP). The RSP listen to AtmosphereEvent like a normal AtmosphereHandler, but delegate the processing on the event to a servlet.service() method (a normal Servlet invocation), with a special addition which consist of storing the AtmosphereEvent as an HttpServletRequest attribute. The idea here is to support existing framework (like Jersey, Strut, JSF, etc.) without the need to modify those to support Atmosphere. Most of those frameworks support extension point, and all of them just need a reference to the AtmosphereEvent in order to suspend, resume and broadcast. Why is it called a Reflector? Mostly because every message broadcasted will be sent back to the client without any filtering or transformation. Say differently all suspended response will write as it is the message they receive (See AtmosphereHandler.onMessage for more info). Anyway as a developer you don’t need to know that…but if you are a framework developer, this is how you can use the Atmosphere Framework easily. In the current case, the RSP is configured using the Jersey’s main Servlet, called ServletContainer. As soon as you deploy your application, the Atmosphere framework will handle the lifecycle of the Jersey Servlet.

Next step is to write the web.xml:

        <servlet>
            <description>AtmosphereServlet</description>
            <servlet-name>AtmosphereServlet</servlet-name>
            <servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
            <!-- Uncomment if you want to use Servlet 3.0 Async Support
            <async-supported>true</async-supported>
            -->
            <init-param>
                <param-name>com.sun.jersey.config.property.packages</param-name>
                <param-value>org.atmosphere.samples.chat.resources</param-value>
            </init-param>
            <init-param>
                <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
                <param-value>org.atmosphere.core.AtmosphereFilter</param-value>
            </init-param>
            <load-on-startup>0</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>AtmosphereServlet</servlet-name>
            <url-pattern>/resources/*</url-pattern>
        </servlet-mapping>

In short, we define our usual AtmosphereServlet and maps all requests to /resources to it. There is two Jersey related properties defined. The only one you need to care is com.sun.jersey.config.property.packages. The property is required by Jersey to locate where to look for your application. Eventually Atmosphere will auto detect those, but for now you need to add it. In our case, it the fully qualified name of the package of our application. The second property is the Jersey extension point. No need to talk about it this time.

We are now ready to re-write the chat demo described in part I, re-using the same javascript code on the client side but this time without using any AtmosphereHandler, AtmosphereEvent, or Broadcaster, but instead using annotation (yeah!). Let’s start with the survival tool of Atmosphere annotation, which are @Suspend, @Resume and @Broadcast. You can write very powerful application by just using those I will talk about more annotations in part III). What the annotations are for:

  • @Suspend: write the returned value of the annotated method, then suspend the response
  • @Resume: resume the response by committing it
  • @Broadcast: broadcast to all suspended connection the returned value of the annotated method

So without going into the details of REST and Jersey (see this tutorial), writing a chat application just consist of:

 
    @Suspend // the returned String will be written and then response suspended
    @GET
    @Produces("text/html")
    public String cometGet() {
        return "&lt!-- Comet is a programming technique that enables web " +
                    "servers to send data to the client without having any need " +
                    "for the client to request it. -->\n";
    }

    @Broadcast // The returned String will be broadcasted to all suspended response.
    @Consumes("application/x-www-form-urlencoded")
    @POST
    @Produces("text/html")
    public String cometPost(
       @FormParam("action") String action,
       @FormParam("name") String name,
        MultivaluedMap form) { 

        if ("login".equals(action)) {
            return BEGIN_SCRIPT_TAG + toJsonp("System Message", name + " has joined.") + END_SCRIPT_TAG;
        } else if ("post".equals(action)) {
            return BEGIN_SCRIPT_TAG + toJsonp(name, form.getFirst("message")) + END_SCRIPT_TAG;
        } else {
            throw new WebApplicationException(422);
        }
    }

Simple, is it? The way it works is the cometGet will be invoked, its returned value will be written back to the client and the response suspended. When the cometPost gets invoked, it’s returned value will be broadcasted, e.g. all suspended responses will be invoked and the value written as it is to the client since we are using the ReflectorServletProcessor. That’s it! With the use of two simple annotations, we were able to write our first atmosphere-core REST based application. OK, it’s REST twisted, but still :-).

You can download the entire application here, or browse the source code. If you are already using Atmosphere, you must upgrade to Atmosphere 0.2-M1 to take advantages of those new functionality.

As usual, THANKS to the great feedback I’ve got so far. Early adopter, you make a big difference! For any questions, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there! And if you are going to JavaOne, don’t miss the Atmosphere BOF on Tuesday @ 8h30pm with Jersey lead and Java Champion’s Paul Sandoz, which is a great speaker without a Quebecois accent

var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.&#8221; : “http://www.&#8221;);
document.write(unescape(“%3Cscript src=’” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));

var pageTracker = _gat._getTracker(“UA-3111670-3″);
pageTracker._initData();
pageTracker._trackPageview();

technorati:

Categories: Uncategorized
Follow

Get every new post delivered to your Inbox.

Join 51 other followers