Blog

Play! 2.0: A First Impression

Play! is a web framework for Java and Scala. Play promises to bring the developer productivity of web frameworks like Ruby on Rails to the Java and Scala languages. Of course, it wouldn’t make much sense just to copy Rails. So Play adds its own spin: Play 2.0 is fully statically type checked, giving the developer quick feedback when something doesn’t make any sense.

Now that Play 2.0 is getting closer to final release I took some time to dive in. Here are my first impressions, using the Scala APIs.

TL;DR

Pros:

  • Automatic recompile on refresh gives you extremely quick feedback.
  • High modularity makes Play easy to learn, use, and extend.
  • Very easy deployment to production environments.
  • Type safe templates and routes make it easier to keep your application consistent.
  • High performance with support for both synchronous and asynchronous HTTP handling.
  • Java and Scala APIs are quite similar, so should allow easy transfer of skills.
  • Support for pre-compiled assets including CoffeeScript, LESS CSS, and the Closure JavaScript compiler.
  • Sessions are simple cookies, making it easy to scale or perform zero-downtime upgrades.

Cons:

  • Support for WAR packaging is not planned until 2.1. This is needed if you need to deploy in a Servlet Container.
  • Not backwards compatible with the older Play 1.x framework.
  • Custom conf/routes format instead of simple DSL like Scalatra or spray.cc.
  • Many APIs rely on singletons (Scala) or static methods (Java), which can make testing challenging. However, fake implementations of the main abstractions are provided (Application, Request, etc).

Out of the box experience

Getting started with Play is straigthforward. Just download the distribution, unpack it, and add it to your $PATH. After that type play new my-app, select your application’s name (defaults to my-app in this example), and type (Scala, Java, or empty). After choosing the Scala application type I ended up with a new directory containing just a few directories and files (with just 19 lines of Scala code and 17 lines of HTML templates).

Starting the application is as easy as typing play run and pointing your browser to http://localhost:9000/. The initially load takes a while as it needs to compile templates and Scala sources. After the page loads you get a nice explanation what to do next and plenty of documentation links:

Controllers and templates

The first thing you encounter in any web framework is to mapping from HTTP requests to your code. In Play this is extremely straightforward. The request method and path are located in the conf/routes file and the indicated method is invoked in your controller. An example route entry is:

GET     /            controllers.Application.index

So whenever the home page is requested the controllers.Application.index method is invoked. Notice that you’ll get a compile time error if this method does not exist or requires parameters. So if you add the following to the conf/routes file:

GET     /:page       controllers.Application.page(page, format ?= "html")

and refresh your browser you’ll quickly see an error message:

Compilation Error

Fixing this is easy, just add the missing method to the controller.Application object:

def page(name: String, format: String) = Action {
  Ok(<p>You requested page {name} in format {format}</p>.toString).as(HTML)
}

As you can see a controller action is just a method that returns an Action. The Action in turn returns an Ok response, which translates into a HTTP 200 OK response. What I like here is that the controller method is directly linked from the conf/routes file without me having to remember any mapping conventions. There is also no complicated rendering pipelines that are typically associated with component based web frameworks like JSF or Wicket.

In this action we directly returned a piece of HTML. It is also possible to use templates. Let’s change the index page to link to our new controller action. By changing the index.scala.html template to:

@(message: String)

@main("Welcome to Play 2.0") {
  <a href="@routes.Application.page("1984", "PDF")">page example</a>
}

a new link is rendered that maps to the new route. After refreshing the browser you can now click the link to navigate to your new page action. Extremely straightforward and especially nice is the use of type safe URLs and the light-weight template syntax (just using the @ sign to add some Scala code in your HTML template).

Also nice is that templates are turned into ordinary methods (with the first line of the template becoming the parameter list) that can be called from controllers, other templates, or tests. In the template above the main template is invoked to render layout around the provided link.

Forms

Play also has support for forms, validation, and binding form data to domain objects. Unlike most other web frameworks it is not necessary to alter your domain objects to use them with forms. Specifically, there is no need to add a default constructor or any setters. Here is an example domain object User with a form definition:

import play.api.data._, Forms._, validation.Constraints._

// Domain object
case class User(name: String, age: Int)

// Form definition
val userForm = Form(
  mapping(
    "name" -> nonEmptyText,
    "age" -> number(min = 0, max = 150)
  )(User.apply)(User.unapply))

// Form submit action
def register = Action { implicit request =>
  userForm.bindFromRequest.fold(
    errors => BadRequest(views.html.index(errors)),
    user => Redirect(routes.Application.index))
}

The form defines two fields with validation and the mapping from the form fields to the domain object (in this case by using the Scala generated User.apply and User.unapply methods).

The register action uses the form definition to perform validation and bind data from the request to the domain object. In case of errors, the page is rerendered with the validation errors. Otherwise a new user instance is available (and in this case a simple redirect is performed, a real application would probably save the user somewhere). Notice that it is not possible to get a (partially) invalid user object, unlike most other form validation frameworks (like Ruby on Rails, Spring MVC, and Wicket).

To render the form there are some predefined form helpers (also available in Twitter Bootstrap variant). Unfortunately the field names are not statically checked against the form definition:

@(registrationForm: Form[User])

@helper.form(action = routes.Application.register) {
  @helper.inputText(registrationForm("name"))
  @helper.inputText(registrationForm("age"))
  <input type="submit" value="Register" />
}

Databases

Play comes with built-in support for SQL/JDBC databases. The simplest way to use this to first enable the datasource in conf/application.conf:

db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"

Then add a schema evolution file conf/evolutions/default/1.sql:

# --- !Ups

create table users (
  name text primary key,
  age int not null);

# --- !Downs

drop table users;

When you refresh the browser Play will notify you that you need to apply the schema migration:

Pressing the ‘Apply this script now!’ button will immediately perform the database migration.

Now that we have a running database it is easy to query using the JDBC API and the Anorm wrapper library:

import play.api.db._, anorm._, play.api.Play.current

def registeredUsers = DB.withConnection { implicit connection =>
  SQL("select name, age from users")().map(row => User(row[String]("name"), row[Int]("age"))).toList
}

Since DB.withConnection/DB.withTransaction gives you a plain JDBC connection it is also easy to use any other Java or Scala tool to handle database persistence.

Deployment

Deploying a Play application is easy. My preference would be to use the play dist command that builds a ZIP archive. Then you only need a Java runtime and database installation on your production server to run your application (everything else is included in the ZIP, including the Scala libraries and a start script).

Another option is to use the play stage command to run the application in-place. Combine this with a git push to Heroku or your own production server and you’re ready to go.

Performance

Play is build on Netty, which provides very high HTTP server performance. Play supports both synchronous and asynchronous IO, so handling many long-lived connections should be possible (and Play provides excellent support for streaming using its Iteratees based data stream support). Integration with Akka 2.0 should give you all the tools you need to build high-performance and highly-scalable applications.

To give you an idea, I had no trouble getting more than 10.000 HTTP requests per second (tested using jmeter) on my mid-2010 MacBook Pro (2.66 GHz Intel Core i7). The rendered HTML page was fairly trivial (just the user registration form as shown above), but it is good to know basic performance is very good.

Conclusion

This was just a first quick look at Play 2.0, but I’m quite impressed with the ease of learning, development, and deployment. Play 2.0 is a worthy addition to the Typesafe Stack and should give Java and Scala developers a very good platform to build web applications on.

Zilverline gebruikt cookies om content en advertenties te personaliseren en om ons websiteverkeer te analyseren. Ook delen we informatie over uw gebruik van onze site met onze partners voor adverteren en analyse. Deze partners kunnen deze gegevens combineren met andere informatie die u aan ze heeft verstrekt of die ze hebben verzameld op basis van uw gebruik van hun services.

Okee