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.
Data Engineering Design Patterns

Looking for a book that defines and solves most common data engineering problems? I wrote
one on that topic! You can read it online
on the O'Reilly platform,
or get a print copy on Amazon.
I also help solve your data engineering problems π contact@waitingforcode.com π©
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:
- products will be stored in the database - thanks to it, we'll discover Play configuration and database connection
- user will store the details of its order in session-based cart - session handling discovery
- products will be displayed dynamically, according to transmitted identity - we'll discover as well dynamic routes as HTTP parameters handling
- register form - will be an example of form implementation
- responses adapted to JavaScript interface, as JSON - this feature will present how to manipulate response generation
- internationalization - the store will be translated in English and French.
- templating - different parts of won't be presented at the same manner, for example landing pages will have different template from store pages
- store won't be accepted in unstable state - Play automatic tests will help us to disallow deployment of incorrectly coded changes
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:
- app - contains all "executive" files for our web app (as controllers or views). It can also contain model directory which stores application business layer. However, it can be made manually.
- app/assets - all compiled web resources, as JS, are stored here.
- app/controllers - as its name indicates, all controllers must be saved here.
- app/views - this directory stores all views, ie. files shown as responses for user's requests.
- conf - we can find here the configuration files as routes definition (routes) or main configuration file (application.conf)
- project - Play stores here all files needed to compile and configure project.
- public - as for different PHP's frameworks (Zend Framework, Symfony), public directory contains all web specific resources as JS, CSS files or images.
- target - the project is compiled into this directory
- test - this is a source directory for unit or functional tests
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.
Consulting

With nearly 16 years of experience, including 8 as data engineer, I offer expert consulting to design and optimize scalable data solutions.
As an OβReilly author, Data+AI Summit speaker, and blogger, I bring cutting-edge insights to modernize infrastructure, build robust pipelines, and
drive data-driven decision-making. Let's transform your data challenges into opportunitiesβreach out to elevate your data engineering game today!
π contact@waitingforcode.com
π past projects