Play 2.0: Akka, Rest, Json and dependencies
The guys behind the Play framework have been hard at work at the new version Play 2.0. In Play 2.0 scala plays a much more important role, and especially the complete build process has been immensely improved. The only problem so far, I’ve encountered with Play 2.0 is the lack of good documentation. The guys are hard at work at updating the wiki, but its often still a lot of trial and error to get what you want. Note though, that often this isn’t just caused by Play, I also sometimes still struggle with the more exotic Scala constructs ;-)
In this article, I’ll give you an introduction into how you can accomplish some common tasks in Play 2.0 using Scala. More specifically I’ll show you how to create an application that:
- uses sbt based dependency management to configure external dependencies
- is edited in Eclipse (with the Scala-ide plugin) using the play eclipsify command
- provides a Rest API using Play’s routes
- uses Akka 2.0 (provided by the Play framework) to asynchronously call the database and generate Json (just because we can)
- convert Scala objects to Json using the Play provided Json functionality (based on jerkson)
I won’t show the database access using Querulous, if you want to know more about that look at this article. I’d like to convert the Querulous code to using Anorm. But since my last experiences with Anorm were, how do I put this, not convincingly positive, I’m saving that for a later day.
Creating an application with Play 2.0
Getting up and running with Play 2.0 is very easy and is well documented, so I won’t spent too much time on this. For complete instruction see the Play 2.0 Wiki. To get up and running, after you you have downloaded and extracted Play 2.0, take the following steps:
Execute the following command from the console:
$play new FirstStepsWithPlay20
This will create a new project, and show you something like the following output:
_ __ | | __ _ _ _| | | '_ \| |/ _' | || |_| | __/|_|\____|\__ (_) |_| |__/ play! 2.0-RC2, http://www.playframework.org The new application will be created in /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay20 What is the application name? > FirstStepsWithPlay20 Which template do you want to use for this new application? 1 - Create a simple Scala application 2 - Create a simple Java application 3 - Create an empty project > 1 OK, application FirstStepsWithPlay20 is created. Have fun!
You’ve now got an application you can run. Change to the just created directory and execute play run.
$ play run [info] Loading project definition from /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/project [info] Set current project to FirstStepsWithPlay2 (in build file:/Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/) --- (Running the application from SBT, auto-reloading is enabled) --- [info] play - Listening for HTTP on port 9000... (Server started, use Ctrl+D to stop and go back to the console...)
If you navigate to http://localhost:9000, you can see your first Play 2.0 application. And you’re done with the basic installation of Play 2.0.
Dependency management
I mentioned in the introduction that I didn’t start this project from scratch. I rewrote a Rest service I made with Play 1.2.4, Akka 1.x, JAX-RS and Json-Lift to the components provided by the Play 2.0 framework. Since dependency management changed between Play 1.2.4 and Play 2.0, I needed to configure my new project with the dependencies I needed. In Play 2.0 you do this in a file called build.scala, which you can find in the project folder in your project. After adding the dependencies from my previous project, this file looked like this:
import sbt._ import Keys._ import PlayProject._ object ApplicationBuild extends Build { val appName = "FirstStepsWithPlay2" val appVersion = "1.0-SNAPSHOT" val appDependencies = Seq( "com.twitter" % "querulous" % "2.6.5" , "net.liftweb" %% "lift-json" % "2.4" , "com.sun.jersey" % "jersey-server" % "1.4" , "com.sun.jersey" % "jersey-core" % "1.4" , "postgresql" % "postgresql" % "9.1-901.jdbc4" ) val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings( // Add extra resolver for the twitter resolvers += "Twitter repo" at "http://maven.twttr.com/" , resolvers += "DevJava repo" at "http://download.java.net/maven/2/" ) }
How to use this file is rather straightforward, once you’ve read the sbt documentation (http://code.google.com/p/simple-build-tool/wiki/LibraryManagement). Basically we define the libraries we want, using appDependencies, and we define some extra repositories where sbt should download its dependencies from (using resolvers). A nice thing to mention is that you can specify a %% when defining dependencies. This implies that we also want to search for a library that matches our version of scala. SBT looks at our current configured version and adds a qualifier for that version. This makes sure we get a version that works for our Scala version.
Like I mentioned, I wanted to replace most external libraries I used with functionality from Play 2.0. After removing the stuff I didn’t use anymore this file looks like this:
import sbt._ import Keys._ import PlayProject._ object ApplicationBuild extends Build { val appName = "FirstStepsWithPlay2" val appVersion = "1.0-SNAPSHOT" val appDependencies = Seq( "com.twitter" % "querulous" % "2.6.5" , "postgresql" % "postgresql" % "9.1-901.jdbc4" ) val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings( // Add extra resolver for the twitter resolvers += "Twitter repo" at "http://maven.twttr.com/" ) }
With the dependencies configured, I can configure this project for my IDE. Even though all my colleagues are big IntelliJ proponents, I’m still coming back to what I’m used to: Eclipse. So lets see what you need to do to get this project up and running in Eclipse.
Work from Eclipse
In my Eclipse version I’ve got the scala plugin installed, and the Play 2.0 framework nicely works together with this plugin. To get your project in eclipse all you have to do is run the following command: play eclipsify
jos@Joss-MacBook-Pro.local:~/dev/play-2.0-RC2/FirstStepsWithPlay2$ ../play eclipsify [info] Loading project definition from /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/project [info] Set current project to FirstStepsWithPlay2 (in build file:/Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/) [info] About to create Eclipse project files for your project(s). [info] Compiling 1 Scala source to /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/target/scala-2.9.1/classes... [info] Successfully created Eclipse project files for project(s): FirstStepsWithPlay2 jos@Joss-MacBook-Pro.local:~/dev/play-2.0-RC2/FirstStepsWithPlay2$
Now you can use “import project” from Eclipse, and you can edit your Play 2.0 / Scala project directly from Eclipse. Its possible to start the Play environment directly from Eclipse, but I haven’t used that. I just start the Play project from the command line, once, and all the changes I make in Eclipse are immediately visible. For those of you who’ve worked with Play longer, this is probably not so special anymore. For me, personally, I still am amazed by the productivity of this environment.
provides a Rest API using Play’s routes
In my previous Play project I used the jersey module to be able to use JAX-RS annotations to specify my Rest API. Since Play 2.0 contains a lot of breaking API changes and is pretty much a rewrite from the ground up, you can’t expect all the old modules to work. This was also the case for the Jersey module. I did dive into the code of this module, to see if the changes were trivial, but since I couldn’t find any documentation on how to create a plugin for Play 2.0 that allows you to interact with the route processing, I decided to just switch to the way Play 2.0 does Rest. And using the “routes” file, it was very easy to connect the (just) two operations I exposed to a simple controller:
# Routes # This file defines all application routes (Higher priority routes first) # ~~~~ GET /resources/rest/geo/list controllers.Application.processGetAllRequest GET /resources/rest/geo/:id controllers.Application.processGetSingleRequest(id:String)
The corresponding controller looks like this:
package controllers import akkawebtemplate.GeoJsonService import play.api.mvc.Action import play.api.mvc.Controller object Application extends Controller { val service = new GeoJsonService() def processGetSingleRequest(code: String) = Action { val result = service.processGetSingleRequest(code) Ok(result).as("application/json") } def processGetAllRequest() = Action { val result = service.processGetAllRequest; Ok(result).as("application/json"); } }
As you can see I’ve just created to very simple, basic actions. Haven’t look at fault and exception handling yet, but the Rest API offered by Play really makes using additional Rest framework unnecessary. Thats the first of the frameworks. The next part of my original application that needed to change was the Akka code. Play 2.0 includes the latest version of the Akka library (2.0-RC1). Since my original Akka code was written against 1.2.4, there were a lot of conflicts. Updating the original code, wasn’t so easy though.
Using Akka 2.0
I won’t dive into all the problems I had with Akka 2.0. Biggest problem was the very crappy documentation on the Play Wiki and the crappy documentation on the Akka website my crappy skills at locating the correct information in the Akka documentation. Together with me only using Akka for about three or four months, doesn’t make it the best combination. After a couple of frustation hours though, I just removed all the existing Akka code, and started from scratch. 20 minutes later I got everything working with Akka 2, and using the master configuration from Play. In the next listing you can see the corresponding code (I’ve intentionally left the imports, since in a lot of examples you can find, they are omitted, which makes an easy job, that much harder)
import akka.actor.actorRef2Scala import akka.actor.Actor import akka.actor.Props import akka.dispatch.Await import akka.pattern.ask import akka.util.duration.intToDurationInt import akka.util.Timeout import model.GeoRecord import play.libs.Akka import resources.commands.Command import resources.commands.FULL import resources.commands.SINGLE import resources.Database /** * This actor is responsible for returning JSON objects from the database. It uses querulous to * query the database and parses the result into the GeoRecord class. */ class JsonActor extends Actor { /** * Based on the type recieved we determine what command to execute, most case classes * can be executed using the normal two steps. Execute a query, convert result to * a set of json data and return this result. */ def receive = { // when we receive a Command we process it and return the result case some: Command => { // execute the query from the FULL command and process the results using the // processRows function var records:Seq[GeoRecord] = null; // if the match parameter is null we do the normal query, if not we pass in a set of varargs some.parameters match { case null => records = Database.getQueryEvaluator.select(some.query) {some.processRows} case _ => records = Database.getQueryEvaluator.select(some.query, some.parameters:_*) {some.processRows} } // return the result as a json string sender ! some.toJson(records) } case _ => sender ! null } } /** * Handle the specified path. This rest service delegates the functionality to a specific actor * and if the result from this actor isn't null return the result */ class GeoJsonService { def processGetSingleRequest(code: String) = { val command = SINGLE(); command.parameters = List(code); runCommand(command); } /** * Operation that handles the list REST command. This creates a command * that forwards to the actor to be executed. */ def processGetAllRequest:String = { runCommand(FULL()); } /** * Function that runs a command on one of the actors and sets the response */ private def runCommand(command: Command):String = { // get the actor val actor = Akka.system.actorOf(Props[JsonActor]) implicit val timeout = Timeout(5 seconds) val result = Await.result(actor ? command, timeout.duration).asInstanceOf[String] // return result as String result } }
A lot of code, but I wanted to show you the actor definition and how to use them. Summarizing, the Akka 2.0 code you need to use, to execute a request/reply pattern with Akka is this:
private def runCommand(command: Command):String = { // get the actor val actor = Akka.system.actorOf(Props[JsonActor]) implicit val timeout = Timeout(5 seconds) val result = Await.result(actor ? command, timeout.duration).asInstanceOf[String] // return result as String result }
This uses the global Akka configuration to retrieve an actor of the required type. We then send a command to the actor, and are returned a Future, on which we wait 5 seconds for a result, which we cast to a String. This Future waits for our Actor to send a reply. This is done in the actor itself:
sender ! some.toJson(records)
With Akka replaced I finally got a working system again. When looking through the documentation on Play 2.0 I noticed that they provided their own Json library, starting from 2.0. Since I used Json-Lift in the previous version, I thought it would be a nice exercise to move this code to the Json library, named Jerkson, provided by Play.
Moving to Jerkson
The move to the new library was a fairly easy one. Both Lift-Json and Jerkson use pretty much the same concept of building Json objects. In the old version I didn’t use any automatic marshalling (since I had to comply with the jsongeo format) so in this version I also did the marshalling manually. In the next listing you can see the old version and the new version together, As you can see the concepts used in both are pretty much the same.
#New version using jerkson val jsonstring = JsObject( List("type" -> JsString("featureCollection"), "features" -> JsArray( records.map(r => (JsObject(List( "type" -> JsString("Feature"), "gm_naam" -> JsString(r.name), "geometry" -> Json.parse(r.geojson), "properties" -> ({ var toAdd = List[(String, play.api.libs.json.JsValue)]() r.properties.foreach(entry => (toAdd ::= entry._1 -> JsString(entry._2))) JsObject(toAdd) }))))) .toList))) #Old version using Lift-Json val json = ("type" -> "featureCollection") ~ ("features" -> records.map(r => (("type" -> "Feature") ~ ("gm_naam" -> r.name) ~ ("geometry" -> parse(r.geojson)) ~ ("properties" -> ({ // create an empty object var obj = JNothing(0) // iterate over the properties r.properties.foreach(entry => ( // add each property to the object, the reason // we do this is, that else it results in an // arraylist, not a list of seperate properties obj = concat(obj, JField(entry._1, entry._2)))) obj })))))
And after all this, I have exactly the same as I already had. But now with Play 2.0 and not using any external libraries (except Querulous). So far my experiences with Play 2.0 have been very positive. The lack of good concrete examples and documentation can be annoying sometimes, but is understandable. They do provide a couple of extensive examples in their distribution, but nothing that matched my use cases. So hats off to the guys who are responsible for Play 2.0. What I’ve seen so far, great and comprehensive framework, lots of functionality and a great environment to program scala in. In the next couple of weeks I’ll see if I can get enough courage up to start with Anorm and I’ll look at what Play has to offer on the client side. So far I’ve looked at LESS which I really like, so I’ve got my hopes up for their template solution ;-)
Reference: Play 2.0: Akka, Rest, Json and dependencies from our JCG partner Jos Dirksen at the Smart Java blog.
coolllllllllllllllllllll nice work