Your first Play! – GAE – Siena application: Tutorial with Example

Convention over configuration has became a buzz word these days. It is a software design paradigm which seeks to decrease the number of decisions that developers need to make, gaining simplicity, but not necessarily losing flexibility. More and more web frameworks are using this approach and making web development simple. Ruby on rails, DJango, Grails, CakePHP, ASP .NET, Apache Wicket etc are few in top demand these days.

What’s Play!?

One of the newest one is Play! framework. Play is heavily inspired by Ruby on Rails and Django. A developer familiar with any of these frameworks will feel at home. Play leverages the power of Java to build web applications in an environment that is not Java Enterprise Edition-centric. By lifting away the Java EE constraints, Play provides developers with an easy-to-develop and elegant stack aimed at productivity. I have worked in CakePHP and I am a big fan of it. I believe Play! is bringing the same experience of fast web application development to the Java world, right from hello world baby steps to create a full production ready application and writing tests. When you visit Play!’s overview webpage on their site, the very first line will surely catch your attention. The Play framework is a clean alternative to bloated Enterprise Java stacks. It focuses on developer productivity and targets RESTful architectures. Play is a perfect companion to agile software development. Although the words “bloated Enterprise Java stacks” may offend many Java developers who are very used to work with Spring MVC, Struts of JavaServer Faces; I think its more of less correct and many of us already feels so working for years now in Spring / Struts and realizing how much boiler plate code is being pushed into system and how complex things became over period of time. What I like most about Play! framework is the flexibility of this new framework and how easy it makes the life of developer. The application development becomes a piece of cake (like most CakePHP developers will feel). And the best part is all this you can do with Java without switching your favorite language and shifting to other like Rube or PHP.

Highlights of Play! framework

Let us see few differences of Play! from other Java frameworks (Courtesy Wiki):
  • Play is fully RESTful – there is no Java EE session per connection. This makes Play more outwardly-scalable than many other frameworks.
  • No configuration: download, unpack and develop – it’s that simple.
  • Easy round trips: no need to deploy to an application server, just edit the code and press the refresh button on the browser.
  • Integrated unit testing: unit testing is at the core of Play!
  • Elegant API: rarely will a developer need to import any third party library – Play comes with all the typical stuff built-in.
  • CRUD module: easily build administration UI with little code.
  • Play is a web framework: it is language agnostic and currently supports Java and Scala.
  • Modular architecture

Your first Play! application

Hello world in Play! is straight forward. I will not spend much time in describing steps for installing and configuring Play!. On its homepage, Play! framework developer Guillaume Bort has made this wonderful getting started screencast which you can follow to create your very first Play! application.
Few points I would like to highlight here is:
  • Play is a real “Share nothing” system. Ready for REST, it is easily scaled by running multiple instances of the same application on several servers.
  • Play! loads the code dynamically. Just fix the bug in Java file and hit reload and voila, the change will get reflected instantly on webpage. No need to go through those sluggish rebuild / deployment / server restart.
  • Play! comes with a very efficient template system. The template system is based on Groovy as an expression language. It provides template inheritence, includes and tags.
  • A fully functional stack of modules are available in Play!. Provides integration with Hibernate, OpenID, Memcached… And a plugin system to create your own customize plugin or include one from hundreds of freely available ones.
  • Error discovery is very easy with Play!. When an error occurs, play shows you the source code and the exact line containing the problem. Even in templates.

Your first Play! – GAE – Siena application

For this tutorial, I have chosen a little complex example which we develop in Play! and deploy on Google App Engine (GAE). The GAE offers fast development and deployment; simple administration, with no need to worry about hardware, patches or backups; and effortless scalability. The Google app engine does not support SQL or RDBMS. The App Engine datastore provides robust, scalable storage for your web application, with an emphasis on read and query performance. An application creates entities, with data values stored as properties of an entity. The app can perform queries over entities. All queries are pre-indexed for fast results over very large data sets. Our demo application called eazyBookmark is already running on Google App engine. This is a Bookmark application where user can login and save their bookmarks. Click below image to visit online demo.

Online Demo

Link: http://eazybookmark.appspot.com/ Following are the functional aspects of eazyBookmark app.
  1. User can login to eazyBookmark using their Google account. No need to remember password for new account.
  2. Using “Add link” functionality, user can add any URL and save as bookmark.
  3. Any bookmark can be tagged with multiple tags. This way user can organize the links easily.
  4. A bookmark once added can later be Edited or deleted.
  5. A small bookmarklet script is available for user to add it in browser favorites. This bookmarklet can be later used by User to bookmark any webpage. Read Tools page of eazyBookmark.

Getting started

Create a blank application in Play! using play create eazybookmark commnd.
C:\Play>play new eazybookmark
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.1, http://www.playframework.org
~
~ The new application will be created in C:\Play\eazybookmark
~ What is the application name? [eazybookmark]
~
~ OK, the application is created.
~ Start it with : play run eazybookmark
~ Have fun!
~
The above Hello World video shows in depth how to install and use Play and create your first application.

Adding Google App Engine (GAE) and Siena module to Play!

We need two modules for our eazybookmark application. One is Google App engine (GAE) and another one is Siena. You can install these modules by running following command with Play!.
C:\Play> play install gae
..
..
C:\Play> play install siena
The above commands will install latest version of these modules. If you are behind proxy than the above command may fails. Currently there is no way of setting up proxy with Play! (v1.1) so you can manually download gae and siena from its homepage and copy them in /modules directory of your Play!. Once the modules are installed successfully, you can add following lines in eazybookmark apps /conf/application.conf file.
# ---- Google app engine module ----
module.gae=${play.path}/modules/gae-1.4
# ---- Siena module ----
module.siena=${play.path}/modules/siena-1.3
You may want to restart the Play application to reflect the changes of the modules. Once the application is restarted, the GAE module creates /war/WEB-INF/appengine-web.xml file. You need to update this file and add the application id of GAE. In our case we will have following data. File: /war/WEB-INF/appengine-web.xml
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <application>eazybookmark</application> <version>1</version> </appengine-web-app>
Code language: HTML, XML (xml)
You may want to replace eazybookmark with your own GAE Application ID.

Play! and Siena models

Let us first review the model classes we going to use for our application. We will have four model classes.
  • User – This is a User model. All user related information such as email, name, etc will get stored here.
  • Link – This is Link model. All links related information such as URL, title, description etc will get stored here.
  • Tag – This is Tag model. All the tags related information such as tagname etc will get stored here.
  • LinkTag – This is a mapping model which maps Links with Tag. In Siena to create Many to Many relationship we need to create three classes. This class maps Link and Tag and add a many to many relationship to these entities.
Here is the code for each of the models. File: /app/model/User.java
package models; import siena.*; public class User extends Model { @Id public Long id; public String email; public String name; public Date created; public Date modified; static Query<User> all() { return Model.all(User.class); } public static User findById(Long id) { return all().filter("id", id).get(); } public static User findByEmail(String email) { return all().filter("email", email).get(); } public User() { super(); } public User(String email) { this.email = email; } public String toString() { return email; } }
Code language: Java (java)
The User model has few attributes like email, name, created and modified which define a user. Few methods like findById() and findByEmail() select the User based on Id and email respectively. File: /app/model/Link.java
package models; import java.util.*; import siena.*; public class Link extends Model { @Id(Generator.AUTO_INCREMENT) public Long id; public String url; public String title; public String description; public Date created; public Date modified; @Filter("link") public Query<LinkTag> linktags; @Index("user_index") public User user; public Link(User user, String url) { super(); this.url = url; this.user = user; } public Link() { super(); } static Query<Link> all() { return Model.all(Link.class); } public static Link findById(Long id) { return all().filter("id", id).get(); } public static List<Link> findByUser(User user) { return all().filter("user", user).order("-created").fetch(); } public static void addTag(Link link, Tag tag) { LinkTag linkTag = new LinkTag(link, tag); linkTag.insert(); } public String toString() { return url; } public List<Tag> findTagsByLink() { List<Tag> tags = LinkTag.findByLink(this); return tags; } public static void addTagsFromCSV(Link link, String tagcsv, User user) { Collection<Tag> tags = null; if(null != tagcsv || !tagcsv.equalsIgnoreCase("")) { String [] tagArr = tagcsv.split(","); tags = new ArrayList<Tag>(); for(String tagstr : tagArr) { tagstr = play.templates.JavaExtensions.slugify(tagstr.trim()).trim(); if(null != tagstr && !tagstr.equals("")) { Link.addTag(link, Tag.findOrCreateByName(tagstr,user)); } } } } }
Code language: Java (java)
The Link model class is similar to User. It has its own attributes such as url, title, description etc. Also note that Link model has few methods like findById(), findTagsByLink(), addTag(), addTagsFromCSV() etc. These methods are used from the controller to manipulate the link data. The Link has a many to many relationship with Tag. One link can have multiple tags attached to it. File: /app/model/Tag.java
package models; import siena.*; import java.util.*; public class Tag extends Model { @Id(Generator.AUTO_INCREMENT) public Long id; public String name; @Index("user_index") public User user; public static Query<Tag> all() { return Model.all(Tag.class); } public static Tag findOrCreateByName(String name, User user) { Tag tag = all().filter("user", user).filter("name", name).get(); if(null == tag) { tag = new Tag(); tag.name = name; tag.user = user; tag.insert(); } return tag; } public static Tag findById(Long id) { return all().filter("id", id).get(); } public static Tag findByName(String name, User user) { return all().filter("user", user).filter("name", name).get(); } public static List<Tag> findByUser(User user) { return all().filter("user", user).fetch(); } public String toString() { return name; } }
Code language: Java (java)
The Tag model has one attribute tag name. This can be anything like travel, leisure, technology, news etc. Also note that each tag has one to one mapping with User. This is defined by @Index("user_index") annotation with object User. The Tag has usual methods findById, findByName, findByUser, findOrCreateByName etc which is used from controller to manipulate the Tag data. File: /app/model/LinkTag.java
package models; import java.util.*; import siena.*; public class LinkTag extends Model { @Id public Long id; @NotNull @Column("link_id") @Index("link_index") public Link link; @NotNull @Column("tag_id") @Index("tag_index") public Tag tag; public LinkTag(Link link, Tag tag) { super(); this.link = link; this.tag = tag; } public static Query<LinkTag> all() { return Model.all(LinkTag.class); } public static List<Tag> findByLink(Link link) { List<LinkTag> linkTags = all().filter("link", link).fetch(); List<Tag> tags = new ArrayList<Tag>(); for(LinkTag linkTag : linkTags) { tags.add(Tag.findById(linkTag.tag.id)); } return tags; } public static List<Link> findByTag(Tag tag) { List<LinkTag> linkTags = all().filter("tag", tag).fetch(); List<Link> links = new ArrayList<Link>(); for(LinkTag linkTag : linkTags) { links.add(Link.findById(linkTag.link.id)); } return links; } public String toString() { return link.toString() + " : " + tag.toString(); } public static void deleteByLink(Link link) { List<LinkTag> linkTags = all().filter("link", link).fetch(); if(null != linkTags) { for(LinkTag linktag : linkTags) { linktag.delete(); } } } }
Code language: Java (java)
The LinkTag model links the models Link and Tag. It adds the Many-to-many relationship between these models. We have few useful methods like findByLink, findByTag to get the list of relationship.

The Play! Controllers

The controller in Play! framework handles all the request / response between client and web server. It is a plain Java class which extends Controller class of Play!. In our application we will use two controller classes: Application and Profile. We will also have a helper class called Auth which has few useful methods to determine is a user is logged in, or to get email address of logged in user. This class invoke GAE module to get the authentication details and route user to Google page for authentication. File: /app/controller/Auth.java
package controllers; import play.modules.gae.*; import com.google.appengine.api.users.*; public class Auth { public static boolean isLoggedIn() { return GAE.isLoggedIn(); } public static String getEmail() { return GAE.getUser().getEmail(); } public static User getUser() { return GAE.getUser(); } public static void login(String action) { GAE.login(action); } public static void logout(String action) { GAE.logout(action); } }
Code language: Java (java)
Note that above Auth class is not part of controller (We haven’t extended it with Controller). It is just a utility class that we can use from other controllers to access user information. You may want to create your own package utils and keep this class there. For sake of simplicity, I am keeping this in controllers package. File: /app/controller/Application.java
package controllers; import play.mvc.*; import models.*; import models.User; import java.util.*; public class Application extends Controller { public static void index() { if(Auth.isLoggedIn()) { User user = User.findByEmail(Auth.getEmail()); if(null == user) { user = new User(); user.email = Auth.getEmail(); user.created = new Date(); user.insert(); Profile.edit(user.id); return; } Profile.index(null); } render(); } public static void login() { Auth.login("Application.index"); } public static void logout() { Auth.logout("Application.index"); } }
Code language: Java (java)
Check the Application controller which is the entry point to our application. The index() method will be called whenever user request homepage /. In index() method we are checking if user is logged in. If not, then we show her a Login page using render() method. This method renders the default view of this action. We will see this view later in our example. Also note that if user is authenticated, we redirect it to Profile.index action. If user has logged in for first time in eazyBookmark, she will be redirected to Edit Profile page where she can select username for her profile. File: /app/controller/Profile.java
package controllers; import models.*; import play.*; import play.mvc.*; import java.util.*; import play.data.validation.*; import java.net.*; import net.htmlparser.jericho.*; import java.io.*; import play.Logger; import models.Tag; public class Profile extends Application { @Before static void checkConnected() { if(Auth.getUser() == null) { Application.login(); } else { renderArgs.put("user", Auth.getEmail()); } } public static void index(String tag) { User user = getUser(); List<Link> links = null; if(null == tag || tag.equalsIgnoreCase("")) { links = Link.findByUser(user); } else { links = LinkTag.findByTag(Tag.findByName(tag, user)); } List<Tag> tags = Tag.findByUser(user); render(user, links, tags); } //... //... }
Code language: Java (java)
The Profile controller is extended from Application controller to take advantage of login() / logout() method. Also note that this is not the full code for Profile.java. You can check the full source in the project source code.

The View

All the view files in Play! framework as saved in /views/ folder with .html extension. These are the plain HTML files with certain groovy template tags. Read more about built-in template tags in Play! here. There is a file main.html in /views folder which act as the template for all other views. Following is the content of main.html for eazyBookmark. File: /views/main.html
<!DOCTYPE html> <html> <head> <title>#{get 'title' /}</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/main.css'}"> #{get 'moreStyles' /} <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.ico'}"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript" charset="utf-8"></script> #{get 'moreScripts' /} </head> <body> <div id="pagewidth" > <div id="headerbar"> <div id="headercol"> <div id="logo"> <h3><a href="/">eazyBookmark</a></h3> </div> <div id="headerlink"> #{if user} ${user} <span class="sep">|</span> <a href="@{Application.logout()}">Logout</a> #{/if} #{else} <a href="@{Application.login()}">Login</a> #{/else} </div> </div> </div> <div id="wrapper" class="clearfix"> <div id="maincol"> #{if flash.error} <p class="error">${flash.error}</p> #{/if} #{if flash.success} <p class="success">${flash.success}</p> #{/if} #{doLayout /} </div> </div> <div id="footer"> <p> Copyright &copy; ${new Date().format('yyyy')} <a href="http://eazybookmark.appspot.com">eazyBookmark</a> </p> </div> </div> </body> </html>
Code language: HTML, XML (xml)
In above template main.html file, we have #{doLayout /} tag which renders the content. Also note that we are displaying error / success message using flash mechanism of Play!. For each controller, we create a separate folder in views/ folder. And these folders will contain the view file for each action in given controller. Thus in /views/Application folder we will have index.html and in /views/Profile folder we will have edit.html, editlink.html and index.html. Download these html files from the source code attached at the end of tutorial.

Deploying Play! app in Google App Engine (GAE)

We are done with the coding of our eazyBookmark application. Now we can deploy this app in our GAE account. For that follow the basic steps given here:
  1. Get yourself a GAE account and set up an application. you will need the ID.
  2. Download the GAE SDK for Java
  3. run play war myappname -o myappname-war
  4. run APPENGINE_SDK_DIR/bin/appcfg update myappname-war/
  5. Log in to your app engine console and check out your application!
You can check the Online Demo here: http://eazybookmark.appspot.com

Download full Source Code

eazybookmark.zip (433 KB)

View Comments

Share
Published by
Viral Patel
Tags: GAE Google App Engine JQuery Play-framework Siena Tutorial

Recent Posts

  • Java

Java URL Encoder/Decoder Example

Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…

4 years ago
  • General

How to Show Multiple Examples in OpenAPI Spec

Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…

4 years ago
  • General

How to Run Local WordPress using Docker

Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…

4 years ago
  • Java

Create and Validate JWT Token in Java using JJWT

1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…

4 years ago
  • Spring Boot

Spring Boot GraphQL Subscription Realtime API

GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…

4 years ago
  • Spring Boot

Spring Boot DynamoDB Integration Test using Testcontainers

1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…

4 years ago