Here we implement an API. It comprises a set of routes (the end points), controllers for these routes together with the means of translating Java objects to and from the Json format.
Using Play framework, create a new project: myrent-service-2016
.
Generate an Eclipse view of the project:
cd myrent-service-2016
play deps
play eclipsify
Launch Eclipse and open (or create) a suitable workspace.
Then, import the project.
In a models
package, add this class:
package models;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import play.db.jpa.GenericModel;
@Entity
public class Residence extends GenericModel {
@Id
public Long id;
public String geolocation;
public Long date;
public boolean rented;
public String tenant;
public double zoom;
public String photo;
public Residence() {
id = unsignedLong();
geolocation = "";
date = 0L;
rented = false;
tenant = "";
zoom = 0;
photo = "";
}
public static Residence findById(Long id) {
return find("id", id).first();
}
/**
* Generate a long greater than zero
*
* @return Unsigned Long value greater than zero
*/
private Long unsignedLong() {
long rndVal = 0;
do {
rndVal = new Random().nextLong();
} while (rndVal <= 0);
return rndVal;
}
}
Create a new package called utils
. Create this class in utils
.
package utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import play.data.binding.Global;
import play.data.binding.TypeBinder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
@Global
public class GsonBinder implements TypeBinder<JsonElement>
{
public Object bind(String name, Annotation[] notes, String value, Class toClass, Type toType) throws Exception
{
return new JsonParser().parse(value);
}
}
In the controllers package add a class ResidencesAPI
:
package controllers;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import models.Residence;
import play.mvc.Controller;
public class ResidencesAPI extends Controller {
static Gson gson = new GsonBuilder().create();
/**
*
* @param id
* @param body
*/
public static void createResidence(JsonElement body) {
Residence residence = gson.fromJson(body.toString(), Residence.class);
residence.save();
renderJSON(gson.toJson(residence));
}
/**
* This is an update and differs from createResidence in that the primary key
* (id) is used to retrieve the original residence which is then deleted and
* its place taken by the incoming modified residence.
*
* @param body
* The modified residence
*/
public static void updateResidence(JsonElement body) {
Residence modifiedResidence = gson.fromJson(body.toString(), Residence.class);
Residence residence = Residence.findById(modifiedResidence.id);
if (residence != null) {
modifiedResidence.id = residence.id;
residence.delete();
modifiedResidence.save();
renderJSON(gson.toJson(modifiedResidence));
} else {
notFound();
}
}
public static void getResidence(Long id) {
Residence residence = Residence.findById(id);
if (residence == null) {
notFound();
} else {
renderJSON(gson.toJson(residence));
}
}
public static void getResidences() {
List<Residence> residences = Residence.findAll();
renderJSON(gson.toJson(residences));
}
/**
*
* @param id
* @param residenceId
*/
public static void deleteResidence(Long id) {
Residence residence = Residence.findById(id);
if (residence == null) {
notFound();
} else {
residence.delete();
renderText("success");
}
}
}
Enable our in-memory database by uncommenting the usual setting in 'application.conf'
db.default=mem
Introduce the 'Bootstrap' java class directly into the 'app' folder:
import java.util.List;
import play.jobs.*;
import play.test.*;
import models.*;
@OnApplicationStart
public class Bootstrap extends Job
{
public void doJob()
{
if (Residence.count() == 0)
{
Fixtures.deleteDatabase();
Fixtures.loadModels("data.yml");
}
}
}
Important: once this service app has been completed and tested, comment out the above doJob
method before
running the JUnit tests. Otherwise the tests will likely fail as the expected number of records in the database will differ from that expected in the test code.
Now, specify additional routs in config/routes
file. Here is the complete version:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / Application.index
# Residence
POST /api/residence ResidencesAPI.createResidence
GET /api/residences ResidencesAPI.getResidences
GET /api/residences/{id} ResidencesAPI.getResidence
DELETE /api/residences/{id} ResidencesAPI.deleteResidence
POST /api/residence/update ResidencesAPI.updateResidence
# Ignore favicon requests
GET /favicon.ico 404
# Map static resources from the /app/public folder to the /public path
GET /public/ staticDir:public
# Catch all
* /{controller}/{action} {controller}.{action}
Provide some sample data.
Residence(residence_1):
id: 1234
geolocation: "52.258136,-7.127810"
date: 1448196498688
rented: true
tenant: Homer
zoom: 12.0
photo: "photo 1"
Residence(residence_2):
id: 1235
geolocation: "53.258136,-7.127810"
date: 1448196498765
rented: true
tenant: Barney
zoom: 14.0
photo: "photo 2"
Residence(residence_3):
id: 1236
geolocation: "52.258136,-8.127810"
date: 1448196498999
rented: true
tenant: Marge
zoom: 16.0
photo: "photo 3"
Residence(residence_4):
id: 1237
geolocation: "52.258136,-7.127810"
date: 1448196498666
rented: true
tenant: Lisa
zoom: 18.0
photo: "photo 4"
The program should now build without error. Launch it and browse to the database and study the contents:
localhost:9000/@db
If you are having difficulty launching the h2 browser with the above command, use Postman.
Also, in a browser explore the urls:
http://localhost:9000/api/residences
http://localhost:9000/api/residences
Launch Postman from within Chrome:
Explore the GET and DELETE HTTP commands. Here in Figure 4 is an example of the use of GET.
Using Postman:
Create a JSON version of a Residence object and POST it.
Delete a Residence object.
Update a Residence object.