Skip to content

Using WebSockets with Play Framework 2.1.0

by Kevin Hoffman on March 21st, 2013

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 =>
        ChangeFeed.join(user)
    }
}

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...
     feedActor
   }

   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(
         Seq(
            "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.

From → Technology

  • mandubian

    Cool post!
    Just one very slight remark being Play Json high priest, you can write your Json as following ;)

    val msg = Json.obj(
    “kind” -> kind,
    “user” -> user,
    “docType” -> docType,
    “docName” -> docName,
    “members” -> members
    )

  • Kevin

    Thanks for the tip .. As I said, I copied a lot of code from the sample, so I haven’t run it through the refactor mill yet.

    • mandubian

      No pb, this was just to notify it! Thanks for article!

  • jroper

    Great post! This can also serve as a great example to people that want to use actors with websockets, since for this sort of use case, actors are a very well suited abstraction to use with websockets. It’s great to see examples of how people are doing this, we hope to pull some of these patterns into Play itself to make things even easier.

    Also, a small correction, line 19 I think should be (iteratee, enumerator).

    • Kevin

      You’re right about line 19, I fixed it. I didn’t have copy/paste access to my code so it was all typed by hand … I’m lucky that was the only typo.

  • Meglio

    Doing the same even in baby scripting language like PHP is also under few hours, if not less then one hour. It is not something amazing about Play framework or Scala language.

    • Kevin

      I make no claim that Play/Scala/Akka is the easiest, best, or coolest way to do it. I don’t say that anywhere in the article. All I really do say is that I had need to use WebSockets, and I was already using Play for my enterprise application, and I was pleasantly surprised by the ease with which it was possible, especially using Akka actors which takes care of a ton of concurrency problems automatically.

      That said, I’ve used “baby scripting” frameworks, I’ve also done websockets in Node.js, and several Microsoft frameworks, and thus far, getting it done on the typesafe stack was the easiest and produced the cleanest code. I haven’t tried it in PHP or Python.

  • Abhishek

    Super Nice Post. I just did some tweat to run in a cloud environment and it worked well. :-)

    Atleast I udnerstood how it works.

    I tried in Jelastic http://jelastic.com/docs/websockets

    and there I didn;t find any issue at all.

    Thanks for sharing.