Kotan Code 枯淡コード

In search of simple, elegant code

Menu Close

Using WebSockets with Play Framework 2.1.0

As odd as this may seem to some of you, I have been creating an enterprise web application using the Play Framework and the full typesafe stack, including Scala and Akka. It doesn’t surprise me, however, as the experience I have been having with this stack is quite possibly one of the most productive experiences I’ve had with any web dev stack, open source or commercial.

To get right to the chase – this web application allows multiple users to edit the same entity (which I am calling documents for now) at the same time. I’ve already got the optimistic concurrency thing taken care of (e.g. you can’t commit your changes over top of someone else’s changes). What I wanted to make things even cooler was to push a “toast” (a fade-in-fade-out notification dialog) to the user if they are currently looking at a page that was just modified by someone else.

The solution? WebSockets. I decided I would create a news feed of edits and then each page can decide if it wants to use that socket to be made aware of notifications. The JavaScript on that page decides if the notification is relevant (e.g. the edit news item is for that current document). To do this, I adapted the Play Framework sample websockets-chat to serve as a document edit news feed. Users join the feed when the page decides it needs that feed (for push or pull or both), and they quit when the page goes away (if you navigate away from a page that had a JavaScript-initiated websocket connection, the connection dies and the chatroom-based code considers that a Quit).

So, here’s the code for my WebSocket controller, which will control all of the feeds for my site:

object WebSockets extends Controller {
    def editFeed(user:String) = WebSocket.async[JsValue] { request =>

And here’s the code for the ChangeFeed actor class and it’s companion object. This code should look ridiculously familiar, since it’s basically the chatroom example. All I’ve really done is replace the Talk case class with my PostUpdate case class, which contains information about the type of document being updated and the document name. This information is enough for the individual pages to decide whether they need to display these push notifications.

// imports cut for brevity..
object ChangeFeed {
   implicit val timeout = Timeout(1 second)

   lazy val default = {
     val feedActor = Akka.system.actorOf(Props[ChangeFeed])
     // -- this is where the chatroom robot used to get initialized...

   def join(username:String): scala.concurrent.Future[Iteratee[JsValue,_],Enumerator[JsValue])] = {
   ( default ? Join(username)).map {
     case Connected(enumerator) =>
       val iteratee = Iteratee.foreach[JsValue] { event =>
          default ! PostUpdate(username, (event \ "docType").as[String], (event \ "docName").as[String])
       }.mapDone { _ =>
          default ! Quit(username) // this quits the channel when the web page goes away
       (iteratee, enumerator)
    case CannotConnect(error) => // .. handle error (snipped for brevity)

// The chatroom only allows one username per socket, on a website,
// my users could have multiple tabs open to the same site, so I let them
// open new sockets even if the username is the same.
class ChangeFeed extends Actor {
    val members = Set.empty[String]
    val (feedEnumerator, feedChannel) = Concurrent.broadcast[JsValue]

   def receive = {
     case Join(username) => {
         // yes, I know there's a more idiomatic scala way to do this
         if (!members.contains(username)) {
             members = members + username
         sender ! Connected(feedEnumerator)
         self ! NotifyJoin(username)
     case NotifyJoin(username) => {
        notifyAll("join", username, "", "")
     case PostUpdate(username, docType, docName) => {
        notifyAll("update", username, docType, docName)
     case Quit(username) => {
        members = members - username
        notifyAll("quit", username, "", "")

   def notifyAll(kind: String, user:String, docType: String, docName: String) {
     val msg = JsObject(
            "kind" -> JsString(kind),
            "user" -> JsString(user),
            "docType" -> JsString(docType),
            "docName" -> JsString(docName),
            "members" -> JsArray(members.toList.map(JsString))

// case classes snipped for brevity

Once I’ve got a controller that can ask an Actor for a socket and dispense it accordingly, then the only thing left for me to do is create some client-side JavaScript that accesses my web socket and there are plenty of examples of doing that on the web. The one piece of this that makes things even nicer in Play is that I can automatically get the websocket URL from a controller route:

var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var feedSocket = new WS(@"routes.WebSockets.editFeed.webSocketURL()")

This automatically gives me a URL that looks like ws://server/websockets/editFeed.

All I can say is that so far, using Play to build an enterprise web application has been an absolute joy and, having been able to create this websocket-based “edit feed” infrastructure in just a few hours was absolutely amazing.