Typesafe Activator

Play Akka Angular WebSocket

Play Akka Angular WebSocket

gigiigig
Source
September 26, 2013
play akka angular websocket

Illustrates how to create a WebSocket based application using Play in Scala,using Akka for manage WebSockets and AngularJS for update the view with data received from sockets.

How to get "Play Akka Angular WebSocket" on your computer

There are several ways to get this template.

Option 1: Choose play-akka-angular-websocket in the Typesafe Activator UI.

Already have Typesafe Activator (get it here)? Launch the UI then search for play-akka-angular-websocket in the list of templates.

Option 2: Download the play-akka-angular-websocket project as a zip archive

If you haven't installed Activator, you can get the code by downloading the template bundle for play-akka-angular-websocket.

  1. Download the Template Bundle for "Play Akka Angular WebSocket"
  2. Extract the downloaded zip file to your system
  3. The bundle includes a small bootstrap script that can start Activator. To start Typesafe Activator's UI:

    In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:

    Or from a command line:

     C:\Users\typesafe\play-akka-angular-websocket> activator ui 
    This will start Typesafe Activator and open this template in your browser.

Option 3: Create a play-akka-angular-websocket project from the command line

If you have Typesafe Activator, use its command line mode to create a new project from this template. Type activator new PROJECTNAME play-akka-angular-websocket on the command line.

Option 4: View the template source

The creator of this template maintains it at https://github.com/gigiigig/play-akka-angular-websocket.

Option 5: Preview the tutorial below

We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.

Preview the tutorial

Quick Overview

This project shows how to use Play2, Akka and AngularJS for create a simple WebSocket based Timer application.

The basic idea is to create a sort of roundtrip from the user action to the response, using ajax calls for all the http requests and a single WebSocket for all the responses. This is just an example, in a real world application we need some more hacks to handle an high users number, and of course we need to write tests.

The Idea

When the user open the application, a WebSocket is associated with the userid, in that case,because this is an example, if user doesn't have an userId in session it is generated automatically and saved in session. (In Play the session is just a cookie, and there are some helpers to manage it)

When user interact with the application clicking on one of the buttons, through Angular an ajax call is sended to the server. The ajax call is handled by a Play Action, this Action instead of send the result of the computation directly to te client, send a message to the TimerActor and close straightaway the http request with an Ok response. TimerActor handle the message, run its logic and send the operation response to the user through the associated WebSocket

This approach has two advantages :

  • in case of long computation, we don't need to keep opened the http connection
  • if the user has more then one window opened, all windows receive the same data, and maintain the same state.

Play Controller

The Play controller has two main functionalities, the first is to create the WebSocket, and the second is to send the right message to the actor when an ajax call come in

To create the socket, inside the withAuthWS method we call the function WebSocket.async[JsValue] () which take a Future[(Iteratee[JsValue, Unit], Enumerator[JsValue])]. This is generated from :

(Iteratee.ignore[JsValue] mapDone {
_ =>
  timerActor ! SocketClosed(userId)
}, enumerator.asInstanceOf[Enumerator[JsValue]])
The Itaretee ignore the input, and when the client close the connection send to the actor the message SocketClosed. The enumerator instead, is received from the TimerActor using the ask pattern :
(timerActor ? StartSocket(userId)) map {
enumerator =>
}

The two ajax handler Actions are trivial :


def start = withAuth {
userId => implicit request =>
  timerActor ! Start(userId)    
  Ok("")
}
withAuth provide a basic authentication, and then the function send a message to the actor and send the Ok("") response to the client and close the http connection

The Actor

The actor manage the creation of channels and the one to one relation between channels and users, and manage also the timer scheduler, increase the timer for active users and send the updated data to users through the sockets, writing inside the channels.

case StartSocket(userId) => When the message StartSocket come in, the actor get or create the channel for the current user

webSockets.get(userId) getOrElse {
  val broadcast: (Enumerator[JsValue], Channel[JsValue]) = Concurrent.broadcast[JsValue]
  UserChannel(userId, 0, broadcast._1, broadcast._2)
}
The main method here is Concurrent.broadcast[JsValue] which create a tuple (Enumerator[JsValue], Channel[JsValue]). This tuple is composed by an enumerator and the related channel, when we'll write inside the channel through channel.push() method, the message is enumerated from the enumerator. So the as last operation for the message StartSocket, the actor send back to the sender, the AppController in that case, this enumerator which is user in the controller for create the socket

The second interesting part in the actor is case UpdateTime() => The message UpdateTime is sended every second to the actor thanks the Akka helper context.system.scheduler.schedule(0 second, 1 second, self, UpdateTime()) When this message come in, the actor update the time for every user in the list usersTimes, and write in the channel the current time webSockets.get(userId).get.channel push Json.toJson(json) and this message will be sended to the client within the socket.

Pretty trivial are the Start and Stop cases, the Stop case after removed the user from the usersTimes list, send also a message to the client with timer 0, for reset the timer label.

Angular

The Angular controller is simple

$scope.start = ->
  $http.get(jsRoutes.controllers.AppController.start().url).success( -> )

$scope.stop = ->
  $http.get(jsRoutes.controllers.AppController.stop().url).success( -> )
Start and stop menthods send a get to the Play action, using Play Js routes to create the right url.

When the controller is initialized, it create and connect a WebSocket

startWS = ->
  wsUrl = jsRoutes.controllers.AppController.indexWS().webSocketURL()

  $scope.socket = new WebSocket(wsUrl)
  $scope.socket.onmessage = (msg) ->
    $scope.$apply( ->
      console.log "received : #{msg}"
      $scope.time = JSON.parse(msg.data).data      
    )
and every time a message come in, it write its content inside the controller model, and the view is automatically updated from Angular.

Considerations

Because we used WebSocket only for the server to client comunication, we can replace that with Server Sent Event and Comet polling without problems thanks to Play api.

The actor which manage sockets and timers is just a trivial example, in a real project with thousands of users, we need to defer some computations to other actors.

comments powered by Disqus