Introduction to Play Framework

When we build webapps, we can think that all of them can be constructed with Spring framework and its related projects. It's right, but which programmer is satisfied by knowing only one framework or only one programming language ? It's why we'll explore here another way to deploying web applications, the deploying with Play Framework.

In this article we'll discover the basics of this framework. The first part will be dedicated to the presentation of the developed project. At the second time we'll set up our first Play project. At the end, we'll make some basic operations, as defining new views or routes.

Coding Play by example

We'll learn Play Framework by writing a classic e-commerce store. Through its features we'll discover Play features:

The project is available on my Github and is developed with 2.3 version of Play.

Set up Play 2.3 project

If you think that Play is "another servlet container-based framework", you're wrong. Play should be thought as "stateless framework" - even if it can be compiled and moved into servlet container as Tomcat. Thanks to this stateless character, we can change the code at runtime, so make our development time more "reactive" (without no rebuild needed at every written change etc.). So, how to setup this tool ?

To answer this question, we must take a look at Play 2.3 installing page. As you can see, all operations (setup, deployment) are done by some tool called Activator. This tool helps to start the work with all Typesafe technologies, as Scala or Play Framework. If you're familiar with Maven, you can consider Activator as another version of it. Yes, this tool can as well compile one project as test or initialize new application based on provided artifact. It also helps in dependency management by manipulating project dependency files. Let's download Activator and open it. If you look carefully in source code and add some output, you'll see that executed command is in the reality the execution of sbt (Simple Build Tool):

/usr/lib/jvm/java-7-openjdk-amd64/bin/java -Dactivator.home=/home/bartosz/play/activator-1.2.3-minimal -Xms1024m -Xmx1024m -XX:PermSize=64m -XX:MaxPermSize=256m -jar /home/bartosz/play/activator-1.2.3-minimal/activator-launch-1.2.3.jar

Before defining an sbt, we need to inspect activator-launch-1.2.3.jar. This archive contains Java classes used to project generation. We can find there some popular Java projects, as:
- Apache Ivy: it's dependency manager. Play uses it to manage dependencies used by the project. Dependencies list is specified in build.sbt file, generated in the root of every Play's project. This file contains not only the dependencies list, but also some basic information about the project, as its version or name (exactly as Maven's pom.xml file).
- Scala: this programming language provides very important element in Play's ecosystem. It's responsible for provide sbt, tool used to build the Scala and Java applications. sbt reads and executes build.sbt file, allowing the combine several .sbt files in building process. For example, in the default Play structure, they're 2 .sbt files: build.sbt in the root and plugins.sbt in /project directory. Both contain dependencies list. The result of building is saved in target directory. Once again, the workflow and event directories (/target) are similar to Maven.
- xsbt: this package handles sbt execution.

To resume the Activator's role, we can consider it as a main element to compile, launch, test and manage project dependencies. Before make these operations, we need to setup our's Play project. And this is very simple and intuitive manipulation. First, you must to create project's directory. After you must invoke Activator's new method and answer to displayed questions. After that, you need to adapt the project to your IDE (Eclipse in our case). The last step consists on running created application. Let's see that on given commands:

# directory creation
bartosz@bartosz-K70ID:cd ~/workspace
bartosz@bartosz-K70ID:mkdir playApp
bartosz@bartosz-K70ID:cd playApp

# Activator's new - the name of our project will be testApp. Consequently, Play application will be stored in testApp directory
bartosz@bartosz-K70ID:~/workspace/playApp/testApp$ activator new

# Now we can move into project directory and transform it into Eclipse-managed project
bartosz@bartosz-K70ID:~/workspace/playApp/testApp$ cd testApp # directory generated after activator new
bartosz@bartosz-K70ID:~/workspace/playApp/testApp$ ./activator eclipse # will transform Play project into Eclipse artifact
[info] Loading project definition from /home/bartosz/workspace/playApp/testApp/project
[info] Set current project to testApp (in build file:/home/bartosz/workspace/playApp/testApp/)
[info] About to create Eclipse project files for your project(s).
[info] Updating {file:/home/bartosz/workspace/playApp/testApp/}root...
[info] Resolving jline#jline;2.11 ...
[info] downloading http://repo1.maven.org/maven2/org/ow2/asm/asm-analysis/4.1/asm-analysis-4.1.jar ...
[info] 	[SUCCESSFUL ] org.ow2.asm#asm-analysis;4.1!asm-analysis.jar (457ms)
[info] downloading http://repo1.maven.org/maven2/org/ow2/asm/asm-util/4.1/asm-util-4.1.jar ...
[info] 	[SUCCESSFUL ] org.ow2.asm#asm-util;4.1!asm-util.jar (438ms)
[info] Done updating.
[info] Compiling 4 Scala sources and 2 Java sources to /home/bartosz/workspace/playApp/testApp/target/scala-2.11/classes...
[info] 'compiler-interface' not yet compiled for Scala 2.11.1. Compiling...
[info]   Compilation completed in 48.497 s
[info] Successfully created Eclipse project files for project(s):
[info] testApp

# Everything looks good, so we can launch testApp and access it with http://localhost:9000
bartosz@bartosz-K70ID:~/workspace/playApp/testApp$ ./activator run
[info] Loading project definition from /home/bartosz/workspace/playApp/testApp/project
[info] Set current project to testApp (in build file:/home/bartosz/workspace/playApp/testApp/)

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

[info] play - 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...)

After these operations, Play project should be correctly imported as Eclipse project and available under http://localhost:9000. Note that you can change the port with -Dhttp.port=PORT parameter:

bartosz@bartosz-K70ID:~/workspace/playApp/testApp$ ./activator run -Dhttp.port=9001
[info] Loading project definition from /home/bartosz/workspace/playApp/testApp/project
[info] Updating {file:/home/bartosz/workspace/playApp/testApp/project/}testapp-build...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Set current project to testApp (in build file:/home/bartosz/workspace/playApp/testApp/)

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

[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9001

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

Basic operations in Play 2.3

The skeleton of our Play application was generated. Let's take a look at generated structure:

bartosz@bartosz-K70ID:~/workspace/playApp/testApp$ tree -L 2
.
β”œβ”€β”€ activator
β”œβ”€β”€ activator.bat
β”œβ”€β”€ activator-launch-1.2.3.jar
β”œβ”€β”€ app
β”‚Β Β  β”œβ”€β”€ assets
β”‚Β Β  β”œβ”€β”€ controllers
β”‚Β Β  └── views
β”œβ”€β”€ build.sbt
β”œβ”€β”€ conf
β”‚Β Β  β”œβ”€β”€ application.conf
β”‚Β Β  └── routes
β”œβ”€β”€ LICENSE
β”œβ”€β”€ logs
β”‚Β Β  └── application.log
β”œβ”€β”€ project
β”‚Β Β  β”œβ”€β”€ build.properties
β”‚Β Β  β”œβ”€β”€ plugins.sbt
β”‚Β Β  β”œβ”€β”€ project
β”‚Β Β  └── target
β”œβ”€β”€ public
β”‚Β Β  β”œβ”€β”€ images
β”‚Β Β  β”œβ”€β”€ javascripts
β”‚Β Β  └── stylesheets
β”œβ”€β”€ README
β”œβ”€β”€ target
β”‚Β Β  β”œβ”€β”€ native_libraries
β”‚Β Β  β”œβ”€β”€ resolution-cache
β”‚Β Β  β”œβ”€β”€ scala-2.11
β”‚Β Β  β”œβ”€β”€ streams
β”‚Β Β  └── web
└── test
    β”œβ”€β”€ ApplicationTest.java
    └── IntegrationTest.java

20 directories, 13 files

Every directory has its own meaning, once again, exactly as for project generated from Maven's artifact. In Play, following directories mean:

Because we already know how to manipulate Play files, we can make some basic changes. In this article we want only to learn some useful Play reflexes, as view generation, route definition or basic embedding of view fragments. Let's start by creating a generic template used for the project. The template will be composed by 3 "includable" parts: top menu, content and footer. The first and the last one are represented by template files placed in /app/views folder. The content is generated transmitted automatically to general layout (in our case called main.scala.html) from view file corresponding to controller response:

@(title: String)(content: Html)

<!DOCTYPE html>

<html>
    <head>
        <title>@title
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript">
    </head>
    <body>
    	@menuTop()
        @content
        @footer()
    </body>
</html>

As you can observe, top menu and footer are represented as functions (presence of () after the name). In fact, they correspond to view files with the same names (menuTop.scala.html and footer.scala.html). Regarding to content, is set automatically by controller response file, inside "method" defining used template. You can observe that in our index.scala.html file which is returned by Play's default Application controller, created at initialization time:

@(message: String)
@main("Virtual shop - the best e-commerce place") { 
	@* We put the @content value here *@
	<h1>Welcome to virtual shop</h1>
}

If you run it, you'll see that returned page will be composed by: top menu, <1 /> with "Welcome to virtual shop" as content and footer. You can notice also another thing - a String parameter passed to main template. In fact, this parameter is defined as title variable (@(title: String)(content: Html)) and used as the content of <title /> tag in main.scala.html.

The second thing which we want to see is routing. As we mentioned earlier, the routes are specified in conf/routes file. If you open it, you should see something like that:

# Home page
GET     /  controllers.Application.index()

#Β User rotures - added by me to open User registration page
GET /register  controllers.User.register()

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

The deduction is simple. The routes are separated by new line character. Every route is composed by 3 elements: the first one corresponds to HTTP method (GET, POST...), the second one to URL and the third to method used to return the response. In our case we wanted to add a link to register page. You can quickly find it under /register URL. It points to /app/controllers/User.java class and its register() method:

public class User extends Controller {

    public static Result register() {
        return ok(register.render("Message used in view file"));
    }
}

As you can see, the class extends play.mvc.Controller public abstract class. It contains some of static methods which are handled to generate responses. Beware, these methods must be static. Otherwise, a similar exception can be produced:

[ERROR] [07/05/2014 21:14:40.758] [play-akka.actor.default-dispatcher-2] [ActorSystem(play)] Uncaught error from thread [play-akka.actor.default-dispatcher-2] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled
java.lang.IncompatibleClassChangeError: Expected static method controllers.User.register()Lplay/mvc/Result;
	at Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(routes_routing.scala:74)
	at Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(routes_routing.scala:74)
	at play.core.Router$HandlerInvokerFactory$$anon$4.resultCall(Router.scala:264)
	at play.core.Router$HandlerInvokerFactory$JavaActionInvokerFactory$$anon$15$$anon$1.invocation(Router.scala:255)

After conversion, we have our static method corresponding to routes mapping. We see that this methods returns an instance of play.mvc.Result. This class is, as its name indicates, the controller's response to manipulated request. ok() method means that the response should be generated with 200 HTTP code. register.render corresponds to the template created under /app/views/User/register.scala.html:

@(message: String)


@main("Welcome to register page") { 
	form registration => @message <=
}

The render method takes one String argument. As you can deduce, this argument corresponds to variable defined in view files (@(message: String)). We could change the signature and, for example, pass an instance of our database object instead of simple String. This element brings another interesting Play feature - dynamic conversion of views to Java files. In reality, register object corresponds to the register.scala.html view of /app/views/User directory and it's imported as standard Java file:

import views.html.User.*;

It's because view templates are compiled and considered as normal Java dependencies. You can see it by going to your's target view directory and listing classes generated to User.register() view:

bartosz@bartosz-K70ID:~/workspace/playApp/testApp/target/scala-2.11/classes/views/html/User$ ls
register$$anonfun$f$1.class  register.class  register$.class

Note also that if you can't see views.html.User.* in your Eclipse, it means that the view wasn't compiled or that the compiled classes weren't reloaded in Eclipse. For the first case, you can call ./activator compile and check if everything works. For the second case, you can select the root directory of your project in Eclipse and refresh it by pushing F5 for example.

This article is only an introduction to Play Framework. In the first part we discovered some of key concepts of this framework (configuration, routes, controllers). At the second one we discovered the main components to initialize and deploy Play application. The last part was a little more pragmatic because it shown how to make some basic operations as view rendering or routes definition.


If you liked it, you should read:

πŸ“š Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!