How to demo the Play framework 1.2 - live coding script

08 June 2010

Peter Hilton

by Peter Hilton

If you want someone to know how fast and straightforward it is to build a Java web application with the Play framework, then show, don’t tell. The most compelling way to do this is to download Play 1.2 and start coding a web application from scratch, while they watch. This article is a script for a five-minute live-coding Play demo.

This script is based on how I have seen Play’s creator, Guillaume Bort, demonstrate Play to introduce it to a small audience. More or less the same demo, but with Ajax, is the video on the front page of http://www.playframework.org/. This is an especially good way of showing Play to a small group of people, if you are fairly fluent with the framework, because you can answer questions about how you do something by just writing the code and showing it working far more quickly than you would be able to with other frameworks.

Summary

  1. Download the Play framework.

  2. Create and run the application.

  3. Inspect the set-up for the default welcome page.

  4. Replace the welcome page with a trivial dynamic template.

  5. Generate a Java compilation error.

  6. Retrieve HTTP request parameters.

  7. Show message file content in the template

  8. Add the project to Eclipse.

  9. Add a JPA Entity.

  10. Add an HTML form for creating entity instances.

  11. Add a link for deleting each instance.

  12. Use a Java extensions in the view template.

  13. Add form validation.

Source code for this article is available at https://github.com/hilton/play-tasks.

Create and run the application

Open http://www.playframework.org/ Download the binary

There is no need to install Play as such, although you might want to add the download’s directory to your PATH, so you can execute the $PLAY_HOME/play commmand directly. On my machine, though, it’s just as easy to just type ~/Downloads/play-1.2.4/play for this kind of demo.

Execute play new tasks ~ What is the application name? tasks

A task list application is an easy example for people to understand, but it is more fun to pick an example that is more familiar and relevant to your audience, such as 'customers' or 'cocktails'.

Execute find tasks -type f (optional) - List the generated files

It is crucial to note that Play has not generated a lot of code here; the files are minimal stubs that you will only add to, rather than a lot of generated code that you will have to delete.

Execute play run tasks - Start the Play server runtime to run the application.

Depending on what you are used to, start-up time may seem extremely short.

Open http://localhost:9000/ - Show welcome page.

The welcome page describes how the stub application works, which the next few steps show.

Start a text editor.

You could use an IDE, such as Eclipse, already at this point. However, it is useful to start with a plain text editor to make it clear that you do not have to compile the Java code yourself.

Edit conf/routes - Show the route for the GET / HTTP request.

Since Play has an HTTP focus, in incoming HTTP request is a good starting point for explaining how it works. The routes file, is therefore crucial, because this defines the (two-way) mapping between HTTP requests and Java methods.

Edit app/controllers/Application.java - Show the index() method.

Edit app/views/main.html - Show the default template: HTML 5, CSS and jQuery.

Edit app/views/Application/index.html - Show the #{welcome /} tag and replace it with <h1>Tasks</h1>.

Open http://localhost:9000/ - Show the heading.

Edit public/stylesheets/main.css - Add some CSS to make things less ugly:

html { border-top: 5px solid #67A927;  }
body { font-family:"Helvetica Neue"; padding:2em; background: #F7F7F7 url(/public/playmanual/logo.png) no-repeat 98% 20%; }
body:before { content:'Play task list demo'; color:#568C00; font-size:150%; text-transform:uppercase; letter-spacing:0.4em; }
ul { padding:0; list-style:none; }
li, form { width:30em; background:white; padding:1em; border:1px solid #ccc; border-radius:0.5em; margin:1em 0; position:relative; }
li a { text-decoration:none; color:transparent; position:absolute; top:1em; right:1em; }
li a:after { content:'❎'; color:#aaa; font-size:120%; font-weight:bold; }
form * { font-size:120%; }
input { width:16em; }
button { cursor:pointer; color: white; background-color: #3D6F04; background-image: -webkit-linear-gradient(top, #5AA706, #3D6F04); text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); border: 1px solid #CCC; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border-radius:4px; }
p.error { margin:0; color:#c00; }

Show dynamic data in the template

Edit app/views/Application/index.html - Change the heading to <h1>${items}</h1>.

Edit app/controllers/Application.java - Change the index() method body to the following (omitting a semi-colon).

final String items = "Things"
render(items);

Open http://localhost:9000/ - Show the Java compilation error.

Edit app/controllers/Application.java - Add the missing semi-colon.

Open http://localhost:9000/ - Show the new heading, and Java changes without compilation or deployment.

Edit app/controllers/Application.java - Replace the items declaration line with a String items method parameter.

Edit app/controllers/Application.java - Undo the last change - remove the parameter and put the declaration back.

Show message file content in the template

Edit app/views/Application/index.html - Change the heading to <h1>&{'model.shipments'}</h1>.

Open http://localhost:9000/ - Show the message key being displayed, because the message is not defined.

Edit conf/messages - Add the line model.shipments = Shipments

Open http://localhost:9000/ - Show the message being displayed.

Eclipse

Execute Control+C - Show how little logging there is by default.

Execute play eclipsify tasks - Generate Eclipse project and class path configuration.

Eclipse File » Import… » Existing projects into workspace - Show project structure.

Eclipse eclipse/tasks.launch » Run » tasks - Start the Play server runtime from within Eclipse.

Open http://localhost:9000/ - Show the application running.

IntelliJ IDEA

Execute Control+C - Show how little logging there is by default.

Execute play idealize tasks - Generate IDEA project and class path configuration.

Execute open tasks/tasks.ipr - Open the project in IDEA.

Execute play run tasks - Start the Play server again.

JPA entity

Edit app/models - create class:

@Entity
public class Task extends play.db.jpa.Model {

   public String title;
}

At this point you may need to explain that Task is a Java Bean at run-time, because Play dynamically adds getter and setter methods for the public fields, turning them into normal Java Bean properties.

Edit app/controllers/Application.java - Change the index() method body to

List tasks = Task.findAll();
render(tasks);

Edit app/views/Application/index.html - After the heading, add:

<ul>
#{list tasks, as:'task'}
   <li>${task.title}</li>
#{/list}
</ul>

Open http://localhost:9000/ - Show the JPA error.

Edit conf/application.conf - Uncomment the line # db=mem

Open http://localhost:9000/ - Show the page - no tasks.

HTML form

Edit app/views/Application/index.html - After the list, add:

#{form @add()}
<p>
  <input name="task.title" autofocus>
  <button type="submit">Add Task</button>
</p>
#{/form}

Edit app/controllers/Application.java - Add the method:

public static void add(final Task task) {
   task.save();
   index();
}

Open http://localhost:9000/ - Add tasks.

Edit app/views/Application/index.html - Inside the <li> add a link:

<a href="@{delete(task.id)}">delete</a>

As for forms, there is also a tag for generating links; this way just generates the URL.

Edit conf/routes - Add GET /delete Application.delete

Edit app/controllers/Application.java - Add the method, noting the id parameter:

public static void delete(final Long id) {
   Task task = Task.findById(id);
   task.delete();
   index();
}

Open http://localhost:9000/ - Delete tasks - show the link URL and query string parameter.

Edit conf/routes - Change the delete route to GET /delete/{id} Application.delete

Open http://localhost:9000/ - Delete tasks - show the link URL and URL path parameter.

Java extensions

Edit app/views/Application/index.html - Change the heading to:

<h1>${tasks.size()} Task${tasks.pluralize()}</h1>

Open http://localhost:9000/ - Add/delete tasks to show singular and plural forms.

If you are lucky, at this point someone in the audience will be smart enough to point out that some plurals are not just formed by adding an 's', at which point you can change the example, and show the pluralize method with one or more parameters, e.g. ${tasks.pluralize(messages.get('task'), messages.get('tasks'))}

Form validation

Edit app/controllers/Application.java - Add the @Valid annotation to the add method’s Shipment parameter, replace the first line of the method body (Task.save();) with the following.

if (validation.hasErrors()) {
   validation.keep();
}
else {
   task.save();
}

Edit app/views/Application/index.html - immediately after the form tag, add:

#{errors}
    <p class="error">${error}</p>
#{/errors}

Open http://localhost:9000/ - Show the validation error when submitting an empty name.

The validation error is just 'Required', but we can change this.

Edit conf/messages - Add the line validation.required = %s is a required field

Open http://localhost:9000/ - Show the new validation error.

Now we get the field name, but not as a formatted label.

Edit conf/messages - Change the placeholder in validation.required to &{{ "{%s" }}}, and add the line task.name = Task name

Open http://localhost:9000/ - Show the new validation error.

This lists validation errors in one place. A better way is to list the errors next to each field.

Edit app/views/Application/index.html - Replace the errors tag with:

#{ifErrors}
    <p class="error">Validation failed</p>
#{/ifErrors}

… and after the text input and button, before the closing form tag, add:

<p class="error">#{error 'task.title'/}</p>

Open http://localhost:9000/ - Show the new validation error.

Peter Hilton is a senior software developer at Lunatech Research and committer on the Play open-source project.