Skip to content

10. Version 5 - PAM Web / JSF Application

10.1. Application Architecture

The architecture of the PAM web application will be as follows:

In this version, the GlassFish server will host all layers of the application:

  • the [web] layer is hosted by the server’s servlet container (1 below)
  • the other layers [business logic, DAO, JPA] are hosted by the server’s EJB3 container (2 below)

The [business logic, DAO] components of the application running in the EJB3 container have already been implemented in the client/server application discussed in Section 7.1, which had the following architecture:

The [business logic, DAO] layers ran in the GlassFish server’s EJB3 container, and the [UI] layer ran in a console or Swing application on another machine:

In the architecture of the new application:

only the [web / jsf] layer needs to be written. The other layers [business, DAO, JPA] are already in place.

In document [ref3], it is shown that a web application where the web layer is implemented with Java Server Faces has an architecture similar to the following:

This architecture implements the MVC (Model, View, Controller) design pattern. The processing of a client request proceeds as follows:

If the request is made using a GET, the following two steps are executed:

  1. request - the client browser sends a request to the controller [Faces Servlet]. The controller handles all client requests. It is the application’s entry point. It is the C in MVC.
  2. response - the C controller instructs the selected JSF page to render. This is the view, the V in MVC. The JSF page uses an M model to initialize the dynamic parts of the response it must send to the client. This model is a Java class that can call upon the [business] layer [4a] to provide the V view with the data it needs.

If the request is made via a POST, two additional steps occur between the request and the response:

  1. request - the browser client makes a request to the [Faces Servlet] controller.
  2. processing - the C controller processes this request. Indeed, a POST request is accompanied by data that must be processed. To do this, the controller is assisted by event handlers specific to the application [2a]. These handlers may require the business layer [2b]. The event handler may need to update certain M models [2c]. Once the client’s request has been processed, it may trigger various responses. A classic example is:
    • an error page if the request could not be processed correctly
    • a confirmation page otherwise

The event handler returns a string-type result called a navigation key to the controller [Faces Servlet].

  1. navigation - the controller selects the JSF page (= view) to send to the client. This selection is based on the navigation key returned by the event handler.
  2. response - the selected JSF page sends the response to the client. It uses its M model to initialize its dynamic parts. This model can also call upon the [business] layer [4a] to provide the JSF page with the data it needs.

In a JSF project:

  • the controller C is the [javax.faces.webapp.FacesServlet] servlet. This is found in the [jsf-api.jar] library.
  • The V views are implemented by JSF pages.
  • The M models and event handlers are implemented by Java classes often called "backing beans".
  • In JSF 1.x versions, bean definitions and the rules for navigating from one page to another are defined in the [faces-config.xml] file. It contains the list of views and the rules for transitioning from one to another. Starting with JSF version 2, bean definitions can be made using annotations, and page transitions can be hard-coded directly in the bean code.

10.2. How the application works

When the application is requested for the first time, the following page appears:

You then fill out the form and request the salary:

The following result is displayed:

This version calculates a fictitious salary. Do not pay attention to the page’s content, but rather to its layout. When you click the [Reset] button, you return to page [A].

Incorrect entries are flagged, as shown in the following example:

10.3. The NetBeans Project

We will build an initial version of the application where the [business] layer will be simulated. We will have the following architecture:

When event handlers or models request data from the [business] layer [2b, 4a], it will provide them with fictitious data. The goal is to obtain a web layer that responds correctly to user requests. Once this is achieved, all that remains is to install the server layer developed in Section 7.1:

This will be version 2 of the web version of our PAM application.

The NetBeans project for version 1 is the following Maven project:

  • in [1], the configuration files
  • in [2], the XHTML pages and the stylesheet
  • in [3], the [web] layer classes
  • in [4], the objects exchanged between the [web] layer and the [business] layer, and the [business] layer itself
  • in [5], the message file for the application's internationalization
  • in [6], the application dependencies

We will review some of these elements.

10.3.1. Configuration files

The [web.xml] file is the one generated by default by NetBeans, along with the configuration for an exception page:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>  
  <context-param>
    <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
  </context-param> 
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
  <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>
  </servlet-mapping>
  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
  <welcome-file-list>
    <welcome-file>faces/index.xhtml</welcome-file>
  </welcome-file-list>
  <error-page>
    <error-code>500</error-code>
    <location>/faces/exception.xhtml</location>
  </error-page>
  <error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/faces/exception.xhtml</location>
  </error-page>
</web-app>
  • line 30: [index.html] is the application's home page
  • lines 32–39: configuration of the exception page

The [exception.html] page is taken from [ref3]. Its code is as follows:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.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">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="form">
        <h3><h:outputText value="#{msg['exception.header']}"/></h3>
        <h:panelGrid columnClasses="col1,col2" columns="2" border="1">
          <h:outputText value="#{msg['exception.httpCode']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.status_code']}"/>
          <h:outputText value="#{msg['exception.message']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.exception']}"/>
          <h:outputText value="#{msg['exception.requestUri']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.request_uri']}"/>
          <h:outputText value="#{msg['exception.servletName']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.servlet_name']}"/>
        </h:panelGrid>
      </h:form>
    </h:body>
  </f:view>
</html>

Any exception not explicitly handled by the web application code will cause a page similar to the one below to be displayed:

The [faces-config.xml] file will be as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
    <message-bundle>messages</message-bundle>
  </application>
</faces-config>

Note the following points:

  • Lines 9–14: The [messages.properties] file will be used for page internationalization. It will be accessible in XHTML pages via the msg key.
  • line 15: defines the [messages.properties] file as the priority source for error messages displayed by the <h:messages> and <h:message> tags. This allows you to override certain default JSF error messages. This feature is not used here.

10.3.2. The style sheet

The [styles.css] file is as follows:


.libelle{
   background-color: #ccffff;
   font-family: 'Times New Roman',Times,serif;
   font-size: 14px;
   font-weight: bold
}
body{
   background-color: #ffccff
}

.error{
   color: #ff3333
}

.info{
   background-color: #99cc00
}

.titleInfo{
   background-color: #ffcc00
}

Here are some examples of JSF code using these styles:


<h:outputText value="#{msg['form.infos.employee']}"
 styleClass="titleInfo"/>

<h:panelGrid columns="3"
rowClasses="label,info">

<h:message for="hoursWorked"
 styleClass="error"/>

10.3.3. The message file

The message file [messages_fr.properties] is as follows:


form.title=Payroll
form.comboEmployees.label=Employee
form.hoursWorked.label=Hours worked
form.daysWorked.label=Days worked
form.hoursWorked.required=Enter the number of hours worked
form.hoursWorked.validation=Invalid data
form.daysWorked.required=Enter the number of days worked
form.daysWorked.validation=Invalid data
form.btnSalary.label=Salary
form.btnClear.label=Clear
exception.header=The following exception occurred
exception.httpCode=HTTP error code
exception.message=Exception message
exception.requestUri=URL requested when the error occurred
exception.servletName=Name of the servlet requested when the error occurred
form.employeeInfo=Employee Information
form.employee.lastName=Last Name
form.employee.last_name=Last name
form.employee.address=Address
form.employee.city=City
form.employe.zipCode=Zip Code
form.employee.index=Index
form.contributions.info=Social Security Contributions Information
form.contributions.csgrds=CSGRDS
form.contributions.csgd=CSGD
form.contributions.pension=Pension
form.contributions.social-security=Social Security
form.infos.indemnites=Compensation Information
form.benefits.hourlyWage=Hourly Wage
form.maintenance.allowance.day = Maintenance / Day
form.meal.allowancesDay=Meals / Day
form.allowances.paid\u00e9 leave=Paid leave
form.salary.info=Salary Information
form.salary.base=Base Salary
form.salary.socialContributions=Social Contributions
form.salary.maintenance=Maintenance Allowances
form.salary.meal=Meal Allowances
form.salary.net=Net Salary

These messages are all used in the [index.xhtml] page, except for those in lines 11–15, which are used in the [exception.xhtml] page.

10.3.4. The scope of beans

The [web.forms.Form] bean will have a request scope:


import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Form implements Serializable {

The [web.utils.ChangeLocale] bean will have application scope:


package web.utils;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
  // page locale
  private String locale = "fr";
  
  public ChangeLocale() {
  }
  
  public String setFrenchLocale(){
    locale = "fr";
    return null;
  }
  
  public String setEnglishLocale(){
    locale = "en";
    return null;
  }

  public String getLocale() {
    return locale;
  }

  public void setLocale(String locale) {
    this.locale = locale;
  }
  
  
}

10.3.5. The [business] layer

The [business] layer implements the following IMetierLocal interface:


package business;

import java.util.List;
import javax.ejb.Local;
import jpa.Employee;

@Local
public interface ILocalJob {
  // Get the pay stub
  PayStub calculatePayStub(String SS, double hoursWorked, int daysWorked);
  // list of employees
  List<Employee> findAllEmployees();
}

This interface is the one used in the server-side of the client/server application described in Section 7.1.

The Business class that we will use to test the [web] layer implements this interface as follows:


package business;

...
public class Metier implements IMetierLocal {
  
  // employee dictionary indexed by social security number
  private Map<String, Employee> hashEmployees = new HashMap<String, Employee>();
  // list of employees 
  private List<Employee> employeeList;
  
  // Get the pay stub
  public PayrollCalculatePayroll(String SS,
    double hoursWorked, int daysWorked) {
    // retrieve the employee with SS number
    Employee e = hashEmployees.get(SS);
    // return a sample pay stub
    return new PayStub(e, new Contribution(3.49, 6.15, 9.39, 7.88), new PayElements(100, 100, 100, 100, 100));
  }
  
  // list of employees
  public List<Employee> findAllEmployees() {
    if(employeesList == null){
      // create a list of two employees
      employeeList = new ArrayList<Employee>();
      listEmployees.add(new Employee("254104940426058", "Jouveinal", "Marie", "5 Rue des Oiseaux", "St. Corentin", "49203", new Compensation(2, 2.1, 2.1, 3.1, 15)));
      listEmployees.add(new Employee("260124402111742", "Laverti", "Justine", "La brûlerie", "St Marcel", "49014", new Compensation(1, 1.93, 2, 3, 12)));
      // employee dictionary indexed by social security number
      for (Employee e : listEmployees) {
        hashEmployees.put(e.getSS(), e);
      }
    }
    // Return the list of employees
    return employeesList;
  }
}

We leave it to the reader to decipher this code. Note the method used: to avoid having to implement the EJB part of the application, we simulate the [business] layer. Once the [web] layer is verified as correct, we can then replace it with the actual [business] layer.

10.4. The [index.xhtml] form and its template [Form.java]

We will now build the form’s XHTML page and its model.

Recommended reading in [ref3]:

  • Example #3 (mv-jsf2-03) for the list of tags that can be used in a form
  • Example #4 (mv-jsf2-04) for drop-down lists populated by the model
  • Example #6 (mv-jsf2-06) for input validation
  • Example #7 (mv-jsf2-07) for handling the [Clear] button

10.4.1. Step 1


Question: Build the [index.xhtml] form and its [Form.java] model needed to display the following page:


The input components are as follows:

No.
id
JSF type
model
role
1
comboEmployees
<h:selectOneMenu>
String comboEmployeesValue
List<Employee> getEmployees()
contains the list of employees in the format
"first name last name".
2
hoursWorked
<h:inputText>
String hoursWorked
number of hours worked - real number
3
daysWorked
<h:inputText>
String daysWorked
number of days worked - integer
4
btnSalary
<h:commandButton>
 
starts the salary calculation
5
btnReset
<h:commandButton>
 
resets the form to its initial state
  • The getEmployees method will return a list of employees retrieved from the [business] layer. The objects displayed by the combo box will have the itemValue attribute set to the employee's social security number and the itemLabel attribute set to a string consisting of the employee's first and last name.
  • The [Salary] and [Clear] buttons will not be linked to event handlers for the time being.
  • Input validity will be checked.

Image

Test this version. In particular, verify that input errors are properly flagged.

Note: It is important that the ID attributes of the page components do not contain accented characters. With Glassfish 3.1.2, this causes the application to crash.

10.4.2. Step 2


Question: Complete the [index.xhtml] form and its [Form.java] template to display the following page once the [Salary] button has been clicked:


The [Salary] button will be connected to the model’s calculateSalary event handler. This method will use the calculatePaystub method from the [business] layer. This paystub will be generated for the employee selected in [1].

In the model, the pay stub will be represented by the following private field:


  private PayrollSheet payrollSheet;

which has get and set methods.

To retrieve the information contained in this object, you can write expressions like the following in the JSF page:


<h:outputText value="#{form.payrollSheet.employee.name}"/>

The expression in the value attribute will be evaluated as follows:

[form].getPayrollSheet().getEmployee().getName() where [form] represents an instance of the [Form.java] class. The reader can verify that the get methods used here do indeed exist in the [Form], [PayrollSheet], and [Employee] classes, respectively. If this were not the case, an exception would be thrown when evaluating the expression.

Test this new version.

10.4.3. Step 3


Question: Complete the [index.xhtml] form and its template [Form.java] to obtain the following additional information:


We will follow the same approach as before. There is an issue with the euro currency symbol found in [1], for example. In an internationalized application, it would be preferable to use the display format and currency symbol of the selected locale (en, de, fr, ...). This can be achieved as follows:


          <h:outputFormat value="{0,number,currency}">
            <f:param value="#{form.payrollSheet.employee.dailyMaintenanceAllowance}"/>
</h:outputFormat>

We could have written:


          <h:outputText value="#{form.payroll.employee.dailyMaintenanceAllowance} є">

but with the en_GB locale (British English), the amount would still be displayed in euros when it should be in pounds (£). The <h:outputFormat> tag allows information to be displayed based on the locale of the displayed JSF page:

  • line 1: displays the {0} parameter, which is a number representing a monetary amount
  • line 2: the <f:param> tag assigns a value to the {0} parameter. A second <f:param> tag would assign a value to the parameter labeled {1}, and so on.

10.4.4. Step 4

Recommended reading: Example #7 (mv-jsf2-07) in [ref3].


Question: Complete the form [index.xhtml] and its template [Form.java] to handle the [Reset] button.


The [Reset] button restores the form to the state it was in when it was first requested via a GET request. There are several challenges here. Some have been explained in [ref3].

The form returned by the [Raz] button is not the entire form but only the entered portion of it:

Image

This result can be achieved using an <f:subview> tag as follows:


      <f:subview id="viewInfos" rendered="#{form.viewInfosIsRendered}">
... the part of the form that you want to be able to hide
</f:subview>

The <f:subview> tag encloses the entire portion of the form that can be displayed or hidden. Any component can be displayed or hidden using the rendered attribute. If rendered="true", the component is displayed; if rendered="false", it is not. If the rendered attribute takes its value from the model, then the display of the component can be controlled programmatically.

In the example above, we will control the display of the viewInfos view using the following field:


  private boolean viewInfosIsRendered;

along with its get and set methods. The methods handling clicks on the [Salary] and [Clear] buttons will update this boolean depending on whether the viewInfos view should be displayed or not.