Archive

Archive for March, 2009

Getting started with Atmosphere CPR part 1: Writing the HelloWord of Comet….a Chat application

Time to get started with Atmosphere CPR (Comet Portable Runtime)! In this first part, I will describe how to write a chat application and deploy in on Tomcat, Jetty and GlassFish.

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_0att.JPG

Let do the basic first. Let’s use Maven 2 and create the file structure:

 %  mvn archetype:create -DgroupId=org.atmosphere.samples
 -DartifactId=chat -DarchetypeArtifactId=maven-archetype-webapp

Which will create the following structure:

./chat
./chat/pom.xml
./chat/src
./chat/src/main
./chat/src/main/resources
./chat/src/main/webapp
./chat/src/main/webapp/index.jsp
./chat/src/main/webapp/WEB-INF
./chat/src/main/webapp/WEB-INF/web.xml

The next step is to add the required context.xml and save it under META-INF/

.
./chat
./chat/pom.xml
./chat/src
./chat/src/main
./chat/src/main/resources
./chat/src/main/webapp
./chat/src/main/webapp/index.jsp
./chat/src/main/webapp/WEB-INF
./chat/src/main/webapp/WEB-INF/lib
./chat/src/main/webapp/WEB-INF/lib/atmosphere-portable-runtime-0.1-ALPHA1.jar
./chat/src/main/webapp/WEB-INF/web.xml
./chat/src/main/webapp/META-INF
./chat/src/main/webapp/META-INF/context.xml

Finally, let’s add Atmosphere CPR library to the pom.xml so it gets added to our WEB-INF/lib

         <dependency>
             <groupId>org.atmosphere</groupId>
             <artifactId>atmosphere-portable-runtime</artifactId>
             <version>0.1-ALPHA1</version>
         </dependency>

         <repositories>
              <repository>
                <id>maven2.java.net</id>
                <name>Java.net Repository for Maven 2</name>
                <url>http://download.java.net/maven/2</url>
              </repository>
         </repositories>

We are now ready to write our first AtmosphereHandler, which is the central piece of any Atmosphere CPR application. Let’s just implement this interface

 38 package org.atmosphere.samples.chat;
 39 
 40 import java.io.IOException;
 41 import java.util.logging.Logger;
 42 import javax.servlet.http.HttpServletRequest;
 43 import javax.servlet.http.HttpServletResponse;
 44 import org.atmosphere.cpr.AtmosphereEvent;
 45 import org.atmosphere.cpr.AtmosphereHandler;
 46 import org.atmosphere.cpr.Broadcaster;
 47 
 48 /**
 49  * Simple AtmosphereHandler that implement the logic to build a Chat application.
 50  *
 53  */
 54 public class ChatAtmosphereHandler implements AtmosphereHandler {

As described here, implementing an AtmosphereHandler requires two methods:


    /**
     * When a client send a request to its associated {@link AtmosphereHandler}, it can decide
     * if the underlying connection can be suspended (creating a Continuation)
     * or handle the connection synchronously.
     *
     * It is recommended to only suspend request for which HTTP method is a GET
     * and use the POST method to send data to the server, without marking the
     * connection as asynchronous.
     *
     * @param event an {@link AtmosphereEvent}
     * @return the modified {@link AtmosphereEvent}
     */
    public AtmosphereEvent onEvent(AtmosphereEvent event) throws IOException;


    /**
     * This method is invoked when the {@link Broadcaster} execute a broadcast
     * operations. When this method is invoked its associated {@link Broadcaster}, any
     * suspended connection will be allowed to write the data back to its
     * associated clients.
     *
     * @param event an {@link AtmosphereEvent}
     * @return the modified {@link AtmosphereEvent}
     */
    public AtmosphereEvent onMessage(AtmosphereEvent event) throws IOException;

The onEvent method will be invoked every time a request gets mapped to it associated AtmosphereHandler. There is two way to map request to an AtmosphereHandler. By default, the name of the AtmosphereHandler will be used, e.g. assuming we name our web application chat.war, a request to http://localhost:8080/chat/ChatAtmosphereHandler will invoke the AtmophereHandler.onEvent. For the chat, let’s assume we will suspend the response when the browser is sending us GET request

 77         HttpServletRequest req = event.getRequest();
 78         HttpServletResponse res = event.getResponse();
 79 
 80         res.setContentType("text/html");
 81         res.addHeader("Cache-Control", "private");
 82         res.addHeader("Pragma", "no-cache");
 83         if (req.getMethod().equalsIgnoreCase("GET")) {
 84             res.getWriter().write("<!-- Comet is a programming technique that enables web " +
 85                     "servers to send data to the client without having any need " +
 86                     "for the client to request it. -->\n");
 87             res.getWriter().flush();
 88             event.suspend();

The central piece is the AtmosphereEvent, from which we can retrieve the request and response object. Next we do some setup and then once we are ready we just need to invoke the AtmosphereEvent.suspend(), which will automatically tell Atmosphere CPR to not commit the response. Not committing the response means we can re-use it later for writing. In the current exercise, we will use the suspended response when someone enter join or enter sentence inside the chat room. Now let’s assume when a user logs in or enter sentences, the browser set a POST (posting some data). So when a user logs in

 90             res.setCharacterEncoding("UTF-8");
 91             String action = req.getParameterValues("action")[0];
 92             String name = req.getParameterValues("name")[0];
 93 
 94             if ("login".equals(action)) {
 95                 event.getBroadcaster().broadcast(
 96                         "System Message from "
 97                         + event.getWebServerName(), name + " has joined.");
 98                 res.getWriter().write("success");
 99                 res.getWriter().flush();

The important piece here is line 94. A Broadcaster’s role is to publish data to the suspended responses. As soon as you broadcast data, all suspended responses will be given a chance to write the content of the broadcast. Above we just broadcast the name and also which WebServer we are running on (for demo purpose). Calling Broadcaster.broadcast() will in turn invoke your AtmosphereHandler.onMessage will all suspended response. Here let’s assume we just reflect (write) what we receive:

124     public AtmosphereEvent
125             onMessage(AtmosphereEvent event) throws IOException {
126         HttpServletRequest req = event.getRequest();
127         HttpServletResponse res = event.getResponse();
128         res.getWriter().write(event.getMessage().toString());
129         res.getWriter().flush();
130         return event;
131     }

Next is when the user enter some chat messages:

100             } else if ("post".equals(action)) {
101                 String message = req.getParameterValues("message")[0];
102                 event.getBroadcaster().broadcast(BEGIN_SCRIPT_TAG + toJsonp(name, message) + END_SCRIPT_TAG);
103                 res.getWriter().write("success");
104                 res.getWriter().flush();

Here we encode the message using the JSON format so it make simple for the client’s javascript to update the page. That’s it for the AtmosphereHandler. You can see the complete source code here.

Now let’s assume we want to have a more fine grain way to map our AtmosphereHandler to the request. To achieve that, create a file called atmosphere.xml under src/main/webapp/META-INF/ and define the mapping you want:

  1 <atmosphere-handlers>
  2     <atmosphere-handler context-root="/chat" class-name="org.atmosphere.samples.chat.ChatAtmosphereHandler">
  3         <property name="name" value="Chat"/>
  4     </atmosphere-handler>
  5 </atmosphere-handlers>

With this file, all requests to /chat will be mapped to our ChatAtmosphereHandler.

Now let’s explore the client side. First, let’s write a very simple index.html file:

  1 <?xml version="1.0" encoding="UTF-8" ?>
  2 <!DOCTYPE html
  3 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  5 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  6     <head>
  7         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  8         <title>Atmosphere Chat</title>
  9         <link rel="stylesheet" href="stylesheets/default.css" type="text/css" />
 10         <script type="text/javascript" src="javascripts/prototype.js"></script>
 11         <script type="text/javascript" src="javascripts/behaviour.js"></script>
 12         <script type="text/javascript" src="javascripts/moo.fx.js"></script>
 13         <script type="text/javascript" src="javascripts/moo.fx.pack.js"></script>
 14         <script type="text/javascript" src="javascripts/application.js"></script>
 15     </head>
 16     <body>
 17         <div id="container">
 18             <div id="container-inner">
 19                 <div id="header">
 20                     <h1>Atmosphere Chat</h1>
 21                 </div>
 22                 <div id="main">
 23                     <div id="display">
 24                     </div>
 25                     <div id="form">
 26                         <div id="system-message">Please input your name:</div>
 27                         <div id="login-form">
 28                             <input id="login-name" type="text" />
 29                             <br />
 30                             <input id="login-button" type="button" value="Login" />
 31                         </div>
 32                         <div id="message-form" style="display: none;">
 33                             <div>
 34                                 <textarea id="message" name="message" rows="2" cols="40"></textarea>
 35                                 <br />
 36                                 <input id="post-button" type="button" value="Post Message" />
 37                             </div>
 38                         </div>
 39                     </div>
 40                 </div>
 41             </div>
 42         </div>
 43         <iframe id="comet-frame" style="display: none;"></iframe>
 44     </body>
 45 </html>

Simple form that will send back to the server the login’s name and the chat message entered. To update on the fly the interface as soon as our ChatAtmosphereHandler.onMessage write/send us data, let’s use prototype and behavior javascript. I’m assuming you are either familiar with those framework or have basic understanding how they work. This will be defined under application.js. As soon as the user enter its login name, let’s do

 12     login: function() {
 13         var name = $F('login-name');
 14         if(! name.length > 0) {
 15             $('system-message').style.color = 'red';
 16             $('login-name').focus();
 17             return;
 18         }
 19         $('system-message').style.color = '#2d2b3d';
 20         $('system-message').innerHTML = name + ':';
 21 
 22         $('login-button').disabled = true;
 23         $('login-form').style.display = 'none';
 24         $('message-form').style.display = '';
 25 
 26         var query =
 27         'action=login' +
 28         '&name=' + encodeURI($F('login-name'));
 29         new Ajax.Request(app.url, {
 30             postBody: query,
 31             onSuccess: function() {
 32                 $('message').focus();
 33             }
 34         });
 35     },

When the user write new chat message, let’s push

 36     post: function() {
 37         var message = $F('message');
 38         if(!message > 0) {
 39             return;
 40         }
 41         $('message').disabled = true;
 42         $('post-button').disabled = true;
 43 
 44         var query =
 45         'action=post' +
 46         '&name=' + encodeURI($F('login-name')) +
 47         '&message=' + encodeURI(message);
 48         new Ajax.Request(app.url, {
 49             postBody: query,
 50             requestHeaders: ['Content-Type',
 51             'application/x-www-form-urlencoded; charset=UTF-8'],
 52             onComplete: function() {
 53                 $('message').disabled = false;
 54                 $('post-button').disabled = false;
 55                 $('message').focus();
 56                 $('message').value = '';
 57             }
 58         });
 59     },

Now when we get response, we just update the page using

 60     update: function(data) {    
 61         var p = document.createElement('p');
 62         p.innerHTML = data.name + ':
' + data.message; 63 64 $('display').appendChild(p); 65 66 new Fx.Scroll('display').down(); 67 }

The way the index.html and application.js interact is simply defined by:

 69 var rules = {
 70     '#login-name': function(elem) {
 71         Event.observe(elem, 'keydown', function(e) {
 72             if(e.keyCode == 13) {
 73                 $('login-button').focus();
 74             }
 75         });
 76     },
 77     '#login-button': function(elem) {
 78         elem.onclick = app.login;
 79     },
 80     '#message': function(elem) {
 81         Event.observe(elem, 'keydown', function(e) {
 82             if(e.shiftKey && e.keyCode == 13) {
 83                 $('post-button').focus();
 84             }
 85         });
 86     },
 87     '#post-button': function(elem) {
 88         elem.onclick = app.post;
 89     }
 90 };
 91 Behaviour.addLoadEvent(app.initialize);
 92 Behaviour.register(rules);

See the complete source code here. So far so good, now we are ready to deploy our application into our favorite WebServer.

Here are simple pictures from different WebServer:

Glassfish v3

GFv3.jpg

Jetty

jetty.jpg

Tomcat

tomcat.jpg

Grizzly

Grizzly.jpg

Wow that was easy! Download the war or src to get started. Follow us on Twitter for daily update about the project status and ask your questions using users@atmosphere.dev.java.net

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: Atmosphere

Atmosphere 0.1-ALPHA1 released with Support for Tomcat, Jetty, Grizzly and GlassFish

Atmosphere 0.1-ALPHA1 is now officially released and support Tomcat, Jetty, Grizzly and GlassFish. Finally a Comet/Ajax Push framework you can build on top of it and deploy everywhere!

IMG_0314.JPG

In the upcoming weeks I will start blogging about how easy it is to write cool Comet Application using Atmosphere.

Below is the official announcement


Salut,

Atmosphere CPR (Comet Portable Runtime) module 0.1-alpha1 is now ready to be tested, 
explored and deployed. Please visit out web site to download it:

    * http://atmosphere.dev.java.net

    * http://is.gd/ngPP

if you are using Maven2, just add:

         <dependency>
             <groupId>org.atmosphere</groupId>
             <artifactId>atmosphere-portable-runtime</artifactId>
             <version>0.1-ALPHA1</version>
         </dependency>


As always, your feed back is more than appreciated.  Please visit the
users list and leave some comments:

    * users@atmosphere.dev.java.net

or

    * http://twitter.com/atmosphere_java


This is our first ever release and we support
=============================================

* Autodetection of Container (we are are portable!)
* Tomcat 6.x
* Jetty 6.x
* GlassFish v2.x
* GlassFish v3 Prelude, v3
* Grizzly 1.9.x
* OSGi enabled

What coming in 0.1 GA
======================
* WLS (BEA) support
* Maven create-artifact support
* Resin support
* Servlet 3.0 support
* Blocking I/O Server support (Tomcat)
* Several Broadcaster (for filtering/aggregating)
* Async I/O (emulated).

The Atmosphere roadmap/design can be browsed from here:

    * http://is.gd/l3hN

Checking out the release
=========================
    * svn https://www.dev.java.net/svn/atmosphere/tags/atmosphere_0_1_ALPHA1


How to get started
===================

The main interface for creating Atmosphere application is called AtmosphereHandler:

     * https://atmosphere.dev.java.net/nonav/apidocs/org/atmosphere/cpr/AtmosphereHandler.html

To suspend/resume a response or broadcast messages, use:

     * https://atmosphere.dev.java.net/nonav/apidocs/org/atmosphere/cpr/AtmosphereEvent.html

0.1-alpha1 ship with two easy to learn sample:

(Chat)

    * http://is.gd/ngJQ

(Flickr)

    * http://is.gd/ngJW

Those samples can be downloaded from here:

    * http://is.gd/ngQv


What your application needs in order to use Atmosphere
======================================================

To bundle Atmosphere inside your web application, download atmosphere-cpr jar file from here:

http://download.java.net/maven/2/org/atmosphere/atmosphere-portable-runtime/

and put it under WEB-INF/lib

Download the context.xml file (required for Tomcat support):

   * http://is.gd/ngMm

and put it under META-INF/ or your war file

Optionally, you can define you AtmosphereHandler by creating an atmosphere.xml like:

   * http://is.gd/ngM1

under META-INF of you war file

This is optional, as the framework can autodetect your AtmosphereHandler and 
map it using it class's name (see the chat demo for more info).

File issue here:

  * https://atmosphere.dev.java.net/issues/


Documentations
==============

(Javadoc)

    * http://is.gd/ngOy

(Source code)

    * http://is.gd/ngOW

A set of blogs/tutorial will be available soon from here

    * http://is.gd/ngPm

Follow us on Twitter for daily update about the project status.

This is your chance to participate to a revolution. Join the community soon 🙂

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

GlassFish v3 Extreme Makeover using GrizzlyAdapter part 1: Hello World

GlassFish v3 offer a lot of extensibility point, and one of them is quite interesting: GrizzlyAdapter. Any applications developed using the GrizzlyAdapter API can be deployed inside v3 and transform a fish into a monster…an extreme makeover!

IMG_0433.JPG

As Jerome described, GlassFish v3 is built on top of Grizzly, yet up to now, it was not possibly to deploy a native GrizzlyAdapter directly inside V3. Technically, at startup GlassFish runtime (OSGi or not) bootstrap and instantiate a Grizzly’s SelectorThread, one of the main entry point when embedding Grizzly (more info here I,II,II,IV, V, VI). Next it configure the special GrizzlyAdapter called ContainerMapper. That mapper is responsible for mapping request to its associated container (GrizzlyAdapter). It is not always clear in the GlassFish official documentation (they hide the monster ;-)), but many cool features of v3 are GrizzlyAdapter: JRuby, Groovy, admin CLI support, automatic download of admin gui, etc. So technically it was “easy” to extend that functionality and allow any GrizzlyAdapter to be deployed. It also means that any GrizzlyAdapter can now be deployed in GlassFish and gets nice features like admin support, monitoring, etc., features that are not always available in Grizzly itself.

So let’s start simple. Let’s write an HelloWord example. The code looks like

  1 
  2 import com.sun.grizzly.tcp.http11.GrizzlyAdapter;
  3 import com.sun.grizzly.tcp.http11.GrizzlyRequest;
  4 import com.sun.grizzly.tcp.http11.GrizzlyResponse;
  5 import java.io.IOException;
  6 
  7 public class HelloWorldAdapter extends GrizzlyAdapter {
  8 
  9     @Override
 10     public void service(GrizzlyRequest request, GrizzlyResponse response) {
 11         try {
 12             response.getWriter().println("HelloWorld");
 13         } catch (IOException ex) {
 14             ex.printStackTrace();
 15         }
 16     }
 17 }

In order to be deployed in GlassFish v3, let’s just jar this class, and add under META-INF a file called grizzly-glassfish.xml which tells GlassFish that this jar is a Grizzly application, and which context path will map to this application:

<adapters>
    <adapter context-root="/helloworld" class-name="com.sun.grizzly.http.HelloWorldAdapter"/>
</adapters>

Now just deploy this mini monster using GlassFish v3 admin cli:

% ${glassfish.home}/bin/asadmin deploy helloworld.jar

The v3 log will looks like:

INFO: The Admin Console is already installed, but not yet loaded.
9-Mar-2009 5:15:35 PM com.sun.enterprise.v3.server.AppServerStartup run
INFO: GlassFish v3  startup time : Felix(1257ms) startup services(1162ms) total(2419ms)
9-Mar-2009 5:15:37 PM  
INFO: Deployment expansion took 4
9-Mar-2009 5:15:37 PM org.glassfish.deployment.admin.DeployCommand execute
INFO: Deployment of helloworld done is 368 ms

Now let add complexity to our GrizzlyAdapter by adding some properties. Let’s customize the returned message:

  1 package com.sun.grizzly.http;
  2 
  3 import com.sun.grizzly.tcp.http11.GrizzlyAdapter;
  4 import com.sun.grizzly.tcp.http11.GrizzlyRequest;
  5 import com.sun.grizzly.tcp.http11.GrizzlyResponse;
  6 import java.io.IOException;
  7 
  8 public class HelloWorldAdapter extends GrizzlyAdapter {
  9 
 10     private String helloWorld = "HelloWorld";
 11 
 12     @Override
 13     public void service(GrizzlyRequest request, GrizzlyResponse response) {
 14         try {
 15             response.getWriter().println(helloWorld);
 16         } catch (IOException ex) {
 17             ex.printStackTrace();
 18         }
 19     }
 20 
 21     /**
 22      * Set the returned message
 23      */
 24     public void setMessage(String helloWorld){
 25         this.helloWorld = helloWorld;
 26     }
 27 
 28 }

Now to customize the message, let’s add a new property in our grizzly-config.xml file:

<adapters>
    <adapter context-root="/helloworld" class-name="com.sun.grizzly.http.HelloWorldAdapter">
        <property name="message" value="I like GrizzlyAdapter!"/>
    </adapter>
</adapters>

Freaking simple :-). So the sky is the limit…any Grizzly based application can now be deployed in v3. One of them can be found here. That project aim is to develop a GrizzlyAdapter acting as a proxy…which means you can transform v3 into a proxy! Note that you don’t need to anything else installed (just the smallest distribution of v3). Another example is are Grizzlet, which are simple POJO for doing Comet based application. Since the support for Grizzlet was made from a GrizzlyAdapter, you can always “jar|war” your Grizzlet and deploy them in v3 now. An example can be downloaded here. The grizzly-glassfisn.xml looks like:

<adapters>
    <adapter context-root="/JMakiGrizzlet" class-name="com.sun.grizzly.container.GrizzletAdapter">
        <property name="grizzletName" value="com.sun.grizzly.grizzlet.JMakiGrizzlet"/>
        <property name="rootFolder" value="../domains/domain1/applications/"/>
    </adapter>
</adapters>

This is just the beginning, but I recommend you follow Jerome’s blog on what can also be extended in v3…as they will also apply to GrizzlyAdapter based application!! For any questions, post them to users@grizzly.dev.java.net or tweet us here.

_uacct = “UA-3111670-1”;
urchinTracker();

technorati:

Categories: Uncategorized