A Web Application (JSF) consumes JSON

In this tutorial, we’ll build a simple website using JSF web framework to consume the following API. The website displays a form in which the fields are mapped to the bean and added as URL parameters. If nothing wrong happened the results will be displayed in a table, else a result not found page will pop-up.

Prerequisite


  1. JDK 8.
  2. Maven 3+
  3. Tomcat v8+
  4. JSF 2.x
  5. HTTPClient 4.x
  6. JQuery 3.x
  7. JQuery-ui

 

Create a new project


First of all let’s create a new dynamic web application using eclipse, by fallowing the screen-shots below:
1. Go to File -> New -> Dynamic web application

JSFProject

2. Make sure to check “generate web.xml deployment descriptor”

JSF

3. Right-click on the project -> configure -> convert to Maven

Dependencies

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ExpediaExercise</groupId>
<artifactId>ExpediaExercise</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.github.jsimone</groupId>
<artifactId>webapp-runner</artifactId>
<version>8.5.11.2</version>
<destFileName>webapp-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.faces/jsf-impl -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.faces/jsf-api -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-servlet-core -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.25</version>
</dependency>
</dependencies>
</project>

 

WEB-INF/web.xml


web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>JSFExercise</display-name>
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
<url-pattern>*.jsf</url-pattern>
<url-pattern>*.xhtml</url-pattern>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<context-param>
<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
</web-app>

 

Resources


Now its time to create our resources folder, which will include both default CSS and JS files, to handle application’s styling and related JavaScript.

  1. Create: WebContent/webapp/resources folder
  2. Create: WebContent/webapp/resources/js and WebContent/webapp/resources/css folders
  3. Add default.js to your js folder
  4. Add default.css to your css folder

default.js: Handles date’s related events for date fields in the form.
Assumptions:

  • If min and max start date are filled, travel start and end date will be disabled.
  • First date picker can’t be in the past (Min start date > now).
  • Second date picker is dependent on the first date picker (second date >= first date).

$(document).ready(function() {
$(".datepickerMax").attr("disabled", "disabled");
$(".datepickerMin").datepicker({
dateFormat : 'yy-mm-dd',
minDate : 0,
onSelect : function() {
$(".datepickerMax").removeAttr("disabled");
$(".travelStartDate").attr("disabled", "disabled");
$(".travelEndDate").attr("disabled", "disabled");
}

});

$(".datepickerMax").focus(function() {
$(this).datepicker({
showOn : 'button',
dateFormat : 'yy-mm-dd',
minDate : $('.datepickerMin').datepicker('getDate')
});
});

$(".travelStartDate").datepicker({
dateFormat : 'yy-mm-dd',
minDate : 0,
onSelect : function() {
$(".datepickerMax").attr("disabled", "disabled");
$(".datepickerMin").attr("disabled", "disabled");
}

});

$(".travelEndDate").focus(function() {
$(this).datepicker({
showOn : 'button',
dateFormat : 'yy-mm-dd',
minDate : $('.travelStartDate').datepicker('getDate')
});
});

});

 

default.css: Handles application styling.


h1{
width:300px;
margin-right:30px;
text-align:right;
}

.btn-submit{
margin-left:250px;
margin-top: 10px;
}

label{
display:inline-block;
width:200px;
margin-right:30px;
text-align:right;
}

div{
padding-top: 10px;
}

input{
border-bottom-color: #b3b3b3;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
border-bottom-style: solid;
border-bottom-width: 1px;
border-left-color: #b3b3b3;
border-left-style: solid;
border-left-width: 1px;
border-right-color: #b3b3b3;
border-right-style: solid;
border-right-width: 1px;
border-top-color: #b3b3b3;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border-top-style: solid;
border-top-width: 1px;`
}

.deal-table{
border-collapse:collapse;
}

.deal-table-header{
text-align:center;
background:none repeat scroll 0 0 #E5E5E5;
border-bottom:1px solid #BBBBBB;
padding:16px;
}

.odd-row{
text-align:center;
background:none repeat scroll 0 0 #FFFFFFF;
border-top:1px solid #BBBBBB;
}

.even-row{
text-align:center;
background:none repeat scroll 0 0 #F9F9F9;
border-top:1px solid #BBBBBB;
}

 

WebContent pages


Main xhtml pages that will be used in our website:

Header

Now we’ll create a header.xhtml to include our scripts, style-sheets, title and other common elements all in one place.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>ConsumeREST Exercise</title>
<link rel="stylesheet" href="webapp/resources/css/jquery-ui.css" />
<link rel="stylesheet" href="webapp/resources/css/default.css" />
http://webapp/resources/js/jquery-3.1.1.js
<script type="text/javascript" src="webapp/resources/js/default.js" />
</h:head>
</ui:composition>

 

 Index

Home page or index.xhtml displays the form from which the fields are obtained.

Source-Code For index.xhtml

404 page

Not found page that is displayed whenever an unexpected event (exception) occurs.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">

<!--header-->
<ui:include src="header.xhtml"/>
<body>
<h1>No Result Found</h1>
</body>
</html>

 

Result page

result.xhtml Displays results retrieved from the web-service in a table.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<!--header-->
<ui:include src="header.xhtml" />
<!--header-->
<h:body>
<h:dataTable value="#{tripBean.offerDateRangeList}" var="list"
styleClass="deal-table"
headerClass="deal-table-header"
rowClasses="odd-row,even-row">

<h:column>
<!-- column header -->
<f:facet name="header">Travel Start Date</f:facet>
<!-- row record -->
#{list.travelStratDate}
</h:column>

<h:column>
<f:facet name="header">Travel End Date</f:facet>
#{list.travelEndDate}
</h:column>

<h:column>
<f:facet name="header">Length Of Stay</f:facet>
#{list.length}
</h:column>
</h:dataTable>
</h:body>
</html>

 

JavaBean


A trip bean registered with JSF to represent a trip and encapsulate its details.


package com.jsf.rest.exercise;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

import org.json.JSONArray;
import org.json.JSONException;

import java.io.IOException;
import java.io.Serializable;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
@ManagedBean
@SessionScoped
public class TripBean implements Serializable {

// verify sender/receiver of the serialized object have loaded compatible
// classes
private static final long serialVersionUID = 1L;

/* Members-begin */
private String destinationName;
private String minTripStartDate;
private String maxTripStartDate;
private int maxStarRating;
private int minStarRating;
private int maxTotalRate;
private int minTotalRate;
private int maxGuestRating;
private int minGuestRating;
private String travelStartDate;
private String travelEndDate;
private List<OfferDateContent> offerDateRangeList;
/* Members-end */

public String submit() {

JSONArray json = null;

try {
List<OfferDateContent> offerDateRangeArrayList = new ArrayList<>();
TripDetails detailsService = new TripDetailsImpl();
String apiUrl = "https://radiant-escarpment-30246.herokuapp.com/rest/trip/details";
// consume JSON

json = JsonConsumer.readJsonFromUrl(detailsService.getDetailsUrl(apiUrl, getDestinationName(),
getMinTripStartDate(), getMaxTripStartDate(), getMinStarRating(), getMaxStarRating(),
getMinGuestRating(), getMaxGuestRating(), getMinTotalRate(), getMaxTotalRate(),
getTravelStartDate(), getTravelEndDate()));
System.out.println(json.toString());
// get required data
OfferDateContent content = detailsService.getTripDetails(json);
offerDateRangeArrayList.add(content);
setOfferDateRangeList(offerDateRangeArrayList);

// reduce code duplication by catching multiple exceptions
} catch (IOException | JSONException | URISyntaxException | NullPointerException e) {
e.printStackTrace();
// redirect to not found page
return "notfound.xhtml";
}
return "result.xhtml";
}

/*
* Encapsulated fields
*/

public void setOfferDateRangeList(List<OfferDateContent> offerDateRangeList) {
this.offerDateRangeList = offerDateRangeList;
}

public List<OfferDateContent> getOfferDateRangeList() {
return offerDateRangeList;
}

public String getTravelStartDate() {
return travelStartDate;
}

public void setTravelStartDate(String travelStartDate) {
this.travelStartDate = travelStartDate;
}

public String getTravelEndDate() {
return travelEndDate;
}

public void setTravelEndDate(String travelEndDate) {
this.travelEndDate = travelEndDate;
}

public String getDestinationName() {
return destinationName;
}

public void setDestinationName(String destinationName) {
this.destinationName = destinationName;
}

public String getMinTripStartDate() {
return minTripStartDate;
}

public void setMinTripStartDate(String minTripStartDate) {
this.minTripStartDate = minTripStartDate;
}

public String getMaxTripStartDate() {
return maxTripStartDate;
}

public void setMaxTripStartDate(String maxTripStartDate) {
this.maxTripStartDate = maxTripStartDate;
}

public int getMaxStarRating() {
return maxStarRating;
}

public void setMaxStarRating(int maxStarRating) {
this.maxStarRating = maxStarRating;
}

public int getMinStarRating() {
return minStarRating;
}

public void setMinStarRating(int minStarRating) {
this.minStarRating = minStarRating;
}

public int getMaxTotalRate() {
return maxTotalRate;
}

public void setMaxTotalRate(int maxTotalRate) {
this.maxTotalRate = maxTotalRate;
}

public int getMinTotalRate() {
return minTotalRate;
}

public void setMinTotalRate(int minTotalRate) {
this.minTotalRate = minTotalRate;
}

public int getMaxGuestRating() {
return maxGuestRating;
}

public void setMaxGuestRating(int maxGuestRating) {
this.maxGuestRating = maxGuestRating;
}

public int getMinGuestRating() {
return minGuestRating;
}

public void setMinGuestRating(int minGuestRating) {
this.minGuestRating = minGuestRating;
}

/*
* End of Encapsulated fields
*/

public static class OfferDateContent {
String travelStratDate;
String travelEndDate;
String length;

public OfferDateContent(String travelStratDate, String travelEndDate, String length) {
super();
this.travelStratDate = travelStratDate;
this.travelEndDate = travelEndDate;
this.length = length;
}

public OfferDateContent() {
super();
}

public String getTravelStratDate() {
return travelStratDate;
}

public void setTravelStratDate(String travelStratDate) {
this.travelStratDate = travelStratDate;
}

public String getTravelEndDate() {
return travelEndDate;
}

public void setTravelEndDate(String travelEndDate) {
this.travelEndDate = travelEndDate;
}

public String getLength() {
return length;
}

public void setLength(String length) {
this.length = length;
}
//Assumption -> equality is based on length
@Override
public boolean equals(Object obj) {
if ((obj == null) || (getClass() != obj.getClass())) {
return true;
}
OfferDateContent offer = (OfferDateContent) obj;
if (offer.getLength().equals(this.length))
return true;

return false;
}

@Override
public int hashCode() {
return super.hashCode();
}

}

}

 

JSONConsumer


A class to consume JSON from an external web-service:


package com.jsf.rest.exercise;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;

import org.json.JSONArray;
import org.json.JSONException;
public class JsonConsumer {

private static String readAll(Reader reader) throws IOException {
StringBuilder sb = new StringBuilder();
int c;
//read to the end
while ((c = reader.read()) != -1) {
sb.append((char) c);
}
return sb.toString();
}

public static JSONArray readJsonFromUrl(String url) throws IOException, JSONException {
InputStream is = new URL(url).openStream();
//try-with resources to ensures that the resource is closed
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")))) {

String jsonStr = readAll(bufferedReader);
JSONArray json = new JSONArray(jsonStr);
return json;
}
}
}

 

TripDetails


The fallowing are made with the assumption that there will be a number of consumers for TripDetails and the general form has revealed itself, read more about premature abstraction here.

  1. TripDetails interface: contain two abstract methods.
  2. TripDetailsImpl: implementation for the methods above.

TripDetails.java

package com.jsf.rest.exercise;

import java.net.URISyntaxException;

import org.json.JSONArray;
import org.json.JSONException;

import com.jsf.rest.exercise.TripBean.OfferDateContent;

public interface TripDetails {

String getDetailsUrl(String apiUrl, String destinationName, String minTripStartDate, String maxTripStartDate,
int minStarRating, int maxStarRating, int minGuestRating, int maxGuestRating, int minTotalRate,
int maxTotalRate, String travelStartDate, String travelendDate) throws URISyntaxException;

OfferDateContent getTripDetails(JSONArray json) throws JSONException;
}

 

TripDetailsImpl.java

package com.jsf.rest.exercise;

import java.net.URISyntaxException;

import org.apache.http.client.utils.URIBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.jsf.rest.exercise.TripBean.OfferDateContent;

public class TripDetailsImpl implements TripDetails{

@Override
public String getDetailsUrl(String apiUrl, String destinationName, String minTripStartDate, String maxTripStartDate,
int minStarRating, int maxStarRating, int minGuestRating, int maxGuestRating, int minTotalRate,
int maxTotalRate, String travelStartDate, String travelendDate) throws URISyntaxException {
/*
* Add URL parameters
*/

URIBuilder uriBuilder = new URIBuilder(apiUrl);
if (destinationName != null && !destinationName.isEmpty() )
uriBuilder.addParameter("destinationName", destinationName);
if (minTripStartDate != null && !minTripStartDate.isEmpty() ) {
uriBuilder.addParameter("minTripStartDate", travelStartDate);
// avoid NPE as the field might be disabled
if (maxTripStartDate != null && !maxTripStartDate.isEmpty())
uriBuilder.addParameter("maxTripStartDate", maxTripStartDate);
}
if (minStarRating > 0 && minStarRating <= 10)
uriBuilder.addParameter("minStarRating", String.valueOf(minStarRating));
if (maxStarRating > 0 && maxStarRating <= 10)
uriBuilder.addParameter("maxStarRating", String.valueOf(maxStarRating));
if (minGuestRating > 0 && minGuestRating <= 10)
uriBuilder.addParameter("minGuestRating", String.valueOf(minGuestRating));
if (maxGuestRating > 0 && maxGuestRating <=10)
uriBuilder.addParameter("maxGuestRating", String.valueOf(maxGuestRating));
if (minTotalRate > 0 && minTotalRate <= 10)
uriBuilder.addParameter("minTotalRate", String.valueOf(minTotalRate));
if (maxTotalRate > 0 && maxTotalRate <= 10)
uriBuilder.addParameter("maxTotalRate", String.valueOf(maxTotalRate));
if (minTripStartDate ==null || minTripStartDate.isEmpty() ) {
if (travelStartDate != null && !travelStartDate.isEmpty())
uriBuilder.addParameter("travelStartDate", travelStartDate);
if (travelendDate != null &&!travelendDate.isEmpty())
uriBuilder.addParameter("travelendDate", travelendDate);
}
return uriBuilder.toString();
}

@Override
public OfferDateContent getTripDetails(JSONArray json) throws JSONException{

/*
* Loop around the JSON response
*/

OfferDateContent content = new OfferDateContent();
for (Object obj : json) {
JSONObject jsonObj = (JSONObject) obj;
content.setLength(jsonObj.getString("length"));
content.setTravelStratDate(jsonObj.getString("travelStratDate"));
content.setTravelEndDate(jsonObj.getString("travelEndDate"));
}
return content;
}

}

 

Live Demo

 

 

Download or Clone


RESTful download

Share

Comments

comments

Leave a Reply

Your email address will not be published. Required fields are marked *