Using WebSockets on Heroku with Java and the Play Framework

Last Updated: 27 March 2014

java play websockets

Table of Contents

This tutorial demonstrates how to build a Java and Play Framework application that uses a WebSocket, deployed to Heroku.

Sample code for the demo application is available on GitHub. Edits and enhancements are welcome. Just fork the repository, make your changes and send us a pull request.

Prerequisites

Create WebSocket app

The sample application provides a simple example of using a WebSocket with Java and Play. You can clone the sample and follow along with the code as you read. If you’d rather write the app yourself you can add the sample code to a new Play app as you go.

Option 1. Clone the sample app

If you want to get going more quickly you can just clone the sample app:

$ git clone git@github.com:heroku-examples/play-websockets-sample.git
Cloning into 'play-websockets-sample'...
remote: Counting objects: 31, done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 31 (delta 0), reused 31 (delta 0)
Receiving objects: 100% (31/31), 38.33 KiB | 0 bytes/s, done.
Checking connectivity... done

Option 2. Create a new Play app

$ play new
       _
 _ __ | | __ _ _  _
| '_ \| |/ _' | || |
|  __/|_|\____|\__ /
|_|            |__/

play 2.2.0 built with Scala 2.10.2 (running Java 1.6.0_51), http://www.playframework.com

The new application will be created in /Users/jsimone/dev/supportApps/play22test
...

Choose an application name and Java as the language.

Functionality

The sample application renders a simple web page that will open a WebSocket to the backend. The server will send a payload containing the time over the WebSocket once a second. That time will be displayed on the page.

There are 3 important pieces to the interaction that takes place here: a controller method that returns a WebSocket object, a JavaScript method that opens that WebSocket, and an Akka actor that sends the payload across that WebSocket every second. Let’s explore each.

Controller

You can return a WebSocket from a Play controller method.

There is an example in /app/controllers/Application.java in the sample application:

public static WebSocket<String> pingWs() {
    return new WebSocket<String>() {
        public void onReady(WebSocket.In<String> in, WebSocket.Out<String> out) {
            final ActorRef pingActor = Akka.system().actorOf(Props.create(Pinger.class, in, out));
            final Cancellable cancellable = Akka.system().scheduler().schedule(Duration.create(1, SECONDS),
                                               Duration.create(1, SECONDS),
                                               pingActor,
                                               "Tick",
                                               Akka.system().dispatcher(),
                                               null
                                               );

            in.onClose(new Callback0() {
                @Override
                public void invoke() throws Throwable {
                    cancellable.cancel();
                }
            });
        }

    };
}

public static Result pingJs() {
    return ok(views.js.ping.render());
}

public static Result index() {
    return ok(index.render());
}

This method returns a new WebSocket object that has a String as its payload. In the WebSocket object we define the onReady() method to talk to an actor via the Akka scheduler. The work of sending data over the socket will occur in that actor. When the WebSocket is closed the callback registered on the input stream will be called.

The other methods will render our js and html templates.

We’ll also need a route to be set up for these methods in our routes file:

# Home page
GET     /                           controllers.Application.index()
GET     /pingWs                     controllers.Application.pingWs()
GET     /assets/javascripts/ping.js controllers.Application.pingJs()

Model

In the controller example you’ll notice that we pass around the in and out streams of the WebSocket. In our actor we’re able to read from and write to these streams just like any other IO stream. Here’s the code for the Pinger actor (/app/models/Pinger.java):

package models;

public class Pinger extends UntypedActor {
    WebSocket.In<String> in;
    WebSocket.Out<String> out;

    public Pinger(WebSocket.In<String> in, WebSocket.Out<String> out) {
        this.in = in;
        this.out = out;
    }

    @Override
    public void onReceive(Object message) {
        if (message.equals("Tick")) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Calendar cal = Calendar.getInstance();
            out.write(sdf.format(cal.getTime()));
        } else {
            unhandled(message);
        }
    }
}

You’ll notice that this actor counts on the schedule defined in the controller method to send it a “Tick” message every second. When that happens it sends the current date and time over the WebSocket.

Views

The final piece is the client code that will call the WebSocket. For this our sample application uses Scala templates to render JavaScript and HTML called /app/views/ping.scala.js and /app/views/index.scala.html respectively.

index.scala.html provides a div to display the data in and references the JavaScript:

@main("Welcome to Play") {

    <strong>Stats</strong><br>
    <div id="ping"></div>

   <script type="text/javascript" charset="utf-8" src="@routes.Application.pingJs()"></script>
}

ping.scala.js connects to the WebSocket and defines the receiveEvent method to populate the dates into the displayed div as they come across:

$(function() {
    var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
    var dateSocket = new WS("@routes.Application.pingWs().webSocketURL(request)")

    var receiveEvent = function(event) {
        $("#ping").html("Last ping: "+event.data);
    }

    dateSocket.onmessage = receiveEvent
})

Deploy

It’s time to deploy your app to Heroku. If you haven’t done so already put your application into a git repository:

$ git init
$ git add .
$ git commit -m "Ready to deploy"

Create the Heroku app to deploy to:

$ heroku create
Creating high-lightning-129... done, stack is cedar
http://high-lightning-129.herokuapp.com/ | git@heroku.com:high-lightning-129.git
Git remote heroku added

While in beta, WebSocket functionality must be enabled via the Heroku Labs:

$ heroku labs:enable websockets
Enabling websockets for morning-taiga-2382... done
WARNING: This feature is experimental and may change or be removed without notice.
For more information see: https://devcenter.heroku.com/articles/heroku-labs-websockets

Deploy your code with git push.

$ git push heroku master
Counting objects: 31, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (24/24), done.
Writing objects: 100% (31/31), 38.33 KiB | 0 bytes/s, done.
Total 31 (delta 0), reused 0 (delta 0)

-----> Play 2.x - Java app detected
-----> Installing OpenJDK 1.6...done
-----> Building app with sbt
-----> Running: sbt clean compile stage
       Getting org.scala-sbt sbt 0.13.0 ...
       downloading http://s3pository.heroku.com/ivy-typesafe-releases/org.scala-sbt/sbt/0.13.0/jars/sbt.jar ...
  [SUCCESSFUL ] org.scala-sbt#sbt;0.13.0!sbt.jar (416ms)
...
       [info] Done packaging.
       [success] Total time: 7 s, completed Sep 29, 2013 7:51:08 PM
-----> Dropping ivy cache from the slug
-----> Discovering process types
       Procfile declares types -> web

-----> Compiled slug size: 117.3MB
-----> Launching... done, v6
       http://still-hamlet-4310.herokuapp.com deployed to Heroku

Congratulations! Your web app should now be up and running on Heroku. Visit the application to see it in action:

$ heroku open

Scaling

This app demonstrates simple usage of a WebSocket. For production use, and any app that requires more than a single web dyno, please read about building a scalable app with WebSockets.

References