Jean-Francois Arcand works for Ning.com. Previously he has worked for Sun Microsystems where he created Grizzly (NIO Framework) , Atmosphere and was a significant contributor to the GlassFish Application Server. Jean-Francois is a DZone MVB and is not an employee of DZone and has posted 23 posts at DZone. You can read more from them at their website. View Full User Profile

Writing Comet Applications Using Scala and the Atmosphere Framework

08.26.2009
| 13250 views |
  • submit to reddit

Writing Atmosphere's Comet based applications is simple. Imagine using Scala instead of Java...it becomes really simple! No need to learn Lift anymore :-)

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:

  package org.atmosphere.samples.scala.chat

import javax.ws.rs.{GET, POST, Path, Produces, WebApplicationException, Consumes}
import javax.ws.rs.core.MultivaluedMap
import org.atmosphere.core.annotation.{Broadcast, BroadcastFilter, Suspend}
import org.atmosphere.util.XSSHtmlFilter

@Path("/chat")
class Chat {

var JUNK : String = "<!-- 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"

@Suspend
@GET
@Produces(Array("text/html;charset=ISO-8859-1"))
def suspend() = {
var s = new StringBuilder()
for (i <- 0 to 10){
s.append(JUNK)
}
s.toString()
}

@Broadcast
@Consumes(Array("application/x-www-form-urlencoded"))
@POST
@Produces(Array("text/html"))
@BroadcastFilter(Array(classOf[XSSHtmlFilter],classOf[JsonpFilter]))
def publishMessage(form: MultivaluedMap[String, String]) = {
val action = form.getFirst("action")
val name = form.getFirst("name")

val result: String = if ("login".equals(action)) "System Message" + "__" + name + " has joined."
else if ("post".equals(action)) name + "__" + form.getFirst("message")
else throw new WebApplicationException(422)

result
}
}

To suspend the response, I've annotated the suspend() method (line 15) 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 26), 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:

  package org.atmosphere.samples.scala.chat

import org.atmosphere.cpr.BroadcastFilter

class JsonpFilter extends BroadcastFilter[String] {

val BEGIN_SCRIPT_TAG = "<script type='text/javascript'>\n"
val END_SCRIPT_TAG = "</script>\n"

def filter(m : String) = {
var name = m
var message = ""

if (m.indexOf("__") > 0) {
name = m.substring(0, m.indexOf("__"))
message = m.substring(m.indexOf("__") + 2)
}

val result: String = (BEGIN_SCRIPT_TAG + "window.parent.app.update({ name: \""
+ name + "\", message: \""
+ message + "\" });\n"
+ END_SCRIPT_TAG)
}
}

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!

Published at DZone with permission of Jean-Francois Arcand, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jacek Furmankiewicz replied on Wed, 2009/08/26 - 9:03am

I am far from a Scala expert, but I think your code should use NodeSeq XML literals, instead of creating the XML as a String by hand.

http://burak.emir.googlepages.com/scalaxbook.docbk.html#id2892433

Also, looking at the Comet examples in the Lift book:

http://the-lift-book.googlegroups.com/web/master.pdf?gda=KzqvSzwAAAB6j5BPAFhQJU5FBGzkVeURyBcsdm_KCieSjdIK5KVfqZhQBWRFAWHsPl_3piZ--U79Wm-ajmzVoAFUlE7c_fAt

on page 144 (Writing Comet applications) their native Scala approach seems a lot more concise and readable IMHO (without the annotation upon annotation that is evident in your code sample).

 This ain't Java anymore :-)

P.S. And Lift is definitely worth learning regardless. Some very original ideas in there

Jean-Francois Arcand replied on Wed, 2009/08/26 - 3:21pm in response to: Jacek Furmankiewicz

@Jacek Thanks for the pointer. Like I said, that was my first Scala web application. As for Lift, the biggest limitation(see p. 142 of the document you pointed out) is that Lift will scale only when used with Jetty. Atmosphere doesn't suffer that limitation, e.g it always use the underlying native Comet implementation. So if you run on Tomcat, Tomcat AIO will be used. Same for GlassFish, it will use Grizzly Comet.

 Aside from that, I suspect you could run Lift on top of Atmosphere and get the same great scalability. This blog talk about Jersey (REST) augmented to support Atmosphere Annotation, but any other Framework like Lift can be run on top of Atmosphere. As for p.144 sample, well, it's your point of view :-) :-) And yes Lift is a nice Framework :-)

Jacek Furmankiewicz replied on Wed, 2009/08/26 - 8:41pm in response to: Jean-Francois Arcand

Good points, well made. But then I only consider Jetty these days anyway, due to its excellent maven integration.

When there is a "mvn glassfish:run" goal that can start up and run just as fast I may consider it :-)

P.S. I enjoyed your presentation on Comet (with the ICEFaces guy) in Montreal a few months ago. Especially liked how you run the server off your iPhone :-)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.