Archive

Archive for July, 2009

Atmosphere 0.3 Released

Atmosphere 0.3 released with support for Scala, Clustering, Injections, Grails Support, Cometd/Bayeux Protocol, many performance improvements, and many new extension points!!

IMG_0347.JPG

The 0.3 release is our most stable/tested/improved release so far. Read what changed since 0.2:

Oh and the Atmosphere core module is now fully powered by Jersey 1.1.1-ea release, and without having to change a single line of Jersey code!

For any questions or to download Atmosphere, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
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

Writing Comet based application using Scala and the Atmosphere Framework

Writing Atmosphere‘s Comet based applications is simple. Imagine using Scala instead of Java…it becomes really simple! No need to learn Lift anymore 🙂

IMG_0429.JPG

Not being an expert with Scala at all, I’ve decided to re-wrote the Chat based application I’ve recently used when adding cluster support to Atmosphere. This is really my first ever Scala application so don’t jump looking at the code! Instead, tweet me a better implementation. I will not go into the details of how to write an Atmosphere application, so if this is the first time you learn about Atmosphere, start here.

For the chat, I’ve needed to implement two methods: one that will be invoked when suspending a response (Comet’s HTTP streaming technique), and one for sending chat messages to those suspended response (to the other chat member). To do that in Scala, I did:

  1 package org.atmosphere.samples.scala.chat
  2 
  3 import javax.ws.rs.{GET, POST, Path, Produces, WebApplicationException, Consumes}
  4 import javax.ws.rs.core.MultivaluedMap
  5 import org.atmosphere.core.annotation.{Broadcast, BroadcastFilter, Suspend}
  6 import org.atmosphere.util.XSSHtmlFilter
  7 
  8 @Path("/chat")
  9 class Chat {
 10 
 11     @Suspend
 12     @GET
 13     @Produces(Array("text/html"))
 14     def suspend() = "<!-- Comet is a programming technique that enables web " +
 15                 "servers to send data to the client without having any need " +
 16                 "for the client to request it. -->\n"
 17 
 18     @Broadcast
 19     @Consumes(Array("application/x-www-form-urlencoded"))
 20     @POST
 21     @Produces(Array("text/html"))
 22     @BroadcastFilter(Array(classOf[XSSHtmlFilter],classOf[JsonpFilter]))
 23     def publishMessage(form: MultivaluedMap[String, String]) = {
 24         val action = form.getFirst("action")
 25         val name = form.getFirst("name")
 26 
 27         val result: String = if ("login".equals(action)) "System Message" + "__" + name + " has joined."
 28              else if ("post".equals(action)) name + "__" + form.getFirst("message")
 29              else throw new WebApplicationException(422)
 30 
 31         result
 32     }
 33 }

To suspend the response, I’ve annotated the suspend() method (line 11) with @Suspend. The returned value of the suspend() method will be written and then the response will be suspended, waiting for server event, e.g messages from other chatter. Now when someone publish a message, method publishMessage() will be invoked. Since the method is annotated with @Broadcast (line 18), the method’s returned value will be broadcaster to all suspended response, e.g. all suspended connections will get a chance to write the message. But since I don’t want users to publish script or any kind of attack, I’ve also added the @BroadcastFilter annotation, which will make sure to filter malicious characters. Since my javascript client expect JSONp response, I’ve decided to write a BroadcastFilter in Scala that transform the message:

  1 package org.atmosphere.samples.scala.chat
  2 
  3 import org.atmosphere.cpr.BroadcastFilter
  4 
  5 class JsonpFilter extends BroadcastFilter[String] {
  6     
  7     val BEGIN_SCRIPT_TAG = "<script type='text/javascript'>\n"
  8     val END_SCRIPT_TAG = "</script>\n"
  9     
 10     def filter(m : String) = {
 11         var name = m
 12         var message = ""
 13         
 14         if (m.indexOf("__") > 0) {
 15             name = m.substring(0, m.indexOf("__"))
 16             message = m.substring(m.indexOf("__") + 2)
 17         }
 18         
 19         val result: String = (BEGIN_SCRIPT_TAG + "window.parent.app.update({ name: \""
 20                 + name + "\", message: \""
 21                 + message + "\" });\n"
 22                 + END_SCRIPT_TAG)
 23     }
 24 }

Then I just pass the name of that class to the @BroadcastFilter annotation:

 @BroadcastFilter(Array(classOf[XSSHtmlFilter],classOf[JsonpFilter]))

What amaze me here is one filter is written in Java, the other one in Scala!. That’s it. With those two classes, the Atmosphere REST Chat demo works. To complicate the application, I’ve decided to deploy it into a cluster. This is simple to achieve by annotating the publishMesaage with:

  @Cluster(Array(classOf[JGroupsFilter]))

Now my Scala application is Comet-Cluster enabled! That was fun!

For any questions or to download the above sample, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

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

@Cluster: Clustering your Comet application using Atmosphere

It is really simple to add clustering support to an Atmosphere’s Comet based application, and deploy it inside any Servlet Container supporting Servlet 3.0, Comet or not. You just have to decide which group technology you want to use, thanks to Atmosphere Plug in: Shoal or JGroups!

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

First, if you want to get started with Atmosphere, I recommend to take a look at atmosphere-cpr (POJO based) and atmosphere-core (Annotations/REST based) introduction first. Let’s start with the Chat sample written against atmosphere-cpr. The AtmosphereHandler#onEvent looks like

 88         if (req.getMethod().equalsIgnoreCase("GET")) {
 89             event.suspend();
 90             Broadcaster bc = event.getBroadcaster();
 91             bc.getBroadcasterConfig().addFilter(new XSSHtmlFilter());
 92             bc.broadcast(event.getAtmosphereConfig().getWebServerName()
 93                     + "**has suspended a connection from "
 94                     + req.getRemoteAddr());
 95         } else if (req.getMethod().equalsIgnoreCase("POST")) {
 96             res.setCharacterEncoding("UTF-8");
 97             String action = req.getParameterValues("action")[0];
 98             String name = req.getParameterValues("name")[0];
 99             
100             if ("login".equals(action)) {
101                 req.getSession().setAttribute("name", name);
102                 event.getBroadcaster().broadcast("System Message from "
103                         + event.getAtmosphereConfig().getWebServerName() + "**" + name + " has joined.");
104                 res.getWriter().write("success");
105                 res.getWriter().flush();
106             } else if ("post".equals(action)) {
107                 String message = req.getParameterValues("message")[0];
108                 event.getBroadcaster().broadcast(name + "**" + message);
109                 res.getWriter().write("success");
110                 res.getWriter().flush();
111             } else {
112                 res.setStatus(422);
113                 
114                 res.getWriter().write("success");
115                 res.getWriter().flush();
116             }   
117         }   
118         return event;
119     }

One nice feature of Atmosphere is the support of Broadcaster. Broadcaster are used to broadcast messages between suspended responses, e.g. for the chat sample, every time a new message is entered by a user, we use a Broadcaster to broadcast that message to all other users. Broadcaster supports BroadcastFilter, which are really useful for manipulating messages before they get written by the set of suspended connections. If you look at line 80/81 above, we configure the Broadcaster with the XSSHtmlFilter to prevent a malicious chatter to send us Javascript as message, and push that script back to other user. Indeed, BroadcastFilters are quite useful when it’s time to filter/transform/agreggate messages or to broadcast messages to other components like EJB, JMS topics/queues…or cluster!. The good news is Atmosphere supports two clusters technology: JGroups and Shoal. To add cluster support to the above Chat application, all we need to do is:

 84         res.setContentType("text/html");
 85         res.addHeader("Cache-Control", "private");
 86         res.addHeader("Pragma", "no-cache");
 87 
 88         if (req.getMethod().equalsIgnoreCase("GET")) {
 89             event.suspend();
 90             Broadcaster bc = event.getBroadcaster();
 91             bc.getBroadcasterConfig().addFilter(
 92                 new JGroupsFilter(bc, event.getAtmosphereConfig().getWebServerName()));
 93             bc.getBroadcasterConfig().addFilter(new XSSHtmlFilter());

Just need to add the XXXFilter, where xxx is the group technology you want to use: ShoalFilter or JGroupsFilter, and that’s it! Just adding line 91 makes your atmosphere-cpr application clustered and any invocation of Broadcaster.broadcast will reach all AtmosphereHandler instance in the same cluster.

HUH that was too complicated 🙂! Let’s now use atmosphere-core’s support for annotations. The Atmosphere’s REST based Chat sample looks like:

 53 @Path("/chat")
 54 public class ResourceChat {
 55 
 56     @Suspend()
 57     @GET
 58     @Produces("text/html")
 59     public String suspend() {
 60         return "";
 61     }           
 62     
 63     @Broadcast
 64     @Consumes("application/x-www-form-urlencoded")
 65     @POST
 66     @Produces("text/html")
 67     @BroadcastFilter({XSSHtmlFilter.class,JsonpFilter.class})
 68     public String publishMessage(MultivaluedMap form) {
 69         String action = form.getFirst("action");
 70         String name = form.getFirst("name");
 71         
 72         if ("login".equals(action)) {
 73             return ("System Message" + "__" + name + " has joined.");
 74         } else if ("post".equals(action)) { 
 75             return name + "__" + form.getFirst("message");
 76         } else {
 77             throw new WebApplicationException(422);
 78         }   
 79     }   
 80 } 

In order to add clustering support, we just need to annotate the method that broadcast message, e.g:

 65     @Broadcast
 66     @Consumes("application/x-www-form-urlencoded")
 67     @POST
 68     @Produces("text/html")
 69     @BroadcastFilter({XSSHtmlFilter.class,JsonpFilter.class})
 70     @Cluster(
 71         name="chat",
 72         value=ShoalFilter.class
 73     )
 74     public String publishMessage(MultivaluedMap form) {
 75         String action = form.getFirst("action");
 76         String name = form.getFirst("name");
 77         
 78         if ("login".equals(action)) {
 79             return ("System Message" + "__" + name + " has joined.");
 80         } else if ("post".equals(action)) {
 81             return name + "__" + form.getFirst("message");
 82         } else {
 83             throw new WebApplicationException(422);
 84         }
 85     }
 86 }

That’s it!. By just annotating the proper method, Atmosphere will makes sure to properly configure the Shoal/JGroups so every broadcast will be shared inside the cluster.

For any questions or to download the above sample, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

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