Websockets in Play

Posted: April 25, 2011 in Play Framework
Tags: ,

It has been nearly 4 months since I released the first beta of the Play Framework eBook, quickly followed by the final release accompanied with the paperback edition. Comments and feedback have been fantastic, so thank you all!

I released the book fully updated to the 1.1 version of Play, and now 4 months later, Play 1.2 has been released with a host of new features. As a quick snapshot these features include

  • Dependency Management – using Apache Ivy
  • Evolutions – for tracking and organising changes to your database schemas
  • H2 in memory database – replaces the older HSQLDB, and comes with a web console accessible using the URL /@db
  • Updates to the TestRunner
  • Plus many bug fixes and small enhancements to the Play codebase

All of these features sound absolutely great, but there is one set of features that I have been really looking forward to, and that is WebSockets!

Using WebSockets
So, why so exciting? Well quite simply put, a few years ago, in my spare time I used to write Java Swing based multi-player turn based games. Usually they were clones of popular board games, but they had a real problem! No-one really enjoyed downloading and installing a game. They wanted it on-demand, in a browser, easily accessible. But Web programming did not really lend itself well to this type of world. Play made it very easy to come close using AJAX and its long-polling method (discussed in Chapter 12) of suspending the HTTP request until there is an update, but for me I was eager for Websockets.

I finally got round to playing with Websockets this weekend and I must say, I am impressed. The Play Dev guys have done a great job with this. The documentation is not fantastically verbose, so this post is an attempt to make life a little easier for anyone wanting to play with Websockets using my favourite framework!

What do you need
Quite simply, you need Play 1.2 and a web browser that support websockets. So, switch off IE (even if you have IE9!), and use Chrome. If you have Firefox or Opera, the websocket protocol has been disabled due to some security issues, which is disappointing, but these will be cleared up at some point, so for now I will keep learning and playing with Websockets as they will be a great competitive advantage later.

Let’s try it out
Unlike the Long Polling method, which would check the database for changes to the model at predefined intervals, the idea of a websocket is to be always open, waiting for an event, and then broadcasting that event to everyone who needs to know. Therefore, the way to achieve this in Play is to have a Stateful object residing on the server side. I know this breaks a lot of the stateless, RESTful ideals of Play, but it is the only way to make this work. So, lets start with out Stateful Model.

Create a new application called websocket.

play new websocket

Now, create a new file called StatefulModel.java in the app/models directory and add the following code.

package models;

import play.libs.F;

public class StatefulModel {
   public static StatefulModel instance = new StatefulModel();
   public final F.EventStream event = new F.EventStream();

   private StatefulModel() { }
}

So, a very simple class. It is has a private constructor and a single static instance variable to enforce the Singleton pattern, meaning only a single instance of this model can exist on the server. The only other attribute is an EventStream object. This is an important piece. The EventStream is the core of the WebSockets implementation in Play. It allows events to be published, which notifies any waiting listeners that an event has been published. In this example, we are using a standard EventStream, which just gives access to the current event. Play also provides an ArchivedEventStream (check out the chat sample application to see it in action), which gives access to all the archived messages as well. To explain this EventStream concept in more detail, lets take a look at the Controller.

Open the Application.java in the app/controllers directory, and add the following code.

package controllers;

import play.mvc.*;
import models.*;

public class Application extends Controller {

   public static void index() {
      render();
   }

   public static class WebSocket extends WebSocketController {

      public static void listen() {
         while(inbound.isOpen()) {
            String event = await(StatefulModel.instance.event.nextEvent());
            outbound.send(event);
         }
      }
   }
}

So, not a lot going on here either. The index action will simply render the index page, and then we have a listen action inside a WebSocket static class. The WebSocket static class implements a new type of controller introduced in Play 1.2 called WebSocketController. The difference between a WebSocketController and a standard Controller is that it does away with the request/response model, and in its place has an inbound and outbound model. Here, the code simply loops while the inbound is open (which means the browser is still open, and the web socket is still connected). We then wait for a new event from our StatefulModel’s EventStream, by calling nextEvent.

The await function replaces the suspend function in play1.1. The await function will suspend the http request, freeing up the play framework to continue to process requests, until a new event is added to our Stateful Model, at which point the code will continue processing from the point where it left off. This is another important change in Play1.2. It does not call the method from the beginning again, it carries on where it left off, making the code more readable.
Once the code continues it sends the data from the event to the outbound, which sends the event back to the web browser.

So, how does the browser deal with it? Well, it is again really simple.
Open views/application/index.html and add the following code.

#{extends 'main.html' /}
#{set title:'Home' /}

<div id="socketout"></div>

<script type="text/javascript">
    // Create a socket
    var socket = new WebSocket('@@{Application.WebSocket.listen}')

    // Message received on the socket
    socket.onmessage = function(event) {
        $('#socketout').append(event.data+"<br />");
    }
</script>

So, all we have here is a DIV tag where we will output our Web Socket events, and a bit of javascript that, a) creates the websocket connection, and b) deals with the on message event by appending the data to the DIV.

One final piece before we start to add events to our websocket, is the routes file. Unlike the normal Catch-All request, this does not work, as the catch all works on Controller.action name, and we have also a static class to contend with as well. Therefore, for our routing to work, we also need to add a route. Open the routes file and add the following route next to the homepage

WS      /socket                                 Application.WebSocket.listen

Note, the WS as the HTTP Request type, to describe a WebSocket request. The rest of the route is accessed as you would expect any other route to be configured.

That is our websocket implementation up and running. The final part however is to start adding some events. If we started our application now, we would just see a blank page. Our websocket would be open, but there would be no data coming back to us from the server. So, lets create an asynchronous job to start putting some messages on the EventStream.

Create a new directory called app/jobs, and then create a file called Startup.java. Add the following code.

package job;

import play.jobs.*;
import models.StatefulModel;

@OnApplicationStart(async = true)
public class Startup extends Job {
   public void doJob() throws InterruptedException {
      int i = 0;

      while (true) {
         i++;
         Thread.sleep(1000);
         StatefulModel.instance.event.publish("On step " + i);
      }
   }
}

This is a simple startup job, that has been set to run asynchronously. It simply loops forever (or until the server is stopped) and on each iteration, it sleeps for 1 second, and then publishes an event to our StatefulModel’s EventStream with a String saying “On Step ” followed by the count. This will continue until the server is stopped.

To get this code to execute, we needed to specify the @OnApplicationStart was to run in async mode. Without this setting, the Bootstrap job would be configured so that it MUST complete before the first request can be executed. This makes sense, as if we are using the Bootstrap job to read runtime settings for our application, we would not want it to start until it had done all of its work. In this case however, we just want it to run in parallel.

If you now start the server with

play run websocket

Now point your browser at localhost:9000, you should see your browser start counting up from 1. But what if you open another web browser? You will see that it will start counting in time with the other browser. The event is effectively broadcasted to all listening browsers. Obviously this is by design and you could tailor the way in which you build your application, but the concept is a powerful one.

And before I sign off, another big thank you to the Play developers. This request for Websockets came directly from the very active Play community, and the Play Dev guys implemented superbly whilst staying true to the framework. A fine piece of software engineering.

About these ads
Comments
  1. opensas says:

    as usual, an excellent article… should be published soon at play planet and dzone!!!

  2. Sakuraba says:

    Awesome demonstration of the new capabilities. Thank you!

  3. Tom says:

    Thank you for this article. I must try these new playframework capabilities.

  4. Ebot Tabi says:

    Hi Wayne
    thanks for the tips on websockets on play, i really love play even though i am new to it but it speed up my development and migration from a php to java app up to 60%, i am looking forward with more stuffs on it

  5. lalit says:

    i am not able to see the message in browser,i followed exactly what u have mentioned but of no progress,please guide.
    i am using chrome 14.0.835.186,

    in request header it is showing

    Request URL:ws://localhost:9000/socket
    GET ws://localhost:9000/socket HTTP/1.1

    Sec-WebSocket-Origin: http://localhost:9000

    Connection: Upgrade

    Host: localhost:9000

    Sec-WebSocket-Key: sbB1Mkkst8O7V7hIp5XS6g==

    Upgrade: websocket

    Sec-WebSocket-Version: 8

  6. john says:

    this i tried,even i tried the play example for chat,websocket is not working in my chrome latest browser,socket gets opened,but on server side nothing happens in controller.

  7. Chris Alvarez says:

    So how would Play! support something like Socket IO?

  8. Chrono says:

    Doesn’t work for me, I’m getting a compilation error: “HTTP Verb expected”.
    It seems WS in the routes file is not accepted, can’t find any solution for it.

    Ubuntu 12.04
    Firefox 16.02
    Chromium 20.0.1132.47
    Play 2.0.4

  9. Vortilion says:

    I would love to see the ePlay-Tutorial chapter 12 updated to Websockets instead of Long polling. Don’t really understand how to do this myself… :-(

  10. Heya i am for the first time here. I came across this board and
    I find It really useful & it helped me out much.

    I hope to give something back and help others like you helped me.

  11. Ben-Admin says:

    Hi,
    what about the case, when websocket is not supported by browser or by proxy?
    Is there automatic fallback to long pooling in Play!?
    Thanks.

    • codemwnci says:

      Prior to websockets, Play was able to do long polling using the await() method inside a standard controller. This was able to wait for either a future, or a predefined time, and one waking would be able to continue execution. Play does not support automatic fallback like socket.io unfortunately.

  12. Excellent post. I used to be checking continuously this blog and I’m inspired! Extremely helpful information specifically the ultimate section :) I take care of such info much. I used to be seeking this certain info for a very lengthy time. Thank you and good luck.

  13. Mahmoud says:

    Nice work … but i have an issue , i am using glassfish server when i deploy the app on glassfish server the socket is not open . and i configured the glassfish to enable websockets … any help please

    • codemwnci says:

      When deployed as a WAR file, websockets is not enabled. It only works when using the embedded netty server that comes with Play. There are a number of features that does not work when deployed as a WAR file, of which Websockets is one of them.

  14. José Gonzáles says:

    sorry, I’m new to play framework, I’m using play-1.2.4, I have followed exactly the steps showed in this post except here:
    String event = await(StatefulModel.instance.event.nextEvent());
    I used
    String event = await(StatefulModel.instance.event.nextEvent()).toString();
    (I don’t think that is my problem)
    I’m getting this:
    WebSocket connection to ‘ws://localhost:9000/socket’ failed: Error during WebSocket handshake: Unexpected response code: 426
    I’m using Chrome 33.0 and Windows 7 64.
    thanks, great post by the way

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s