In my previous blog post I introduced the new Akka I/O and already kind of demonstrated that you can use it – in combination with spray – to build HTTP servers with Akka. This post is the first in a series which build on these core concepts and dive into the domain of modern and reactive web applications: We’ll create Gabbler, a simple push-enabled chat application using the latest and greatest tools like Scala, Akka, spray and AngularJS.
Setting up the project
As usual, we are going to use the amazing sbt build tool. I have heard rumors that some don’t like it, but anyway, here’s some interesting stuff from our build.sbt
:
scalaVersion := "2.10.2"
// TODO Remove as soon as spray-json is on Maven Cental
resolvers += "spray repo" at "http://repo.spray.io"
// TODO Remove as soon as spray is final and on Maven Cental
resolvers += "spray nightlies repo" at "http://nightlies.spray.io"
libraryDependencies ++= Dependencies.gabbler
And as I try to stick to the rules Josh laid down in his Effective sbt talk at Scala Days 2013 here is our project/Dependencies.scala
(rule #2: track dependencies in one place):
object Library {
// Versions
val akkaVersion = "2.2.0-RC2"
val logbackVersion = "1.0.13"
val sprayVersion = "1.2-20130628" // Compatible with Akka 2.2.0-RC2
val sprayJsonVersion = "1.2.5"
// Libraries
val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion
val akkaSlf4j = "com.typesafe.akka" %% "akka-slf4j" % akkaVersion
val logbackClassic = "ch.qos.logback" % "logback-classic" % logbackVersion
val sprayCan = "io.spray" % "spray-can" % sprayVersion
val sprayRouting = "io.spray" % "spray-routing" % sprayVersion
val sprayJson = "io.spray" %% "spray-json" % sprayJsonVersion
}
object Dependencies {
import Library._
val gabbler = List(
akkaActor,
akkaSlf4j,
logbackClassic,
sprayCan,
sprayRouting,
sprayJson
)
}
As you can see, we are still on the latest released version 2.10.2
of Scala, although I’d love to go for Scala 2.11
milestone whatsoever. But we all know that it can be a little tricky to find our library dependencies published against these latest development versions. As in my previous post we are using Akka 2.2
and spray 1.2
.
The user interface
Don’t expect anything fancy or stylish here! I’m not a web designer and never will be. Luckily there’s Twitter Bootstrap which prevents me from creating a complete mess. Enough talk, here’s the Gabbler UI:
As you can see, we are using the Bootstrap starter template. We add two columns: the left contains a textarea for new messages and a button to send them off, the right hosts the list of received messages.
We have to logon first. For sake of simplicity we can use any username-password combination with equal username and password. Then we can send messages which are pushed to all connected clients.
The AngularJS client
To build the client, we are using AngularJS, which seems to be one of the most promising JavaScript frameworks for reactive web applications out there. We won’t cover the details here, but just point out some of the most interesting aspects.
Let’s first take a look at the view portion of the client, which is a single HTML 5 page:
<!doctype html>
<html data-ng-app="gabbler">
…
<body data-ng-controller="GabblerCtrl">
…
<div class="container">
<div class="row">
<!--### The form for a new message ###-->
<div class="span6">
<h3>Gabble away</h3>
<form name="form" novalidate>
<fieldset>
<div>
<textarea id="text-input" rows="3" placeholder="Enter your message ..."
data-ng-model="message.text" required></textarea>
</div>
<div>
<button type="submit" class="btn btn-primary" data-ng-click="sendMessage()"
data-ng-disabled="form.$invalid">Gabble away</button>
</div>
</fieldset>
</form>
</div>
<!--### The list of messages ###-->
<div class="span6 messages">
<h3 data-ng-init="getMessages()">Gibble gabble</h3>
<div data-ng-repeat="msg in messages">
<div class="username">{{ msg.username }}</div>
<div class="text">{{ msg.text }}</div>
</div>
</div>
</div>
</div>
…
</body>
</html>
As you can see, this is a completely logic-free HTML 5 page which can be opened in any browser. All interactivity comes through AngularJS data-ng-
directives, which usually show up as attributes of HTML elements, and AngularJS expressions, embedded in {{ }}
, which are evaluated kind of like JavaScript expressions.
The controller part of the client, which gets connected to the view through the data-ng-controller
directive of the body
element, is a JavaScript object:
function GabblerCtrl($scope, Message) {
$scope.messages = [];
$scope.getMessages = function() {
var messages = Message.query(function() {
$scope.messages = messages.concat($scope.messages);
$scope.getMessages();
});
}
$scope.message = new Message({ "username": "" });
$scope.sendMessage = function() {
$scope.message.$save();
$scope.message = new Message({ "username": "" });
};
}
Our GabblerCtrl
defines the $scope.getMessages
function that queries the server for messages asynchronously and adds any new messages sent from the server to the messages
array. Notice that the callback for the query calls $scope.getMessages
again, hence the client is polling the server. We don’t use WebSocket here by purpose, because it will be illustrative to use Akka actors to implement long polling in a very elegant fashion on the server side. The controller also defines the $scope.sendMessage
function which sends a new message to the server and reinitializes – i.e. empties – the local message which is stored in the $scope.message
variable.
The model part of the client is made up from the $scope.messages
array and the $scope.message
object, which are synchronized with the view through AngularJS’s two-way data binding. Therefore the view gets updated anytime new messages are sent from the server, resulting in new entries in the list of messages in the right column. Also, when the user clicks the “Gabble away” button, reinitializing the $scope.message
variable leads to blanking out the textarea for new messages in the left column.
The server skeleton
With the client in place, let’s start looking at the RESTful HTTP server. In this post we just cover a server skeleton with incomplete implementations for accetping GET and POST requests.
In my previous post I have shown how to build a HTTP server with Akka I/O and spray-can. This time we add another spray module – spray-routing – which provides a high-level, very flexible routing DSL for elegantly defining RESTful web services.
First we have to create an application:
object GabblerServiceApp extends App {
val system = ActorSystem("gabbler-service-system")
val interface = GabblerSettings(system).interface
val port = GabblerSettings(system).port
system.actorOf(GabblerService.props(interface, port), "gabbler-service")
readLine(s"Hit ENTER to exit ...$newLine")
system.shutdown()
}
As usual we create an ActorSystem
and shut it down at the end, after hitting “ENTER”, which is a feature we probably don’t want to add to a mission critical high-available production system. We also get some configuration settings from the GabblerSettings
Akka extension which we use to create the GabblerService
top-level actor:
class GabblerService(interface: String, port: Int) extends HttpServiceActor with ActorLogging {
import GabblerService._
import SprayJsonSupport._
import context.dispatcher
IO(Http)(context.system) ! Http.Bind(self, interface, port)
…
As you can see, GabblerService
extends HttpServiceActor
from spray-routing which makes the routing DSL available. When the GabblerService
actor is created, we send a Http.Bind
message to the I/O manager for HTTP to register itself as listener for HTTP connections.
Other than in my previous post we don’t handle the various HTTP messages natively, but instead use the routing DSL to define the actor’s behavior. The core concepts of the routing DSL are routes and directives.
Routes, which are simply functions of type RequestContext => Unit
and can be nested or composed, are used to either complete a request or recejct or ignore it. Here is a very simple route that completes a request:
context => context.complete("Hello, world!")
Directives are small building blocks for arbitrarily complex routes which can have inner routes, transform or filter the incoming RequestContext
or extract values form it. spray-routing already provides many useful directives, e.g. for completing a request, matching against the HTTP verb or extracting path elements:
path("order" / IntNumber)(id =>
get(
complete(
"Received GET request for order " + id
)
) ~
put(
complete(
"Received PUT request for order " + id
)
)
)
Using this routing DSL we define the behavior of the GabblerService
actor:
…
override def receive: Receive =
runRoute(apiRoute ~ staticRoute)
def apiRoute: Route =
// format: OFF
authenticate(BasicAuth(UsernameEqualsPasswordAuthenticator, "Gabbler"))(user =>
path("api" / "messages")(
get(context =>
log.debug("User '{}' is asking for messages ...", user.username)
// TODO Complete this user's request later, when messages are available!
) ~
post(
entity(as[Message]) { message =>
complete {
log.debug("User '{}' has posted '{}'", user.username, message.text)
// TODO Dispatch message to all users!
StatusCodes.NoContent
}
}
)
)
)
// format: ON
def staticRoute: Route =
path("")(getFromResource("web/index.html")) ~ getFromResourceDirectory("web")
}
As you can see we define the receive
method in terms of the runRoute
method which gets a route composed of apiRoute
and staticRoute
.
Let’s first look at the pretty straightforward staticRoute
, which is a composite of two simple routes. The first is made up from the path
direcitve matching the empty path and the getFromResource
directive which completes a GET request with the given resource, which is web/index.html
in our case. The second route is constructed with the getFromResourceDirectory
direcitve and completes all GET requests with resources from the web
directory. Essentially, staticRoute
is used to serve Gabbler‘s one and only HTML file as well as all the CSS, JavaScript, image, etc. resources.
Now let’s take a look at apiRoute
. It extracts the authenticated user and then matches requests with path api/messages
. Within that, GET and POST requests are accepted.
We use basic authentication with a simple scheme: We accept any username-password combination with equal username and password:
object UsernameEqualsPasswordAuthenticator extends UserPassAuthenticator[BasicUserContext] {
override def apply(userPass: Option[UserPass]): Future[Option[BasicUserContext]] = {
val basicUserContext =
userPass flatMap {
case UserPass(username, password) if username == password => Some(BasicUserContext(username))
case _ => None
}
Promise.successful(basicUserContext).future
}
}
On receiving a GET request we simply log that the authenticated user is asking for messages. Notice that we dont’ complete the request here in order to support long polling, which will be introduced in the next post.
On receiving a POST request we extract the body into an instance of the Message
case class by using the entity
directive. This is supported by spray-json integration with the following Message
companions:
object Message extends DefaultJsonProtocol {
implicit val format = jsonFormat2(apply)
}
case class Message(username: String, text: String)
As you can see, marshalling and unmarshalling of case classes with spray is a breeze.
Within the entity
directive we complete the request with status code 204
“No Content” after logging that the authenticated user has posted a message. Again, we save an essential aspect – sending the message to all connected users – for the next post.
Conclusion
We have introduced Gabbler, a modern and reactive push-enabled chat application. Actually we have only shown Gabbler‘s client portion, built with AngularJS and a server skeleton built with Scala, Akka I/O and spray. In the next posts we will complete the server step by step towards a fully functional solution.
The complete source code is available on GitHub, the current state under the tag step-01
.