The web request handling layer for Ext JS 4 clients is a JSON-generating proxy to the service layer interfaces. The domain entities are converted into JSON representations within this layer; so our first step is to create some helper code to make this task easier.
There are several excellent open source JSON generation projects that can assist in this task including Jackson (http://jackson.codehaus.org) and Google Gson (http://code.google.com/p/google-gson/). Such libraries parse POJOs into an appropriate JSON representation via their declared fields. With the release of Java EE 7, we no longer have a need for third-party libraries. The Java API for JSON Processing (JSR-353) is available in all Java EE 7-compliant application servers including GlassFish 4. We will leverage this API for generating and parsing JSON data.
Our first addition is a new domain interface:
package com.gieman.tttracker.domain; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; public interface JsonItem{ public JsonObject toJson(); public void addJson(JsonObjectBuilder builder); }
This very simple interface defines two methods to help with JSON generation. The toJson
method creates a JsonObject
that represents the entity. The addJson
method adds the entity properties to a JsonObjectBuilder
interface. We will see how these two methods are used very soon.
Each of our domain entities will need to implement the JsonItem
interface, and this can be achieved by simply adding the interface to the abstract superclass of all the domain entities:
package com.gieman.tttracker.domain; import java.io.Serializable; import java.text.SimpleDateFormat; import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; public abstract class AbstractEntity implements JsonItem, Serializable{ @Override public JsonObject toJson() { JsonObjectBuilder builder = Json.createObjectBuilder(); addJson(builder); return builder.build(); } }
The JsonObjectBuilder
interface defines a set of methods that add the name/value pairs to the JSON object associated with the builder. The builder
instance adds the fields defined in the descendent classes that implement the addJson
method. We will start with the Company
object.
The addJson
method that needs to be added to the Company
class is as follows:
@Override public void addJson(JsonObjectBuilder builder) { builder.add("idCompany", idCompany) .add("companyName", companyName); }
The JsonObject
representation of the Company
instance is created by calling the builder.build()
method in the superclass. The generated JsonObject
can then be written by a JsonWriter
instance to an output source.
The addJson
method that needs to be added to the Project
class is as follows:
@Override
public void addJson(JsonObjectBuilder builder) {
builder.add("idProject", idProject)
.add("projectName", projectName);
if(company != null){
company.addJson(builder);
}
}
Note that it is always a good practice to perform null
object tests before accessing the object methods. It is possible to create a project
object without a company
instance and hence we perform the company != null
test prior to adding the company
JSON properties to the project builder
instance. We could have used the following code to add the company
properties to the project builder
instance directly:
builder.add("idProject", idProject) .add("projectName", projectName) .add("idCompany", company.getIdCompany() ) .add("companyName", company.getCompanyName() );
However, we would now have replicated the builder.add("idCompany"…)
code across two classes (Company.addJson
and Project.addJson
), thus making the future maintenance prone to errors. Changing the JSON property name from idCompany
to companyId
, for example, would require the scanning of code to check for possible usage across all classes, not just the Company
class. The creation of Company
JSON should belong with the Company
class as we have implemented.
This Task
class will implement the addJson
method as follows:
@Override public void addJson(JsonObjectBuilder builder) { builder .add("idTask", idTask) .add("taskName", taskName); if(project != null){ project.addJson(builder); Company company = project.getCompany(); company.addJson(builder); } }
Note once again how we chain the call to addJson
for both the project
and company
classes to add their JSON properties to the task's builder
instance.
The User.addJson
method is defined as follows:
@Override public void addJson(JsonObjectBuilder builder) { builder.add("username", username) .add("firstName", firstName) .add("lastName", lastName) .add("email", email) .add("adminRole", adminRole + "") .add("fullName", firstName + " " + lastName); }
The fullName
property is for convenience only; we can just as easily create a fullName
field that concatenates the firstName
and lastName
fields in our Ext JS code. However, keeping this code at the source of the JSON generation allows for easier maintenance. Consider the business change request "add a middleName
field to the User
entity". The fullName
inclusion of the new middleName
field is then a trivial exercise and would be available to the Ext JS client without any further changes.
The addJson
method adds all of the TaskLog
fields to the builder
instance. The DATE_FORMAT_yyyyMMdd
constant is used to format the taskLogDate
to an 8-digit representation of the year/month/day and is added to the TaskLog
class as follows:
static final SimpleDateFormat DATE_FORMAT_yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
The addJson
method will use the SimpleDateFormat
instance to format the taskLogDate
field:
public void addJson(JsonObjectBuilder builder) { builder.add("idTaskLog", idTaskLog) .add("taskDescription", taskDescription) .add("taskLogDate", taskLogDate == null ? "" : DATE_FORMAT_yyyyMMdd.format(taskLogDate)) .add("taskMinutes", taskMinutes); if (user != null) { user.addJson(builder); } if (task != null) { task.addJson(builder); } }
The taskLogDate
field is being formatted in a way that cannot be misunderstood when converting to a JavaScript Date
object in Ext JS clients. Without the use of the SimpleDateFormat
instance, the builder
instance would call the default toString
method on the taskLogDate
object to retrieve the String representation, resulting in an output similar to the following:
Wed Aug 14 00:00:00 EST 2013
Using the SimpleDateFormat
instance configured with a date pattern of yyyyMMdd
will ensure that such a date is formatted to 20130814
.
Date formatting in enterprise applications can cause many issues if not approached with a standard strategy. This is even more applicable when we are developing applications to be used worldwide, with multiple timezones and different languages. The dates should always be formatted in a way that can be interpreted in the same way regardless of language, timezone, and user preferences.
We will be using JSON to transmit data between the GlassFish server and the Ext JS client. The transfer is bidirectional; the server will send the JSON data to the Ext JS client, and the Ext JS client will be sending the data in the JSON format back to the server. The server and client will consume and produce the JSON data.
There are no rules for structuring the JSON data as long as it conforms to the specifications (http://tools.ietf.org/html/rfc4627). Ext JS 4 models allow any form of valid JSON structure through the use of associations; our approach keeps the JSON structure to its simplest form. The previously defined addJson
methods return simple, flat data structures without nesting or arrays. As an example, a task
instance could be serialized into the following JSON object (formatting included for readability):
{ success: true, data: { "idTask": 1, "taskName": "Write Chapter 7", "idProject": 1, "projectName": "My Book Project", "idCompany": 1, "companyName": "PACKT Publishing" } }
The data
payload represents the task
object that will be consumed by the Ext JS 4 client. We could have defined the JSON representation of the task
object as follows:
{ success: true, data: { "idTask": 1, "taskName": "Write Chapter 7", "project": { "idProject": 1, "projectName": "My Book Project ", "company": { "idCompany": 1, "companyName": "PACKT Publishing" } } } }
In this structure we see that the task
instance belongs to a project
, which in turn belongs to a company
. Both these JSON representations are legal; they both contain the same task
data in valid JSON format. However, which of these two will be easier to parse? Which will be easier to debug? As an enterprise application developer we should always keep the KISS principle in mind. The Keep It Simple, Stupid (KISS)principle states that most systems work best if they are kept simple and unnecessary complexities should be avoided.
3.149.249.174