Editor’s note: We found an interesting series of articlesLearn 30 New Technologies in 30 Days, is being translated, an update a day, year-end gift package. Here’s Day 30.
Today is the last day and I decided to learn about the Play framework. I originally wanted to write about Scala, but after studying it for a few hours, I realized that it was impossible to write Scala in one day. So today I will cover the basics of the Play framework, and then learn how to use it to develop applications.
What is the Play Framework?
Play is an open source modern Web framework for writing extensible Web applications in Java and Scala. It increases productivity by automatically overloading changes, and because it is designed to be a stateless, non-blocking architecture, it is easy to write scale-out Web applications using the Play framework.
Why use it?
My reasons are as follows:
-
Developer Productivity: I’ve been writing Java for 8 years, but in the last few months I’ve spent more time working with Python and JavaScript (Node.js). One of the things that surprised me most about working with dynamic languages was how fast I could program them. Java EE and the Spring framework are not ideal for rapid prototyping and development, but with the Play framework, updates appear immediately when you refresh a page with a change, and it supports hot-reloading all Java code, templates, etc., which makes your iterations much faster.
-
By nature: The Play framework is built on top of Netty, so it supports non-blocking I/O, which makes parallel remote calls much easier, which is important for high-performance applications in service-oriented architectures.
-
Support for Java and Scala: The Play framework is a truly multilingual Web framework that allows developers to use both Java and Scala in their projects.
-
First-class REST JSON support: It’s easy to write REST-based applications. There is good support for HTTP routing, which translates HTTP requests into concrete actions. The JSON marshalling/unmarshalling API is currently the core API, so there is no need to add a library to do this.
Application type cases
In today’s introduction, you’ll develop a social bookmarking application that allows users to post and share links. You can see the application in action here, as this is the same application from Day 22, so please see it to get a better understanding of the case.
Developing the Play app
Refer to the documentation to learn how to install the Play framework and start developing your application.
$play new getbookmarks _ _ __ | | __ _ _ _ | '_ \ | | / _' | | | | | __ there comes / | _ | \ | \ __ / | _ | | __ / 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!
After typing the command above, the framework asks a few questions. It first asks for the name of the application, and then asks if you want to create a Scala or Java application. By default, it uses the folder name as the name of the application.
The above command will create a new directory getbookmarks and generate the following files and directories:
- The app directory contains application-specific code such as controllers, views, and models. The controller package contains the Java code that responds to the URL route, the view directory contains the server-side templates, and the model directory contains the application’s domain model. In this application, a domain is a Story class.
- The conf directory contains the application configuration and routing definition files.
- The project directory contains the build scripts, and the build system is based on SBT.
- PUBLIC contains common resources such as CSS, JavaScript, and IMG directories.
- The test directory contains application tests.
Publish the PLAY console with the following command to run the default program written by PLAY.
$ 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/) _ _ __ | | __ _ _ _ | '_ \ | | / _' | | | | | __ there comes / | _ | \ | \ __ / | _ | | __ / 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...)
You can now go tohttp://localhost:9000
The application is now running in.
Create the Story domain class
The application has a single domain class, called Story, which creates a new package model and 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 above defines a simple JPA Entity with @Entity and @ID JPA annotations, Play uses its own ORM layer called eBean, and each Entity class must extend the base model class.
EBean is disabled by default. Enabling it requires opening Application.conf and uncommenting the following line.
ebean.default="models.*"
Enable the database
Starting the application’s database, the Play framework provides built-in H2 database support. To enable it, open the application.conf file and uncomment the following two lines.
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
Refresh your browser and you’ll see:
Click on theApply this script now
Deploy the SQL changes on it.
Define the routing of the application
So you can rewrite the REST backend and the AngularJS backend using the Play framework. In the conf/routes file, copy and paste the following code.
# 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 above code represents:
- When the user issues a GET request to the application
The URL "/"
.index.html
Will be rendered. - When the user issues a GET request
'/ api/v1/stories'
, you get all the stories in JSON format. - When the user sends a POST request to
'/ api/v1/stories'
, a new Story will be created. - When a user gets a request
'/ api/v1/stories/123'
, the story with id 123 will be rendered.
Creating a Story Controller
Create a Java class in the controller package and paste the following code into the 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 above code will do:
-
It defines the allStories() method, which finds all the stories in the database. It does this using the Model.Finder API, then converts the list of stories to JSON format and returns the result, with an HTTP status code of 200 (that is, OK).
-
SubmitStory () method will first read from the JSON URL and the full name of the field, and then send a GET request to the ‘http://gooseextractor-t20.rhcloud.com/api/v1/extract?url’, This will find the title, the summary, and the main image for which the URL has been given. Create a story that uses all the information and store it in the database, returning the HTTP status code 201 (that is, create).
-
The getStory() method gets the Story for the given StoryID, converts the Story into JSON format, and returns the response.
You can download the AngularJS front end from my GitHub repository and replace the public directory with one of the libraries.
You can now visit http://localhost:9000/ to see the results.
This is the end of the 30-day series. Thank you very much for your continued attention and I hope you can grow with SegmentFault.
So, see you next year. Oh~ Happy New Year to Y’all !
Day 30: Play Framework–A Java Developer Dream Framework