Day 30: Play Framework — A Java Developer Dream Framework

To celebrate the last day of my 30 day challenge, I decided to learn the Play framework. I wanted to write about Scala, but after a few hours of study realized I couldn’t do justice to Scala in one day. I plan to dedicate time to Scala in December and will share my experiences then. In this blog post we will look at the Play framework basics and then develop an application using it.

Play Framework logo

What is Play Framework?

Play is an open-source modern web framework for writing scalable web applications in Java and Scala. It enables rapid productivity by automatically reloading the changes. Play by design has a stateless and non-blocking architecture. This makes it easy to horizontally scale web applications written using the Play framework.

Why should I care?

My reasons for learning Play framework are :

  1. Developer Productivity : I have been programming in Java for last 8 years but from last few months I am spending more time with Python and JavaScript(Node.js). One thing that has amazed me most while working with dynamic languages is how quickly one can develop an application using dynamic languages. Both Java EE and Spring framework are not ideal choices for quick prototyping and rapid development. With Play framework you make a change, refresh the web page, and voila! you can see the change. It supports hot reload for all the Java code, templates, etc allows you to iterate much faster.

  2. Reactive by Nature : The Play framework is built on top of Netty, so it supports non-blocking I/O. It is very easy and inexpensive to make remote calls in parallel, which is important for high performance apps in a service oriented architecture.

  3. Supports both Java and Scala : Play framework is a true polyglot web framework. A developer can use both Java and Scala language in their project.

  4. First class REST JSON Support The Play framework makes it very easy to write RESTful applications. It has very good support for HTTP routing. HTTP routing translates HTTP requests into action calls. JSON marshalling/unmarshalling API is present in the core API so no need to add a library to do that.

Application Usecase

In this blog post, we will develop a social bookmarking application which allows users to post and share links. You can view the live application running on OpenShift here. This is the same application which we developed on day 22 so please refer to the blog to better understand the application usecase.

Installing Play

Please refer to the documentation to learn how to install Play framework on your machine.

Developing the Play Application

Now that we have covered the basics let’s start with the application development.

Go to any convenient location on the file system for the application and run the following command:

$ play new getbookmarks
 
       _
 _ __ | | __ _ _  _
| '_ \| |/ _' | || |
|  __/|_|\____|\__ /
|_|            |__/
 
play 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_25), http://www.playframework.com
 
The new application will be created in /Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks
 
What is the application name? [getbookmarks]
> 
 
Which template do you want to use for this new application? 
 
  1             - Create a simple Scala application
  2             - Create a simple Java application
 
> 2
OK, application getbookmarks is created.
 
Have fun!

As shown above, after typing the command, the play framework asks couple of questions. First it asks us the name of the application and then it asks us whether we want to create a Scala application or Java application. By default, it used folder name as the application name. We choose option 2 to create a Java application.

The above command creates a new directory getbookmarks and generate following files and directories.

  1. The app directory contains the application specific code like controllers, views, and models. The controllers package contains Java code that will respond to url routes. The views directory contains server side templates. The models directory will contain application domain model. In this application, domain will be a Story class.

  2. The conf directory contains application configuration and the routes definition files.

  3. The project directory contains the build scripts. The build system is based on sbt.

  4. The public contains our public assets like css , javascript, and images directory.

  5. The test directory contains application tests.

Now we can run the default application created by Play by launching the play console by running the play command and then using the run command.

$ cd getbookmarks
$ play
[info] Loading project definition from /Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks/project
[info] Set current project to getbookmarks (in build file:/Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks/)
       _
 _ __ | | __ _ _  _
| '_ \| |/ _' | || |
|  __/|_|\____|\__ /
|_|            |__/
 
play 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_25), http://www.playframework.com
 
> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.
 
[getbookmarks] $ run
[info] Updating {file:/Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks/}getbookmarks...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
 
--- (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...)

Now we can the application running at http://localhost:9000.

Play Framework logo

Create Story domain class

In this application, we only have one domain class called Story. Create a new package models and then create a new Java class.

package models;
 
import play.db.ebean.Model;
 
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
 
 
@Entity
public class Story extends Model{
 
    @Id
    private String id;
 
    private String url;
 
    private String fullname;
 
    private Date submittedOn = new Date();
 
    private String title;
    private String text;
    private String image;
 
 
    public Story() {
 
    }
 
    public Story(String url, String fullname) {
        this.url = url;
        this.fullname = fullname;
    }
 
    public Story(String url, String fullname, String image, String text, String title) {
        this.url = url;
        this.fullname = fullname;
        this.title = title;
        this.text = text;
        this.image = image;
    }
 
   // Getter and Setter removed for brevity
}

The code shown above defines a simple JPA entity and used @Entity and @Id JPA annotations. Play uses its own ORM layer called Ebean and every entity class has to extend the base Model class.

Ebean is disabled by default. To enable it, open the application.conf and uncomment following line.

ebean.default="models.*"

Enable Database

Let us now enable database in our application. Play framework provides in-built support for H2 database. To enable it open the application.conf file and uncomment following two lines.

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

Now refresh the browser and you will get following exception.

Play Framework logo

Click on Apply this script now to apply the SQL changes.

Define Application Routes

In this blog, we are developing the same application we developed on day 22. The application has AngularJS backend and consumes the REST backend. We will write the REST backend using Play framework and reuse the AngularJS backend. In the conf/routes file, copy and paste the code shown below.

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
 
# Home page
GET         /                           controllers.Assets.at(path="/public", file="/index.html")
GET         /api/v1/stories             controllers.StoryController.allStories()
POST        /api/v1/stories             controllers.StoryController.submitStory()
GET         /api/v1/stories/:storyId    controllers.StoryController.getStory(storyId)
 
# Map static resources from the /public folder to the /assets URL path
GET         /assets/*file        controllers.Assets.at(path="/public", file)

The routes shown above :

  1. When a user makes a GET request to the ‘/’ url of the application, then index.html will be rendered.
  2. When a user makes a GET request to ‘/api/v1/stories’, then user will get all the stories in JSON format.
  3. When a user makes POST request goes to ‘/api/v1/stories’, a new story will be created.
  4. When a user makes ‘GET’ request to ‘/api/v1/stories/123’, then story with id 123 will be rendered.

Create StoryController

Now create a new Java class in controllers package. Copy and paste the code shown below in StoryController.java file.

package controllers;
 
 
import com.fasterxml.jackson.databind.JsonNode;
import models.Story;
import play.api.libs.ws.Response;
import play.api.libs.ws.WS;
import play.db.ebean.Model;
import play.libs.Json;
import play.mvc.BodyParser;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Results;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
 
import java.util.List;
import java.util.concurrent.TimeUnit;
 
public class StoryController {
 
    public static Result allStories(){
        List<Story> stories = new Model.Finder<String , Story>(String.class, Story.class).all();
        return Results.ok(Json.toJson(stories));
    }
 
    @BodyParser.Of(BodyParser.Json.class)
    public static Result submitStory(){
        JsonNode jsonNode = Controller.request().body().asJson();
        String url = jsonNode.findPath("url").asText();
        String fullname = jsonNode.findPath("fullname").asText();
        JsonNode response = fetchInformation(url);
        Story story = null;
        if(response == null){
            story = new Story(url,fullname);
        }else{
            String image = response.findPath("image").textValue();
            String text = response.findPath("text").textValue();
            String title = response.findPath("title").textValue();
            story = new Story(url,fullname, image , text , title);
        }
        story.save();
        return Results.created();
    }
 
    public static Result getStory(String storyId){
        Story story = new Model.Finder<String, Story>(String.class, Story.class).byId(storyId);
        if(story == null){
            return Results.notFound("No story found with storyId " + storyId);
        }
        return Results.ok(Json.toJson(story));
    }
 
    private static JsonNode fetchInformation(String url){
        String restServiceUrl = "http://gooseextractor-t20.rhcloud.com/api/v1/extract?url="+url;
        Future<Response> future = WS.url(restServiceUrl).get();
        try {
            Response result = Await.result(future, Duration.apply(30, TimeUnit.SECONDS));
            JsonNode jsonNode = Json.parse(result.json().toString());
            return jsonNode;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
 
    }
 
}

The code shown above does the following :

  1. It defines allStories() method which find all the stories from the database. It uses the Model.Finder API to do that. Then, we convert the list of stories into JSON format and return the result. We return HTTP status code 200 i.e. OK.

  2. The submitStory() method first read the url and fullname fields from the JSON. Then we make a GET request to ‘http://gooseextractor-t20.rhcloud.com/api/v1/extract?url’, which find the title, excerpt, and the main image for the given url. We create a new story using all the information and save it in the database. We return HTTP status code 201 i.e. Created.

  3. The getStory() method fetches the story for the given storyId. We convert the story into JSON format and return back in the response.

AngularJS Front End

I decided to reuse the AngularJS front end which we wrote on day 22. The day 22 showcased how we can use AngularJS with Java Spring framework backend. The best part of using JavaScript MV* frameworks is that you can reuse the frontend code if your application stick to the REST interface client needs. Please read the day 22 blog for more information.

You can download the AngularJS front end from my github repository. Replace the public directory with the one in the repository.

Now refresh the browser and access the application at http://localhost:9000/ in your browser.

Play Framework logo

That’s it for today. Hope you enjoyed this series.

Next Steps

Automatic Updates

Stay informed and learn more about OpenShift by receiving email updates.

Categories
Java, OpenShift Online
Tags
,
Comments are closed.