Practical Scala

Make it work.

Realtime chat with Play & Angular

This blog will be about building practical stuff with Scala. I’m far from expert in any of those technologies. It’s more of a way for me to memorize something new I’m learning at the time. If you find it useful that will be great. Comments are always welcome.

Today we will build obligatory realtime chat application using Scala’s Play Framework, AngularJS and websockets starting from zero.

Roadmap:

  • Setup
  • Creating Play application from template
  • Frontend with AngularJS and Websockets
  • Connecting it to Play

Setup

Prerequisites:

After that is installed you can follow the steps listed in Play Framework guide.

Make sure the above tools are added to your $PATH.

Creating Play application from template

Next step is actually much easier and consists of single console command:

$ activator new my-play-chat play-scala

It will create complete Play Application from play-scala template in my-play-chat directory that you can run using commands listed in console output or simply with SBT.

$ cd my-play-chat
$ activator run

--- (Running the application, auto-reloading is enabled) ---

[info] p.c.s.NettyServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Ctrl+D to stop and go back to the console...)

You can now navigate to http://localhost:9000 and see your running application.

Play uses more or less usual MVC workflow:

  1. server gets a request at path /
  2. conf/routes file controls which controller method serves that path
  3. controller method in app/controllers decides how to reply to the request
  4. view templates in app/views provide the ways to render html from controller methods

Frontend with AngularJS and Websockets

We will build chat UI and client functionality with AngularJS, which is a Javascript MVW Framework from Google. Interaction with the server will be managed through websockets, which is a more convenient realtime alternative to ajax calls.

We will not dive deep into AngularJS here, which is a fascinating topic in itself, just use as little of it as we can to make our UI implementation clean.

Let’s first add a reference to AngularJS in app/views/main.scala.html:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js">
</script>

Make sure it comes before the reference to javascripts/hello.js.

Now let’s describe our javascript data model and UI interactions in public/javascripts/hello.js file:

angular.module("ChatApp", []).controller("ChatController", function($scope){
  // connect to websockets endpoint of our server
  var ws = new WebSocket("ws://localhost:9000/socket");

  // binding model for the UI
  var chat = this;
  chat.messages = [];
  chat.currentMessage = "";
  chat.username = "";

  // what happens when user enters message
  chat.sendMessage = function() {
    var text = chat.username + ": " + chat.currentMessage;
    chat.messages.push(text);
    chat.currentMessage = "";
    // send it to the server through websockets
    ws.send(text);
  };

  // what to do when we receive message from the webserver
  ws.onmessage = function(msg) {
    chat.messages.push(msg.data);
    $scope.$digest();
  };
});

That’s it. That’s the whole client-side logic.

Now we need to do is bind it to the UI with the magic of Angular in app/views/index.scala.html:

@(message: String)

@main("Welcome to Play") {
    <div ng-app="ChatApp">
        <div ng-controller="ChatController as chat">
            <form>
                <input type="text" placeholder="Enter name..."
                       ng-model="chat.username">
                <input type="text" placeholder="Enter message..."
                       ng-model="chat.currentMessage">
                <input type="submit" value="Send"
                       ng-click="chat.sendMessage()">
            </form>

            <ul>
                <li ng-repeat="msg in chat.messages track by $index">
                    {{msg}}
                </li>
            </ul>
        </div>
    </div>
}

ng-* attributes specify bindings to our angular controller specified in javascript above.

Everything should be more or less self-explanatory besides maybe a track by $index, which is just a workaround to allow duplicate elements in ng-repeat.

Connecting it to Play

Server-side functionality will be based on akka actors in context of Play application.

Entities of our domain will be represented as simultaneously existing actors that communicate through message passing.

Actors are objects implementing akka.actor.Actor and they are never created or operated on directly, but only through sending messages to corresponding ActorRef wrappers. That is part of what enables location transparency in actors.

Let’s define Chat actor and corresponding message protocol in the app/models/Chat.scala file:

package models

import akka.actor._

// our domain message protocol
case object Join
case object Leave
final case class ClientSentMessage(text: String)

// Chat actor
class Chat extends Actor {
  // initial message-handling behavior
  def receive = process(Set.empty)

  def process(subscribers: Set[ActorRef]): Receive = {
    case Join =>
      // replaces message-handling behavior by the new one
      context become process(subscribers + sender)

    case Leave =>
      context become process(subscribers - sender)

    case msg: ClientSentMessage =>
      // send messages to all subscribers except sender
      (subscribers - sender).foreach { _ ! msg }
  }
}

According to wikipedia:

An actor is a computational entity that, in response to a message it receives, can concurrently:
- send a finite number of messages to other actors;
- create a finite number of new actors;
- designate the behavior to be used for the next message it receives.

Sending a finite number of messages to other actors is handled by ! method on ActorRef.

We will not be spawning new actors inside of other actors for this example.

The behavior to be used for the next message is described by receive method and context.become calls.

Play provides a functionality to represent WebSocket connection as an actor. Let’s describe a /socket endpoint in our controller that will serve that role.

conf/routes

GET     /                           controllers.Application.index
GET     /socket                     controllers.Application.socket

Now we will describe how to create an actor that handles websocket connection:

app/controllers/Application.scala

package controllers

import javax.inject._
import akka.actor._
import play.api._
import play.api.mvc._
import play.api.Play.current

import models._

// injects Play's default ActorSystem into our controller
@Singleton
class Application @Inject()(actorSystem: ActorSystem) extends Controller {

  // creates actor of type chat described above
  val chat = actorSystem.actorOf(Props[Chat], "chat")

  /*
   Specifies how to wrap an out-actor that will represent
   WebSocket connection for a given request.
  */
  def socket = WebSocket.acceptWithActor[String, String] {
    (request: RequestHeader) =>
    (out: ActorRef) =>
    Props(new ClientActor(out, chat))
  }

  def index = Action {
    Ok(views.html.index("Hello."))
  }
}

Now for the last piece of a puzzle. Our ClientActor class that will represent communication between WebSocket actor and Chat “server”:

app/models/ClientActor.scala

package models

import akka.actor._

class ClientActor(out: ActorRef, chat: ActorRef) extends Actor {

  chat ! Join

  override def postStop() = chat ! Leave

  def receive = {
    // this handles messages from the websocket
    case text: String =>
      chat ! ClientSentMessage(text)

    case ClientSentMessage(text) =>
      out ! text
  }
}

That’s it. The whole application is done.

You can refresh the page in your browser, open several of them and see how each receives the messages sent from the other.

The whole code is available on github.

Diff between Play template and final implementation can be seen here.

It’s less than 100 added lines of HTML+JS+Scala code combined (excluding comments).