Wednesday, April 24, 2013

Maven / Spring / Hibernate / JSF2 Tutorial - Third Part (Advanced JEE Web Application)


In this part we build and describe an advanced JEE Web Application. This application permits to simple users to modify their passwords and to a super user to freely insert, update or delete users, profiles and roles. The application relies on Spring Security to control users access.

This application is built upon the result of Maven/Spring/Hibernate/JSF2 Tutorial - Second Part (Skeleton JEE Web Application).


N.B: In the following by deploying the application we mean :


- In a command line console set your working directory to the path of your Test Eclipse project then successively type :

mvn clean
mvn compile
mvn war:war

- Under the sub-directory Test/target/ you should see the Test-1.0-SNAPSHOT folder. Copy that folder under the webapps/ folder of Apache Tomcat.
- Start your Apache Tomcat Server (if it is not started) then type the following address in a browser: http://localhost:8081/Test-1.0-SNAPSHOT/. (or change the port to your local setting)


Outline:

3.1 - Customizing Application:
  a - Application Messages
  b - JSF Validation Messages
  c - Using JSF Templates
  d - Richfaces Skins

3.2 - Password Update Feature
  a - Presentation Layer
  b - Service (BO) Layer
  c - Repository (DAO) Layer

3.3 - Generic Spring Components
  a - Generic Repository (DAO)
  b - Generic Service (BO)
  c - Generic Controller

3.4 - JSF and Richfaces useful components
  a - Super User features
  b - RichFaces Accordion
  c - JSF Data Table
  d - RichFaces Pick Lists
  e - JSF Converters
  f - JSF navigation

3.5 - Spring Security Access Control

3.1 - Customizing Application:

We propose here to introduce some customization to the application built in Part 2 of this tutorial.

a - Application Messages:

It is recommended to have all the application messages (titles of pages, text inputs or command buttons) stored in property files. This permits to handle them more efficiently.

But what actually are the messages of our application ?

. In Connection.xhtml we defined three titles in the following value attributes:

...
<h:outputText value = "User Name" />
...
<h:outputText value = "Password" />
...
<h:commandButton value = "Login" action = "#{loginController.doLogin()}" />
...

. But also in mshj.tutorial.listener.LoginErrorPhaseListener where we used the two following messages to stress out an authentication failure or a concurrency session exception:

...
new FacesMessage(FacesMessage.SEVERITY_ERROR,
                      "Username or password not valid.", "Username or password not valid")
...
new FacesMessage(FacesMessage.SEVERITY_ERROR,
                      "Maximum number of sessions Exceeded.", "Maximum number of sessions Exceeded")
...

- Create a new file named messages.properties under src/main/resources/.
- Paste into this file the following entries (one per each message):

userName=User Name
password=Password
login=Login
loginFailure=Username or Password not Valid
sessionConcurrency=Maximum Number of Concurrent Sessions Exceeded

- Make JSF aware of the use of the message resource bundle. Edit src/main/webapp/WEB-INF/faces-config.xml such that it finally looks like:

<?xml version="1.0"?>
<faces-config
    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"
    version="2.0">
   
 <!-- Resource Bundles (Messages and JSF Messages) -->
 <application>
  <resource-bundle>
   <base-name>messages</base-name>
   <var>messages</var>
  </resource-bundle>
 </application>   
   
   <!-- JSF and Spring are integrated -->
   <application>
     <el-resolver>
      org.springframework.web.jsf.el.SpringBeanFacesELResolver
     </el-resolver>
   </application>
    
</faces-config>

In Lines 10-15 we added the messages resource bundle.

- In Connection.xhtml transform the previously discussed static values to ones gathered from the messages resource bundle:

...
<h:outputText value = "#{messages.userName}" />
...
<h:outputText value = "#{messages.password}" />
...
<h:commandButton value = "#{messages.login}" action = "#{loginController.doLogin()}" />
...

- In mshj.tutorial.listener.LoginErrorPhaseListener perform these transformations:
. Replace "Username or password not valid" by a call to the messages resource bundle with key "loginFailure":

...
new FacesMessage(FacesMessage.SEVERITY_ERROR,
         FacesContext.getCurrentInstance().getApplication()
              .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                  .getString("loginFailure"),
         FacesContext.getCurrentInstance().getApplication()
              .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                  .getString("loginFailure"))
...

. Then replace "Maximum number of sessions Exceeded" by a call to the messages resource bundle with key "sessionConcurrency":

...
new FacesMessage(FacesMessage.SEVERITY_ERROR,
         FacesContext.getCurrentInstance().getApplication()
              .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                  .getString("sessionConcurrency"),
         FacesContext.getCurrentInstance().getApplication()
              .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                  .getString("sessionConcurrency"))
...

Indeed,

FacesContext.getCurrentInstance().getApplication()
   .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
       .getString("someKey")

returns the value mapped by "someKey" in the application messages resource bundle.

It is also possible to define application messages depending on the user's (language) Locale settings.
You can define a default Locale, i.e. the one to be used if the user's Locale is not supported and a set of supported Locales. Here's an example of configuring french messages for users having a french Locale.

- Edit src/main/webapp/WEB-INF/faces-config.xml such that it finally looks like:

<?xml version="1.0"?>
<faces-config
    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"
    version="2.0">
   
 <!-- Resource Bundles (Messages and JSF Messages) -->
 <application>

  <locale-config>
    <default-locale>en</default-locale>
    <supported-locale>fr_FR</supported-locale>
  </locale-config> 
 
  <resource-bundle>
   <base-name>messages</base-name>
   <var>messages</var>
  </resource-bundle>

 </application>   
   
   <!-- JSF and Spring are integrated -->
   <application>
     <el-resolver>
      org.springframework.web.jsf.el.SpringBeanFacesELResolver
     </el-resolver>
   </application>
    
</faces-config>

- Now add the following messages_fr_FR.properties file under src/main/resources/:

userName=Nom d'Utilisateur
password=Mot de Passe
login=S'authentifier
loginFailure=Nom d'Utilisateur ou Mot de Passe Incorrect
sessionConcurrency=Nombre Maximum de Sessions Concurrentes Atteint

Change your browser Locale to french (if it is not already french) and try out the messages customization after deploying the application.
Note that to switch displayed messages depending on the user's Locale, you have only to create dedicated files and to add the Locale's name as suffix to the corresponding application messages resource bundles.

b - JSF Validation Messages:

JSF validates the inputs according to their constraints. For example, in Connection.xhtml the two input texts (User Name and Password) are set to be required. Leaving one of these inputs empty leads to a default message, for example for password something like: J_password : validation error. Value is required. These messages values can be customized, also depending on the user's Locale.

- Create the following  jsfMessages.properties file under src/main/resources/:

javax.faces.converter.NumberConverter.NUMBER={2}: ''{0}'' is not a number.
javax.faces.converter.NumberConverter.NUMBER_detail={2}: ''{0}'' is not a number. Example: {1}.
javax.faces.converter.LongConverter.LONG={2}: ''{0}'' must be a number consisting of one or more digits.
javax.faces.converter.LongConverter.LONG_detail={2}: ''{0}'' must be a number between -9223372036854775808 to 9223372036854775807 Example: {1}.
javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required.
javax.faces.converter.BigDecimalConverter.DECIMAL={2}: ''{0}'' must be a signed decimal number.
javax.faces.converter.BigDecimalConverter.DECIMAL_detail={2}: ''{0}'' must be a signed decimal number consisting of zero or more digits, that may be followed by a decimal point and fraction. Example: {1}.
javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date.
javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be understood as a date. Example: {1}.

- Create also a french entry jsfMessages_fr_FR.properties file under src/main/resources/:

javax.faces.converter.NumberConverter.NUMBER=Erreur de validation pour {2}: ''{0}'' n'est pas numérique.
javax.faces.converter.NumberConverter.NUMBER_detail=Erreur de validation pour {2}: ''{0}'' n'est pas numérique. Exemple de valeur attendue: {1}.
javax.faces.converter.LongConverter.LONG=Erreur de validation pour {2}: ''{0}'' n'est pas numérique.
javax.faces.converter.LongConverter.LONG_detail=Erreur de validation pour {2}: ''{0}'' doit être un nombre compris entre -9223372036854775808 et 9223372036854775807. Exemple de valeur attendue: {1}.
javax.faces.component.UIInput.REQUIRED=Erreur de validation pour {0}: ce champ est obligatoire.
javax.faces.converter.BigDecimalConverter.DECIMAL=Erreur de validation pour {2}: ''{0}'' doit être un nombre décimal signé.
javax.faces.converter.BigDecimalConverter.DECIMAL_detail=Erreur de validation pour {2}: ''{0}'' doit être un nombre décimal signé composé de zéro ou plusieurs chiffres, qui peuvent être suivis par une virgule et une fraction. Exemple de valeur attendue: {1}.
javax.faces.converter.DateTimeConverter.DATE=Erreur de validation pour {2}:''{0}'' ne peut pas être converti en une date.
javax.faces.converter.DateTimeConverter.DATE_detail=Erreur de validation pour {2}:''{0}'' ne peut pas être converti en une date. Exemple de valeur attendue: {1}.

- Let now JSF be aware of using custom validation messages. Edit src/main/webapp/WEB-INF/faces-config.xml such that it finally looks like:

<?xml version="1.0"?>
<faces-config
    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"
    version="2.0">
   
 <!-- Resource Bundles (Messages and JSF Messages) -->
 <application>

  <locale-config>
    <default-locale>en</default-locale>
    <supported-locale>fr_FR</supported-locale>
  </locale-config> 
 
  <resource-bundle>
   <base-name>messages</base-name>
   <var>messages</var>
  </resource-bundle>

  <message-bundle>jsfMessages</message-bundle>

 </application>   
   
   <!-- JSF and Spring are integrated -->
   <application>
     <el-resolver>
      org.springframework.web.jsf.el.SpringBeanFacesELResolver
     </el-resolver>
   </application>
    
</faces-config>

Note that when customizing the message you can use parameters. Example:

javax.faces.converter.NumberConverter.NUMBER_detail={2}: ''{0}'' is not a number. Example: {1}.

Here {2} will be replaced by the input's label (or id if no label is specified), {0} by the value actually provided by the user for the input and {1} by some generated value that passes the validation.

c - Using JSF Templates:

Usually application pages correspond to some pattern. For example a header (with some logo and title), a footer ... etc. JSF permits to define such pages templates.


- Under src/main/webapp/ add a new folder named templates.
- Create the following Default.xhtml under src/main/webapp/templates/:

<?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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>

    <h:head>
      <link type="text/css" rel="stylesheet" href="../resources/css/myCSS.css" />
    </h:head>

  <body>

    <div id = "header_div">
      <div id = "banner_div" align = "center">
        <ui:insert name = "title">
          <h:graphicImage value="../resources/images/logo.png" />
        </ui:insert>
      </div>
      
      <div id = "subtitle_div" align = "center">
        <ui:insert name = "subtitle"/>
      </div>
      
      <div id = "status_div" align = "center">
        <ui:insert name = "status" />
      </div>
          
    </div>
    
    <div id = "menu_div"  align = "center">
      <ui:insert name = "menu"/>
    </div>
    
    <div id = "messages_div"  align = "center">
      <ui:insert name = "messages">
        <table border = "0" align = "center">
          <tr>
            <td align = "center">
              <h:messages globalOnly="true" styleClass="error-text" />
            </td>
          </tr>
        </table>
      </ui:insert>
    </div>
    
    <div id = "content_div" align = "center">
      <ui:insert name = "content"/>
    </div>
    
    <div id = "footer_div"  align = "center">
      <ui:insert name = "footer">
        <h:graphicImage value="../resources/images/footer.png" />
      </ui:insert>
    </div>

  </body>
  
</html>

Using <ui:insert /> tags we define insertions in the template. For example, in Lines 20-22, we define a header containing a banner image (../resources/images/logo.png). These insertions will be displayed in any page that instantiates the template page. Let's see how to make Connection.xhtml and AccessDenied.xhtml instantiate the Default.xhtml template.

- Edit the Connection.xhtml 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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>
  
  <ui:composition template="../templates/Default.xhtml">
  
    <ui:define name = "subtitle">
      <h3>#{messages.login}</h3>
    </ui:define>
    
    <ui:define name = "content">
      <h:form id = "loginForm">
        <table align = "center" border = "0">
          <tr>
            <th align = "center">
              <h:outputText value = "#{messages.userName}" styleClass="title-text" />
            </th>
            <td align = "center">
              <h:inputText 
                id = "j_username"
                value = "#{loginController.j_username}"
                label = "J_username"
                size = "8"
                maxlength = "8"
                required = "true"
              />
            </td>
            <td align = "center">
              <h:message for = "j_username" styleClass="error-text" />
            </td>
          </tr>
          <tr>
            <th align = "center">
              <h:outputText value = "#{messages.password}" styleClass="title-text" />
            </th>
            <td align = "center">
              <h:inputSecret
                id = "j_password"
                value = "#{loginController.j_password}"
                label = "J_password"
                size = "8"
                maxlength = "8"
                required = "true"
              />
            </td>
            <td align = "center">
                <h:message for = "j_password" styleClass="error-text" />
            </td>
          </tr>
          <tr>
            <td align = "center" colspan = "3">
              <h:commandButton
                value = "#{messages.login}"
                action = "#{loginController.doLogin()}"
              >
                <f:phaseListener type="mshj.tutorial.listener.LoginErrorPhaseListener" />
              </h:commandButton>
            </td>
          </tr>
        </table>
      </h:form>
    </ui:define>
    
  </ui:composition>
  
</html>

Here the <ui:composition /> tag states that this page instantiates a template (more precisely Default.xhtml). Then using <ui:insert /> tags we specify what insertions of the template will be overridden by the current page (here for example the insertion named content is overridden).

- Lets do something analogous with AccessDenied.xhtml by editing it 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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>
  
  <ui:composition template="../templates/Default.xhtml">
  
    <ui:define name = "subtitle" />
    
    <ui:define name = "content">
      <h3>#{messages.accessDenied}</h3>
    </ui:define>
    
  </ui:composition>
  
</html>

- Default.xhtml uses two images and declares a style definition file. Download these files here (logo.pngfooter.png and myCSS.css) and copy them in the corresponding directories of your Eclipse Project (src/main/webapp/resources/images for the two images and src/main/webapp/resources/css for the css file).
AccessDenied.xhtml also defines an new application message identified by the key accessDenied. Add corresponding entries to your src/main/resources/messages*.properties files, such that:
src/main/resources/messages.properties becomes:

userName=User Name
password=Password
login=Login
loginFailure=Username or Password not Valid
sessionConcurrency=Maximum Number of Concurrent Sessions Exceeded
accessDenied=Access Denied !

. and src/main/resources/messages_fr_FR.properties becomes:

userName=Nom d'Utilisateur
password=Mot de Passe
login=S'authentifier
loginFailure=Nom d'Utilisateur ou Mot de Passe Incorrect
sessionConcurrency=Nombre Maximum de Sessions Concurrentes Atteint
accessDenied=Accès Refusé !

Not all insertions defined by Default.xhtml are instantiated for now. One can populate for example the status insertion by a welcome message for the user logged in or the menu insertion with options for navigating between different pages of the application (the latter will be done in further steps).

d - Richfaces Skins:

Using Richfaces one can take advantage of lovely predefined themes. Richfaces comes with a set of three themes we propose to use the Emerald Town theme. The interesting thing here is that you can easily customize those themes.

- Under the folder src/main/resources/ create the file mshj.skin.properties as follows:

baseSkin=emeraldTown
headerBackgroundColor=#4E671D

Here we declare the skin we are using as base then we override some property of the skin.

- Edit src/main/webapp/WEB-INF/web.xml and add the following context parameters to the JSF context parameters section:

  <context-param>
    <param-name>org.richfaces.enableControlSkinning</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>org.richfaces.enableControlSkinningClasses</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>org.richfaces.skin</param-name>
    <param-value>mshj</param-value>
  </context-param>

Here we ask to be allowed to control skins (and their properties). We also provide the configuration file that will contain the base skin name and the properties we want to override.

- Deploy the application and notice the graphical changes.

3.2 - Password Update Feature:

We propose in the following to create a page where an authenticated user can change his password. For that we need a Controller that will interpose between the user and a service (BO) that does the job while relying on a Repository (DAO).
In the following we precisely show how the three layers of our application are designed in a loosely-coupled way driven by the user's need (having her password updated).

a - Presentation Layer:

In the presentation layer we need two things:
(i) a page with three input texts (the old password for more security, the new password and a confirmation of the latter) and a command button to submit the password modification;
(ii) a controller that will store the user's input and relay the corresponding update request to some service.

- Under src/main/webapp/pages/ add a new PasswordUpdate.xhtml page 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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>
  
  <ui:composition template="../templates/Default.xhtml">
  
    <ui:define name = "subtitle">
      <h3>#{messages.updatePassword}</h3>
    </ui:define>

    <ui:define name = "menu">
      <h:form id = "menuForm">
        <rich:menuItem action = "#{loginController.doLogout()}" >
          <h:graphicImage value="../resources/images/logout.png" width = "64" height = "64" />
        </rich:menuItem>
      </h:form>
    </ui:define>
    
    <ui:define name = "content">
      <h:form id = "passwordUpdateForm">
        <table align = "center" border = "0">
          <tr>
            <th align = "center">
              <h:outputText value = "#{messages.currentPassword}" styleClass="title-text" />
            </th>
            <td align = "center">
              <h:inputSecret
                id = "current_password"
                value = "#{passwordUpdateController.currentPassword}"
                label = "current_password"
                size = "8"
                maxlength = "8"
                required = "true"
              />
            </td>
            <td align = "center">
              <h:message for = "current_password" styleClass="error-text" />
            </td>
          </tr>
          <tr>
            <th align = "center">
              <h:outputText value = "#{messages.newPassword}" styleClass="title-text" />
            </th>
            <td align = "center">
              <h:inputSecret
                id = "new_password"
                value = "#{passwordUpdateController.newPassword}"
                label = "new_password"
                size = "8"
                maxlength = "8"
                required = "true"
              />
            </td>
            <td align = "center">
              <h:message for = "new_password" styleClass="error-text" />
            </td>
          </tr>
          <tr>
            <th align = "center">
              <h:outputText value = "#{messages.confNewPassword}" styleClass="title-text" />
            </th>
            <td align = "center">
              <h:inputSecret
                id = "confNew_password"
                value = "#{passwordUpdateController.confNewPassword}"
                label = "confNew_password"
                size = "8"
                maxlength = "8"
                required = "true"
              />
            </td>
            <td align = "center">
              <h:message for = "confNew_password" styleClass="error-text" />
            </td>
          </tr>
          <tr>
            <td align = "center" colspan = "3">
              <h:commandButton
                value = "#{messages.updatePassword}"
                action = "#{passwordUpdateController.update()}"
              />
            </td>
          </tr>
        </table>
      </h:form>
    </ui:define>
    
  </ui:composition>
  
</html>

Note that we implemented the menu insertion of the Default.xhtml template to add a logout entry.
- This menu entry uses an image logout.png, download it (here) then save it under src/main/webapp/resources/images/.

The page uses also some application messages that should be added to the messages bundles:

- Edit src/main/resources/messages.properties and add the following entries:

updatePassword=Update Password
currentPassword=Current Password
newPassword=New Password
confNewPassword=New Password Confirmation


Edit src/main/resources/messages_fr_FR.properties and add the following entries:

updatePassword=Mise à Jour du Mot de Passe
currentPassword=Mot de Passe Courant
newPassword=Nouveau Mot de Passe
confNewPassword=Confirmation du Nouveau Mot de Passe

The page also refers to a controller that we create below:

- Under the package mshj.tutorial.controller create the new PasswordUpdateController.java controller as follows:

package mshj.tutorial.controller;

import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import mshj.tutorial.service.UpdatePasswordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class PasswordUpdateController implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  private String currentPassword;
  
  public String getCurrentPassword() {
    return currentPassword;
  }
  public void setCurrentPassword(String currentPassword) {
    this.currentPassword = currentPassword;
  }
  
  private String newPassword;
  
  public String getNewPassword() {
    return newPassword;
  }
  public void setNewPassword(String newPassword) {
    this.newPassword = newPassword;
  }
  
  private String confNewPassword;
  
  public String getConfNewPassword() {
    return confNewPassword;
  }
  public void setConfNewPassword(String confNewPassword) {
    this.confNewPassword = confNewPassword;
  }
  
  @Autowired
  private PasswordUpdateService passwordUpdateService;
  
  public PasswordUpdateService getPasswordUpdateService() {
    return updatePasswordService;
  }
  public void setPasswordUpdateService(PasswordUpdateService passwordUpdateService) {
    this.passwordUpdateService = passwordUpdateService;
  }
  
  
  
  public void update()
  {
    FacesContext.getCurrentInstance()
      .addMessage(null, 
        new FacesMessage(getPasswordUpdateService()
            .update(getCurrentPassword(), getNewPassword(), getConfNewPassword())));
  }
}

Here we defined containers (properties) for all input text values and the method update which relies on an auto-wired UpdatePasswordService service. What is required from that service is to try to perform the update task and return a message summarizing the results (this message is then added by the controller to the application global messages). Note that for now we don't need the full implementation of the service we only need that it implements the update method (i.e. that it fulfills the contract).
For that we use the following interface:

- Create the new package mshj.tutorial.service and create inside it the following PasswordUpdateService interface:

package mshj.tutorial.service;

public interface PasswordUpdateService
{
  String update(String currentPassword, String newPassword, String confNewPassword);
}

Note that if many profiles of developers are collaborating to fulfill the task, the presentation layer job is done and interestingly without interfering with the other layers (loose-coupling). Time now for the service layer engineer to proceed. So you can take off you "presentation layer" hat and put the "service layer" one.

b - Service (BO) Layer:

Here we start with a contract in hand. We have to provide an implementation for the PasswordUpdateService interface having in mind that all data access stuff should be delegated to a repository bean.

But what's really to be done in the Service layer ?
(i) Checking that the password update request is coherent: that the current password is really the one of the currently connected user and that the new password and its confirmation are equal.
(ii) If the request is coherent ask the Repository bean to do the update job. If not return a message asking the user to fix the error.

- Create the new package mshj.tutorial.service.impl and create inside it the following PasswordUpdateServiceImpl class:

package mshj.tutorial.service.impl;

import java.io.Serializable;
import javax.faces.context.FacesContext;
import mshj.tutorial.model.User;
import mshj.tutorial.repository.UserRepository;
import mshj.tutorial.security.MyUserDetails;
import mshj.tutorial.service.PasswordUpdateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly=true)
public class PasswordUpdateServiceImpl implements Serializable, PasswordUpdateService
{
  private static final long serialVersionUID = 1L;
  
  @Autowired
  private UserRepository userRepository;
  
  public UserRepository getUserRepository() {
    return userRepository;
  }
  public void setUserRepository(UserRepository userRepository) {
    this.userRepository = userRepository;
  }
  
  @Override
  @Transactional(readOnly=false, propagation=Propagation.REQUIRED)
  public String update(String currentPassword, String newPassword,String confNewPassword)
  {
    if (newPassword.equals(confNewPassword))
    {
      User currentUser = ((MyUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser();
      if (currentUser.getPassword().equals(currentPassword))
      {
        currentUser.setPassword(newPassword);
        getUserRepository().update(currentUser);
        return(FacesContext.getCurrentInstance().getApplication()
                .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                .getString("passwordUpdated"));
        
      }
      else
      {
        return(FacesContext.getCurrentInstance().getApplication()
                .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                .getString("wrongPassword"));
      }
    }
    else
    {
      return(FacesContext.getCurrentInstance().getApplication()
              .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
              .getString("passwordsMismatch"));
    }
  }
}

The bean is annotated as Service. The method update is implemented as expected: relying on the auto-wired UserRepository repository to do the update job if the parameters of the update request passes all the checks.

Messages returned by the update method must declared in the resource bundle:

Edit src/main/resources/messages.properties and add the following entries:

passwordsMismatch=New Password does not match its Confirmation
wrongPassword=Check your Current Password
passwordUpdated=Password Successfully Updated. Update will be effective after logout


Edit src/main/resources/messages_fr_FR.properties and add the following entries:

passwordsMismatch=Le Nouveau Mot de Passe et sa Confirmation ne correspondent pas
wrongPassword=Vérifier votre Mot de Passe Courant
passwordUpdated=Mot de Passe mis à jour avec Succès. La mise à jour sera effective après déconnexion


We stress out two things here. First the instruction

(MyUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal()

permits to retrieve the currently connected user.  Second the hole service is declared to be read-only transactional. Only the method update overrides this setting being as expected declared as read-write transactional. Defining transaction settings at the service layer is a good habit since it permits a given method to perform many database accesses (through calling different repositories) in the same transaction.

Remark: Addressing the application message bundles in the service layer maybe looking as a violation of separation of concerns since the service layer is addressing a resource which is handled by the presentation layer. While writing this i was thinking as follows: the business layer is in charge of telling why an update request is not coherent (for example according to the business rule stating that the new password and its confirmation should match), the controller is only in charge of relaying this to the user.

Loose coupling between layers is also respected here. Indeed only the contract to be respected by the Repository is needed for now (we don't need the full implementation yet).

- Create the new package mshj.tutorial.repository and create inside it the following UserRepository interface:

package mshj.tutorial.repository;

import mshj.tutorial.model.User;

public interface UserRepository
{
  void update(User user);
}

Now we can proceed with designing the Repository (DAO) layer of our application task.

c - Repository (DAO) Layer:

Now we have to implement the contract of the UserRepository interface.
- Create the new package mshj.tutorial.repository.impl and create inside it the following UserRepositoryImpl class:

package mshj.tutorial.repository.impl;

import java.io.Serializable;
import mshj.tutorial.model.User;
import mshj.tutorial.repository.UserRepository;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class UserRepositoryImpl implements Serializable, UserRepository
{
  private static final long serialVersionUID = 1L;

  @Autowired
  private SessionFactory sessionFactory;
  
  public SessionFactory getSessionFactory() {
    return sessionFactory;
  }
  public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }  
  
  @Override
  public void update(User user)
  {
    getSessionFactory().getCurrentSession().update(user);
  }
}

The bean is declared as Repository and relies in turn on the Hibernate Session to perform the password update.  and ... we are done !

Before deploying and testing the new page, update the mshj.tutorial.handler.MyAuthenticationSuccessHandler bean to redirect upon a successful authentication the users having no roles to PasswordUpdate.xhtml as follows:

// ...
      if (roles.isEmpty())
      {
        response.sendRedirect("PasswordUpdate.xhtml");
      }
// ...

- Deploy and test the application. Notice that you have now a new button to log out from the application.

3.3 - Generic Spring Components:

A Repository bean will completely rely on Hibernate to perform usual database operations. This motivates thinking about a Generic Repository class (implementing some Generic Repository Interface) that groups the usual Hibernate calls (insert, update, delete, conditioned select) and which will be extended by all specific repositories needed by the application.

a - Generic Repository (DAO):

- Under the package mshj.tutorial.repository create the parametric interface GenericRepository as follows:

package mshj.tutorial.repository;

import java.io.Serializable;
import java.util.List;

public interface GenericRepository<T, ID extends Serializable>
{ 
  public ID save(T entity);
  public void update(T entity);
  public void delete(T entity);
  public List<T> findAll();
  public List<T> find(String query);
}


The interface is parametric of T the type of the Model Class (mapping some table) and ID the type of the class' identifier property (the property mapping the primary key of the table). It presents the usual contract (list of qualified methods) a Repository (DAO) should perform which will be implemented by the its generic implementation:

- Under the package mshj.tutorial.repository.impl create the parametric class GenericRepositoryImpl as follows:

package mshj.tutorial.repository.impl;

import java.io.Serializable;
import java.util.List;
import org.hibernate.SessionFactory;
import mshj.tutorial.repository.GenericRepository;
import org.springframework.beans.factory.annotation.Autowired;

public class GenericRepositoryImpl<T, ID extends Serializable> implements GenericRepository<T, ID>
{
  @Autowired
  private SessionFactory sessionFactory;
  
  public SessionFactory getSessionFactory() {
    return sessionFactory;
  }
  public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }

  private Class<T> myClass;

  public Class<T> getMyClass() {
    return myClass;
  }
  public void setMyClass(Class<T> myClass) {
    this.myClass = myClass;
  }

  public GenericRepositoryImpl(Class<T> myClass)
  {
    this.myClass = myClass;
  }
  
  @SuppressWarnings("unchecked")
  public ID save(T entity)
  {
    return((ID)getSessionFactory().getCurrentSession().save(entity));
  }

  public void update(T entity)
  {
    getSessionFactory().getCurrentSession().update(entity);
  }

  public void delete(T entity)
  {
    getSessionFactory().getCurrentSession().delete(entity);
  }
  
  @SuppressWarnings("unchecked")
  public List<T> findAll()
  {
    return(getSessionFactory().getCurrentSession().createQuery("from " + getMyClass().getSimpleName()).list());
  } 
  
  @SuppressWarnings("unchecked")
  public List<T> find(String queryString)
  {
    return(getSessionFactory().getCurrentSession().createQuery(queryString).list());
  }
}


The class implements the usual contract (list of qualified methods) a Repository (DAO) should perform:

public ID save(T entity); That takes as parameter an instance of the class T describing a line to be inserted in the mapped table and returns an instance of ID which contains the value of the primary key of the inserted line. Returning the value of the primary key is very useful when it is automatically generated using a strategy supported by Hibernate, example: using some auto-incremented sequence.

public void update(T entity); That takes as parameter an instance of the class T describing a line to be updated in the mapped table. Here entity specifies the criteria of selection of the line to be updated (basically its primary key value) as well as the updates to be performed (all other non-primary key fields).

public void delete(T entity); That takes as parameter an instance of the class T describing a line to be deleted in the mapped table.

public List<T> findAll(); Returns a List of instances of the class T corresponding to all the mapped table lines.

public List<T> find(String query); Returns a List of instances of the class T corresponding to the mapped table lines satisfying the HQL (Hibernate Query Language) query query.


We notice here that all methods implementations simply call corresponding methods of the available Hibernate Session which is available using the instruction getSessionFactory().getCurrentSession() (remark that the SessionFactory is auto-wired).The Hibernate Session transparently (w.r.t. to our DAO code) takes care of the database connections, statement creation and execution, result sets parsing ... etc.


Having this done one can easily derive implementations for our UserRepository interface and UserRepositoryImpl class as follows:

- To keep copies of the old Repository interface and class, rename (outside Eclipse in your file system explorer) mshj.tutorial.repository.UserRepoistory.java (respectively mshj.tutorial.repository.impl.UserRepoistoryImpl.java) to mshj.tutorial.repository.UserRepoistory.java.old (respectively to mshj.tutorial.repository.impl.UserRepoistoryImpl.java.old).
Then in Eclipse right-click on your project and choose refresh.


- Under the package mshj.tutorial.repository create the new version of UserRepository interface as follows:

package mshj.tutorial.repository;

import mshj.tutorial.model.User;

public interface UserRepository extends GenericRepository<User, Long>
{
  void update(User user);
}


- Under the package mshj.tutorial.repository.impl create the new version of UserRepositoryImpl class as follows:

package mshj.tutorial.repository.impl;

import java.io.Serializable;
import mshj.tutorial.model.User;
import mshj.tutorial.repository.UserRepository;
import org.springframework.stereotype.Repository;

@Repository
public class UserRepositoryImpl extends GenericRepositoryImpl<User, Long> implements Serializable, UserRepository
{
  private static final long serialVersionUID = 1L;
  
  public UserRepositoryImpl()
  {
    super(User.class);
  }
}

Note that here we fix the type of the parameter T of GenericRepository(Impl) to be User in the constructor.
Note also how concise is now the definition of the Repository layer.

- Deploy and try the new application version.

Comments: Using a Generic Repository bean can be quite useful since the code to be written for specific repositories becomes very concise. Nevertheless the methods you may bookmark in the Generic Repository may be not all needed by (or maybe not sufficient for) all the specific repositories you are brought to write for your application. An optimal solution maybe to stick to the minimum in the Generic Repository : just auto-wire the SessionFactory, provide a myClass property and only a getter for the CurrentSession. Then provide the methods (database operations) you need in your specific repository: declare them as a contract in the repository interface then implement them in the repository class using the inherited CurrentSession getter. The proposed solution release you from handling SessionFactory in your specific repositories while keeping some level of genericity.
Remark: It would have been great if we could shift all @Repository annotations to the GenericRepository or GenericRepositoryImpl ! Unfortunately this actually does not work.

b - Generic Service (BO):

Thinking about Generic Services came to my mind when i was a JEE beginner and thus writing bad JEE code ! At that time i was creating BO's that simply encapsulate DAO's calls and my controllers was wrongly calling as much BO's as i needed and using them in a "spaghetti code fashion" to obtain results requested by the user. In this flawed setting i had as many BO's as DAO's and it seemed more efficient to use Generic BO's that inject the corresponding DAO in a given BO.
Hindsight i realized this is not the perfect way to proceed. Putting a Controller back in its place (only collecting user requests and returning corresponding responses to her) i now write BO's that may use many DAO's thus breaking down the possibility of genericity for the Service layer.

Nevertheless in some cases (where a BO is really using only one DAO) one can take advantage of genericity. This is more or less our case for the Password Update task.

- In the mshj.tutorial.service create the GenericService interface as follows:

package mshj.tutorial.service;

import java.io.Serializable;

public interface GenericService<T,ID extends Serializable> 
{

}

- In the mshj.tutorial.service.impl create the GenericServiceImpl interface as follows:

package mshj.tutorial.service.impl;

import java.io.Serializable;
import mshj.tutorial.service.GenericService;
import mshj.tutorial.repository.GenericRepository;
import org.springframework.beans.factory.annotation.Autowired;

public abstract class GenericServiceImpl <T, ID extends Serializable, D extends GenericRepository<T,ID>> implements GenericService<T, ID>
{
  private Class<T> modelClass;
  
  public Class<T> getModelClass() {
    return modelClass;
  }
  public void setModelClass(Class<T> modelClass) {
    this.modelClass = modelClass;
  }  
  
  private Class<D> repositoryClass;
  
  public Class<D> getRepositoryClass() {
    return repositoryClass;
  }
  public void setRepositoryClass(Class<D> repositoryClass) {
    this.repositoryClass = repositoryClass;
  }
  
  private D myRepository;
  
  public D getMyRepository() {
    return myRepository;
  }

  @Autowired
  public void setMyRepository(D myRepository) {
    this.myRepository = myRepository;
  }
  
  public GenericServiceImpl(Class<T> modelClass, Class<D> repositoryClass)
  {
    this.modelClass = modelClass;
    this.repositoryClass = repositoryClass;
  }

  public ID save(T entity)
  {
    return(getMyRepository().save(entity));
  }

  public void update(T entity)
  {
    getMyRepository().update(entity);
  }

  public void delete(T entity)
  {
    getMyRepository().delete(entity);
  }
  
  public List findAll()
  {
    return(getMyRepository().findAll());
  }
  
  public List find(String request)
  {
    return(getMyRepository().find(request));
  }
}

Note that a Repository (extending GenericRepository) is injected in the GenericServiceImpl class. Very Important !!! The dependency injection is specified on the setter (vs. on the field itself). On run-time, this "gives time" to Spring to find precisely which instance of type D (extending GenericRepository<T,ID>) to instantiate for the field myRepository depending on the parameters <T, ID, D<T,ID>> of your bean service implementing GenericServiceImpl. More precisely if you write several repositories extending GenericRepositoryImpl (even if they have each a different set of prameters) and if you define the dependency injection on the field, Spring will through (among others) the exception:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [mshj.tutorial.repository.GenericRepository] is defined: expected single matching bean but found n: ...

where n is here the number of classes instantiating GenericRepositoryImpl. You can read more about Spring dependency injection here (Especially the Section 4.4.1.2 Setter-based dependency injection).

Having this done one can easily derive implementations for our PasswordUpdateService interface and PasswordUpdateServiceImpl class as follows:

- To keep copies of the old Service interface and class, rename (outside Eclipse in your file system explorer) mshj.tutorial.service.PasswordUpdateService.java (respectively mshj.tutorial.service.impl.PasswordUpdateServiceImpl.java) to mshj.tutorial.service.PasswordUpdateService.java.old (respectively to mshj.tutorial.service.impl.PasswordUpdateServiceImpl.java.old)
Then in Eclipse right-click on your project and choose refresh.

- Under the package mshj.tutorial.service create the new version of PasswordUpdateService interface as follows:

package mshj.tutorial.service;

import mshj.tutorial.model.User;

public interface PasswordUpdateService extends GenericService<User, Long>
{
  public String update(String currentPassword, String newPassword,String confNewPassword);
}

Note that the interface declares the signature of the method update needed by the PasswordUpdateController.

- Under the package mshj.tutorial.service.impl create the new version of PasswordUpdateServiceImpl class as follows:

package mshj.tutorial.service.impl;

import java.io.Serializable;
import java.util.List;
import javax.faces.context.FacesContext;
import mshj.tutorial.model.User;
import mshj.tutorial.repository.UserRepository;
import mshj.tutorial.security.MyUserDetails;
import mshj.tutorial.service.PasswordUpdateService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly=true)
public class PasswordUpdateServiceImpl extends GenericServiceImpl<User, Long, UserRepository> implements Serializable, PasswordUpdateService
{
  private static final long serialVersionUID = 1L;
  
  public PasswordUpdateServiceImpl()
  {
    super(User.class, UserRepository.class);
  }
  
  @Override
  @Transactional(readOnly=false, propagation=Propagation.REQUIRED)
  public String update(String currentPassword, String newPassword,String confNewPassword)
  {
    if (newPassword.equals(confNewPassword))
    {
      User currentUser = ((MyUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser();
      if (currentUser.getPassword().equals(currentPassword))
      {
        currentUser.setPassword(newPassword);
        update(currentUser);
        return(FacesContext.getCurrentInstance().getApplication()
                .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                .getString("passwordUpdated"));
        
      }
      else
      {
        return(FacesContext.getCurrentInstance().getApplication()
                .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
                .getString("wrongPassword"));
      }
    }
    else
    {
      return(FacesContext.getCurrentInstance().getApplication()
              .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
              .getString("passwordsMismatch"));
    }
  }
}

No need to auto-wire the Repository now since this is already done by the generic super class.
Note also that the update(currentUser); call addresses now a method provided by the generic service.

Comments: Remember that Generic Services (BOs) are useful only in specific cases (only one DAO is used by the BO). Nevertheless one can adapt Generic BOs to different needs: BOs using exactly 2 DAOs (Master/Detail setting) ... etc. Off course it is not mandatory that all your Services will be extending Generic ones, do it only if this makes writing code easier for you.
Remark: It would have been great if we could shift all our @Transactional annotations to the GenericService or GenericServiceImpl ! Or at least the @Service annotation ... Unfortunately this actually does not work. For the @Transactional annotations this is not a big limitation since you maybe want to have a more fine-grained control on the transactional aspects of your implemented service methods.

c - Generic Controller:


As we did for other layers one can also think about grouping useful code in some Generic Controller class and making (a subset of) all other Controllers extend it. Example of features you might need for a controller are:

(i) Adding a global message to a view (targeting <h:messages /> tag).
(ii) Getting a message from the application messages resource bundle given its key;
(iii) Getting the current connected user (for example to display a customized welcome message) ... etc.

Note that features (ii) and (iii) could also be available at the Service (BO) layer, and thus one can also think about adding them to a Generic Bo ...
Even feature (i) is used by our LoginErrorPhaseListener ...

So what if we do simpler and implement all these features in static methods of some abstract class. Then all the layers can access the abstract class. Deal ?

- Create the package mshj.tutorial.util and create the ApplicationUtil abstract class as follows:

package mshj.tutorial.util;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import mshj.tutorial.model.User;
import mshj.tutorial.security.MyUserDetails;
import org.springframework.security.core.context.SecurityContextHolder;

public abstract class ApplicationUtil
{  
  public static User getConnectedUser()
  {
    return(((MyUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser());
  }
  
  public static String getApplicationMessage(String bundle, String key)
  {
    return((FacesContext.getCurrentInstance().getApplication()
      .getResourceBundle(FacesContext.getCurrentInstance(), bundle)
      .getString(key) != null)
        ?FacesContext.getCurrentInstance().getApplication()
          .getResourceBundle(FacesContext.getCurrentInstance(), bundle)
          .getString(key)
          :"???" + key + "???");
  }
  
  public static String getApplicationMessage(String key)
  {
    return(getApplicationMessage("messages",key));
  }
  
  public static void addJSFMessage(String component, String message)
  {
    FacesContext.getCurrentInstance()
    .addMessage(component, 
      new FacesMessage(message));
  }
  
  public static void addJSFMessageByKey(String component, String key)
  {
    FacesContext.getCurrentInstance()
    .addMessage(component, 
      new FacesMessage(getApplicationMessage(key)));
  }  
  
  public static void addGlobalJSFMessage(String message)
  {
    addJSFMessage(null, message);
  }
  
  public static void addGlobalJSFMessageByKey(String key)
  {
    addJSFMessageByKey(null, key);
  }
}

Here we defined the different features described above. Please note that you can add a message to a specific component of a page given its "path" in the page. Let's say you have an input text with id it_id inside a form having id f_id the path of this component is "f_id.it_id". The message will then be displayed at the position where a <h:message /> tag was defined for the component.

Using this class will make our code more readable. Now we have to update classes using these features and replace the long instructions they call by simple calls to ApplicationUtil's methods:

- Update mshj.tutorial.listener.LoginErrorPhaseListener as follows:

package mshj.tutorial.listener;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import mshj.tutorial.util.ApplicationUtil;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;

public class LoginErrorPhaseListener implements PhaseListener
{
  private static final long serialVersionUID = 1L;
  
  @Override
  public void afterPhase(PhaseEvent arg0)
  {
    
  }
  
  @Override
  public void beforePhase(PhaseEvent arg0)
  {
    Exception e = (Exception) FacesContext.getCurrentInstance().
      getExternalContext().getSessionMap().get(WebAttributes.AUTHENTICATION_EXCEPTION);
       
    if (e instanceof BadCredentialsException)
    {
      FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
        WebAttributes.AUTHENTICATION_EXCEPTION, null);
      ApplicationUtil.addGlobalJSFMessageByKey("loginFailure");
    }
    else
    {
      if (e instanceof SessionAuthenticationException)
      {
        FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
          WebAttributes.AUTHENTICATION_EXCEPTION, null);
        ApplicationUtil.addGlobalJSFMessageByKey("sessionConcurrency");
      }
    }
  }
  
  @Override
  public PhaseId getPhaseId()
  {
    return PhaseId.RENDER_RESPONSE;
  }
}



- Update mshj.tutorial.service.impl.PasswordUpdateServiceImpl as follows:

package mshj.tutorial.service.impl;

import java.io.Serializable;
import mshj.tutorial.model.User;
import mshj.tutorial.repository.UserRepository;
import mshj.tutorial.service.PasswordUpdateService;
import mshj.tutorial.util.ApplicationUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly=true)
public class PasswordUpdateServiceImpl extends GenericServiceImpl implements Serializable, PasswordUpdateService
{
  private static final long serialVersionUID = 1L;
  
  public PasswordUpdateServiceImpl()
  {
    super(User.class, UserRepository.class);
  }
  
  @Override
  @Transactional(readOnly=false, propagation=Propagation.REQUIRED)
  public String update(String currentPassword, String newPassword,String confNewPassword)
  {
    if (newPassword.equals(confNewPassword))
    {
      User currentUser = ApplicationUtil.getConnectedUser();
      if (currentUser.getPassword().equals(currentPassword))
      {
        currentUser.setPassword(newPassword);
        update(currentUser);
        return(ApplicationUtil.getApplicationMessage("passwordUpdated"));
        
      }
      else
      {
        return(ApplicationUtil.getApplicationMessage("wrongPassword"));
      }
    }
    else
    {
      return(ApplicationUtil.getApplicationMessage("passwordsMismatch"));
    }
  }
}


- Update mshj.tutorial.controller.PasswordUpdateController as follows:

package mshj.tutorial.controller;

import java.io.Serializable;
import mshj.tutorial.service.PasswordUpdateService;
import mshj.tutorial.util.ApplicationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class PasswordUpdateController implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  private String currentPassword;
  
  public String getCurrentPassword() {
    return currentPassword;
  }
  public void setCurrentPassword(String currentPassword) {
    this.currentPassword = currentPassword;
  }
  
  private String newPassword;
  
  public String getNewPassword() {
    return newPassword;
  }
  public void setNewPassword(String newPassword) {
    this.newPassword = newPassword;
  }
  
  private String confNewPassword;
  
  public String getConfNewPassword() {
    return confNewPassword;
  }
  public void setConfNewPassword(String confNewPassword) {
    this.confNewPassword = confNewPassword;
  }
  
  @Autowired
  private PasswordUpdateService passwordUpdateService;
  
  public PasswordUpdateService getPasswordUpdateService() {
    return passwordUpdateService;
  }
  public void setPasswordUpdateService(PasswordUpdateService updatePasswordService) {
    this.passwordUpdateService = updatePasswordService;
  }
  
  public void update()
  {
    ApplicationUtil.addGlobalJSFMessage(
        getPasswordUpdateService()
          .update(getCurrentPassword(), getNewPassword(), getConfNewPassword())
          );
  }
}

We are now done with generic components. We proceed with adding more features to the application while introducing more interesting features of JSF and Richfaces.

3.4 - Super User Features: JSF and Richfaces useful components:

We are now going to add super user features to the application. We need 3 pages for these features: a page for administrating users, a page for administrating profiles and another one for administrating roles. We start by providing the pages and the different application layer they require before explaining used JSF and Richfaces components in details.

a - Super User features:

- Under the package mshj.tutorial.controller create the following three controllers:

. UsersAdministrationController.java:

package mshj.tutorial.controller;

import java.io.Serializable;
import java.util.ArrayList;
import javax.annotation.PostConstruct;
import mshj.tutorial.model.Profile;
import mshj.tutorial.model.User;
import mshj.tutorial.service.ManageUserService;
import mshj.tutorial.util.ApplicationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class UsersAdministrationController implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  @Autowired
  private ManageUserService manageUserService;
  
  public ManageUserService getManageUserService() {
    return manageUserService;
  }
  public void setManageUserService(ManageUserService manageUserService) {
    this.manageUserService = manageUserService;
  }
  
  private User newUser;
  
  public User getNewUser() {
    return newUser;
  }
  public void setNewUser(User newUser) {
    this.newUser = newUser;
  }
  
  private String newUserConfPassword;
  
  public String getNewUserConfPassword() {
    return newUserConfPassword;
  }
  public void setNewUserConfPassword(String newUserConfPassword) {
    this.newUserConfPassword = newUserConfPassword;
  }
  
  @PostConstruct
  public void init()
  {
    clearNewUserForm();
  }
  
  public void clearNewUserForm()
  {
    setNewUser(new User());
    getNewUser().setProfiles(new ArrayList<Profile>());
    setNewUserConfPassword(getNewUser().getPassword());
  }
  
  public void addUser()
  {    
    if (getNewUserConfPassword().equals(getNewUser().getPassword()))
    {
      try
      {
        getManageUserService().addUser(getNewUser());
        clearNewUserForm();
        ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("userAdded"));
      }
      catch(Exception e)
      {
        ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("userNotAdded"));
      }
    }
    else
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("userNotAddedPasswordsMismatch"));
    }
  }
  
  public void updateUser(User user)
  {
    try
    {
      getManageUserService().updateUser(user);
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("userUpdated"));
    }
    catch (Exception e)
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("userNotUpdated"));
    }
  }
  
  public void deleteUser(User user)
  {
    getManageUserService().deleteUser(user);
    ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("userDeleted"));
  }
}


ProfilesAdministrationController.java:

package mshj.tutorial.controller;

import java.io.Serializable;
import java.util.ArrayList;
import javax.annotation.PostConstruct;
import mshj.tutorial.model.Profile;
import mshj.tutorial.model.Role;
import mshj.tutorial.service.ProfileService;
import mshj.tutorial.util.ApplicationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class ProfilesAdministrationController implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  @Autowired
  private ProfileService profileService;
  
  public ProfileService getProfileService() {
    return profileService;
  }
  public void setProfileService(ProfileService profileService) {
    this.profileService = profileService;
  }
  
  private Profile newProfile;
  
  public Profile getNewProfile() {
    return newProfile;
  }
  public void setNewProfile(Profile newProfile) {
    this.newProfile = newProfile;
  }
  
  @PostConstruct
  public void init()
  {
    clearNewProfileForm();
  }
  
  public void clearNewProfileForm()
  {
    setNewProfile(new Profile());
    getNewProfile().setRoles(new ArrayList<Role>());
  }
  
  public void addProfile()
  {
    try
    {
      getProfileService().addProfile(getNewProfile());
      clearNewProfileForm();
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("profileAdded"));
    }
    catch (Exception e)
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("profileNotAdded"));
    }
  }
  
  public void updateProfile(Profile profile)
  {
    try
    {
      getProfileService().updateProfile(profile);
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("profileUpdated"));      
    }
    catch (Exception e)
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("profileNotUpdated"));
    }
  }
  
  public void deleteProfile(Profile profile)
  {
    try
    {
      getProfileService().deleteProfile(profile);
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("profileDeleted"));
    }
    catch (Exception e)
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("profileNotDeleted"));
    }
  }
}


RolesAdministrationController.java:

package mshj.tutorial.controller;

import java.io.Serializable;
import javax.annotation.PostConstruct;
import mshj.tutorial.model.Role;
import mshj.tutorial.service.RoleService;
import mshj.tutorial.util.ApplicationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class RolesAdministrationController implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  @Autowired
  private RoleService roleService;
  
  public RoleService getRoleService() {
    return roleService;
  }
  public void setRoleService(RoleService roleService) {
    this.roleService = roleService;
  }
  
  private Role newRole;
  
  public Role getNewRole() {
    return newRole;
  }
  public void setNewRole(Role newRole) {
    this.newRole = newRole;
  }
  
  @PostConstruct
  public void init()
  {
    clearNewRoleForm();
  }
  
  public void clearNewRoleForm()
  {
    setNewRole(new Role());
  }
  
  public void addRole()
  {
    try
    {
      getRoleService().addRole(getNewRole());
      clearNewRoleForm();
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("roleAdded"));
    }
    catch (Exception e)
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("roleNotAdded"));
    }
  }
  
  public void updateRole(Role role)
  {
    try
    {
      getRoleService().updateRole(role);
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("roleUpdated"));
    }
    catch (Exception e)
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("roleNotUpdated"));
    }
  }
  
  public void deleteRole(Role role)
  {
    try
    {
      getRoleService().deleteRole(role);
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("roleDeleted"));
    }
    catch (Exception e)
    {
      ApplicationUtil.addGlobalJSFMessage(ApplicationUtil.getApplicationMessage("roleNotDeleted"));
    }
  }
}

Comments: Each of the the three controllers above uses a dedicated Service to perform usual database operations (add, update and delete) and provides a property field to hold user data for the creation of a new entry: example private User newUser; for the UsersAdministrationController.java. This field is re-initialized after each new insertion.


- Under the package mshj.tutorial.service create the following three service interfaces:

. ManageUserService.java:

package mshj.tutorial.service;

import java.util.List;

import mshj.tutorial.model.User;

public interface ManageUserService extends GenericService<User, Long>
{
  List<User> findAllButConnectedUser();
  void updateUser(User user);
  void deleteUser(User user);
  void addUser(User newUser);
}


. ProfileService.java

package mshj.tutorial.service;

import java.util.List;

import mshj.tutorial.model.Profile;

public interface ProfileService extends GenericService<Profile, Long>
{
  Profile findByTitle(String title);
  List<Profile> findAllProfiles();
  void updateProfile(Profile profile);
  void deleteProfile(Profile profile);
  void addProfile(Profile profile);
}


. RoleService.java

package mshj.tutorial.service;

import java.util.List;

import mshj.tutorial.model.Role;

public interface RoleService extends GenericService<Role, Long>
{
  Role findByTitle(String title);
  List<Role> findAllRoles();
  void updateRole(Role role);
  void deleteRole(Role role);
  void addRole(Role role);
}


Comments: Each of the three service contracts expose methods used by the controllers.



- Under the package mshj.tutorial.service.impl update the following service implementation:


. ManageUserServiceImpl.java:

package mshj.tutorial.service.impl;

import java.io.Serializable;
import java.util.List;
import mshj.tutorial.model.User;
import mshj.tutorial.repository.UserRepository;
import mshj.tutorial.service.ManageUserService;
import mshj.tutorial.util.ApplicationUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly=true)
public class ManageUserServiceImpl extends GenericServiceImpl<User, Long, UserRepository> implements Serializable, ManageUserService
{
  private static final long serialVersionUID = 1L;
  
  public ManageUserServiceImpl()
  {
    super(User.class, UserRepository.class);
  }
  
  @Override
  public List<User> findAllButConnectedUser()
  {    
    List<User> allUsers = findAll();
    allUsers.remove(ApplicationUtil.getConnectedUser());
    return(allUsers);
  }
  
  @Override
  @Transactional(readOnly=false)
  public void updateUser(User user)
  {
    update(user);
  }
  
  @Override
  @Transactional(readOnly=false)
  public void deleteUser(User user)
  {
    delete(user);
  }
  
  @Override
  @Transactional(readOnly=false)
  public void addUser(User newUser)
  {
    save(newUser);
  }
}

- Under the package mshj.tutorial.service.impl create the following two service implementations:

. ProfileServiceImpl.java

package mshj.tutorial.service.impl;
 
import java.io.Serializable;
import java.util.List;
import mshj.tutorial.model.Profile;
import mshj.tutorial.repository.ProfileRepository;
import mshj.tutorial.service.ProfileService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
@Service
@Transactional(readOnly=true)
public class ProfileServiceImpl extends GenericServiceImpl<Profile, Long, ProfileRepository> implements Serializable, ProfileService
{
  private static final long serialVersionUID = 1L;
   
  public ProfileServiceImpl()
  {
    super(Profile.class, ProfileRepository.class);
  }
    
  public Profile findByTitle(String title)
  {
    return(find("from Profile where title like '" + title + "'").get(0));
  }
  
  @Override
  public List<Profile> findAllProfiles()
  {    
    return(findAll());
  }
  
  @Override
  @Transactional(readOnly=false)
  public void updateProfile(Profile profile)
  {
    update(profile);
  }
  
  @Override
  @Transactional(readOnly=false)
  public void deleteProfile(Profile profile)
  {
    delete(profile);
  }
  
  @Override
  @Transactional(readOnly=false)
  public void addProfile(Profile newProfile)
  {
    save(newProfile);
  }  
}


. RoleServiceImpl.java

package mshj.tutorial.service.impl;
 
import java.io.Serializable;
import java.util.List;
import mshj.tutorial.model.Role;
import mshj.tutorial.repository.RoleRepository;
import mshj.tutorial.service.RoleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
@Service
@Transactional(readOnly=true)
public class RoleServiceImpl extends GenericServiceImpl<Role, Long, RoleRepository> implements Serializable, RoleService
{
  private static final long serialVersionUID = 1L;
   
  public RoleServiceImpl()
  {
    super(Role.class, RoleRepository.class);
  }
    
  public Role findByTitle(String title)
  {
    return(find("from Role where title like '" + title + "'").get(0));
  }
  
  @Override
  public List<Role> findAllRoles()
  {    
    return(findAll());
  }
  
  @Override
  @Transactional(readOnly=false)
  public void updateRole(Role role)
  {
    update(role);
  }
  
  @Override
  @Transactional(readOnly=false)
  public void deleteRole(Role role)
  {
    delete(role);
  }
  
  @Override
  @Transactional(readOnly=false)
  public void addRole(Role newRole)
  {
    save(newRole);
  }  
}


Comments: Each of the three service implementations implement their respective contract methods (reusing methods inherited from the GenericService) and declare their transactional semantics.


- Under the package mshj.tutorial.repository create the following two repository interfaces:

. ProfileRepository.java

package mshj.tutorial.repository;
 
import mshj.tutorial.model.Profile;
 
public interface ProfileRepository extends GenericRepository<Profile, Long>
{
}


. RoleRepository.java

package mshj.tutorial.repository;
 
import mshj.tutorial.model.Role;
 
public interface RoleRepository extends GenericRepository<Role, Long>
{
}


- Under the package mshj.tutorial.repository.impl create the following three service implementations:

. ProfileRepositoryImpl.java

package mshj.tutorial.repository.impl;
 
import java.io.Serializable;
import mshj.tutorial.model.Profile;
import mshj.tutorial.repository.ProfileRepository;
import org.springframework.stereotype.Repository;

@Repository
public class ProfileRepositoryImpl extends GenericRepositoryImpl<Profile, Long> implements Serializable, ProfileRepository
{
  private static final long serialVersionUID = 1L;
   
  public ProfileRepositoryImpl()
  {
    super(Profile.class);
  }
}


. RoleRepositoryImpl.java

package mshj.tutorial.repository.impl;
 
import java.io.Serializable;
import mshj.tutorial.model.Role;
import mshj.tutorial.repository.RoleRepository;
import org.springframework.stereotype.Repository;

@Repository
public class RoleRepositoryImpl extends GenericRepositoryImpl<Role, Long> implements Serializable, RoleRepository
{
  private static final long serialVersionUID = 1L;
   
  public RoleRepositoryImpl()
  {
    super(Role.class);
  }
}

Remark: Adding, Updating or Deleting Users, Profiles or Roles entries may lead to exceptions (org.springframework.dao.DataIntegrityViolationException). This happens whenever a request breaks a constraint at the database level: primary key, unique constraints or deleting entries which are referenced by foreign key in other tables etc. Putting the try/catch blocks in the service level (and not in the controller level as done here) would be a cleaner way to write the code but unfortunately the exceptions will not be caught at that level in all cases. This is due to the fact that these exceptions are thrown after the current transaction commits and that we put our transactional semantics in the service layer. A way to work around this would be to define the transactional semantics at the repository level.

- Under src/main/webapp/pages/ create the following three JSF pages:

. UsersAdministration.xhtml:

<?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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>
  
  <ui:composition template="../templates/Default.xhtml">
  
    <ui:define name = "subtitle">
      <h3>#{messages.usersAdministration}</h3>
    </ui:define>
    
    <ui:define name = "content">
      <table border = "0" align = "center">
        <tr>
          <td align = "center">
            <rich:accordion
              id = "usersAdministrationAccordion"
              rendered = "true"
              switchType = "client"
            >
              <rich:accordionItem
                id = "addNewUserAccordionItem"
                header = "#{messages.addNewUser}"
                rendered = "true"
                disabled = "false"
                switchType = "client"
              >
                <h:form id = "addNewUserAccordionItemForm">
                  <table border = "0" align = "center">
                    <tr>
                      <th align = "center">
                        <h:outputText value = "#{messages.userId}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.userName}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.password}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.confPassword}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.userIdentity}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.userProfiles}" styleClass="title-text"/>
                      </th>
                      <th align = "center"/>
                    </tr>
                    <tr>
                      <td align = "center">
                        <h:inputText id = "newUserId" value = "#{usersAdministrationController.newUser.id}" required = "true" size = "4" maxlength = "4" />
                        <h:message for = "newUserId" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:inputText id = "newUserLogin" value = "#{usersAdministrationController.newUser.login}" required = "true" size = "8" maxlength = "8" />
                        <h:message for = "newUserLogin" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:inputSecret id = "newUserPassword" value = "#{usersAdministrationController.newUser.password}" required = "true" size = "8" maxlength = "8" redisplay="true" />
                        <h:message for = "newUserPassword" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:inputSecret id = "newUserConfPassword" value = "#{usersAdministrationController.newUserConfPassword}" required = "true" size = "8" maxlength = "8" redisplay="true" />
                        <h:message for = "newUserConfPassword" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:inputText id = "newUserIdentity" value = "#{usersAdministrationController.newUser.identity}" required = "true" size = "25" maxlength = "256" />
                        <h:message for = "newUserIdentity" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <rich:pickList
                          id = "newUserProfiles"
                          value = "#{usersAdministrationController.newUser.profiles}"
                          var = "profile"
                          sourceCaption = "#{messages.availableProfiles}"
                          targetCaption = "#{messages.ownedProfiles}"
                          sourceListWidth = "450px"
                          targetListWidth = "450px"
                          listHeight = "100px"
                          orderable = "true"
                          rendered = "true"
                          disabled = "false"
                          required = "false"
                          requiredMessage = "#{messages.profileMandatory}"
                          addText  = "→"
                          addAllText  = "⇒"
                          removeText  = "←"
                          removeAllText  = "⇐"
                          upTopText  = "⇑"
                          upText  = "↑"
                          downText  = "↓"
                          downBottomText  = "⇓"
                          collectionType="java.util.ArrayList"
                        >
                          <!--
                            collectionType="java.util.ArrayList" is necessary
                            see http://manuel-palacio.blogspot.com/2011/02/hibernate-jsf-2-and-manytomany.html
                          -->
                          <f:selectItems value="#{profilesSelectItems.values}" />
                          <f:converter binding="#{profileConverter}" />
                          <rich:column
                            rendered = "true"
                          >
                            <f:facet name = "header">
                              <h:outputText
                                value = "#{messages.id}"
                                rendered = "true"
                                styleClass = "title-text"
                              />
                            </f:facet>
                            <h:outputText
                              value = "#{profile.id}"
                              rendered = "true"
                              styleClass = "data-text"
                            />
                          </rich:column>
                          <rich:column
                            rendered = "true"
                          >
                            <f:facet name = "header">
                              <h:outputText
                                value = "#{messages.title}"
                                rendered = "true"
                                styleClass = "title-text"
                              />
                            </f:facet>
                            <h:outputText
                              value = "#{profile.title}"
                              rendered = "true"
                              styleClass = "data-text"
                            />
                          </rich:column>
                        </rich:pickList>                                    
                      <h:message for = "newUserProfiles" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:commandButton value = "#{messages.add}" action = "#{usersAdministrationController.addUser()}" />
                      </td>
                    </tr>
                  </table>
                </h:form>
              </rich:accordionItem>      
              <rich:accordionItem
                id = "manageExistingUsersAccordionItem"
                header = "#{messages.manageExistingUsers}"
                rendered="true"
                disabled = "false"
                switchType = "client"
              >
                <h:form id = "manageExistingUsersAccordionItemForm">
                  <h:dataTable
                    value="#{usersAdministrationController.manageUserService.findAllButConnectedUser()}"
                    var="user"
                    styleClass="order-table"
                    headerClass="order-table-header"
                    rowClasses="order-table-odd-row,order-table-even-row"
                  >
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.userId}" styleClass="title-text"/>
                      </f:facet>
                      <h:outputText value = "#{user.id}" styleClass="data-text" />
                    </h:column>
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.userName}" styleClass="title-text"/>
                      </f:facet>
                      <h:inputText id = "userLogin" value = "#{user.login}" required = "true" size = "8" maxlength = "8" />
                      <h:message for = "userLogin" styleClass = "error-text" />
                    </h:column>
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.password}" styleClass="title-text"/>
                      </f:facet>
                      <h:inputSecret id = "userPassword" value = "#{user.password}" required = "true" size = "8" maxlength = "8" redisplay="true" />
                      <h:message for = "userPassword" styleClass = "error-text" />
                    </h:column>
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.userIdentity}" styleClass="title-text"/>
                      </f:facet>
                      <h:inputText id = "userIdentity" value = "#{user.identity}" required = "true" size = "25" maxlength = "256"  />
                      <h:message for = "userIdentity" styleClass = "error-text" />
                    </h:column>
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.userProfiles}" styleClass="title-text"/>
                      </f:facet>
                      <rich:pickList
                        id = "userProfiles"
                        value = "#{user.profiles}"
                        var = "profile"
                        sourceCaption = "#{messages.availableProfiles}"
                        targetCaption = "#{messages.ownedProfiles}"
                        sourceListWidth = "450px"
                        targetListWidth = "450px"
                        listHeight = "100px"
                        orderable = "true"
                        rendered = "true"
                        disabled = "false"
                        required = "false"
                        requiredMessage = "#{messages.profileMandatory}"
                        addText  = "→"
                        addAllText  = "⇒"
                        removeText  = "←"
                        removeAllText  = "⇐"
                        upTopText  = "⇑"
                        upText  = "↑"
                        downText  = "↓"
                        downBottomText  = "⇓"
                        collectionType="java.util.ArrayList"
                      >
                        <!--
                          collectionType="java.util.ArrayList" is necessary
                          see http://manuel-palacio.blogspot.com/2011/02/hibernate-jsf-2-and-manytomany.html
                        -->
                        <f:selectItems value="#{profilesSelectItems.values}" />
                        <f:converter binding="#{profileConverter}" />
                        <rich:column
                          rendered = "true"
                        >
                          <f:facet name = "header">
                            <h:outputText
                              value = "#{messages.id}"
                              rendered = "true"
                              styleClass = "title-text"
                            />
                          </f:facet>
                          <h:outputText
                            value = "#{profile.id}"
                            rendered = "true"
                            styleClass = "data-text"
                          />
                        </rich:column>
                        <rich:column
                          rendered = "true"
                        >
                          <f:facet name = "header">
                            <h:outputText
                              value = "#{messages.title}"
                              rendered = "true"
                              styleClass = "title-text"
                            />
                          </f:facet>
                          <h:outputText
                            value = "#{profile.title}"
                            rendered = "true"
                            styleClass = "data-text"
                          />
                        </rich:column>
                      </rich:pickList>                                    
                      <h:message for = "userProfiles" styleClass = "error-text" />
                    </h:column>              
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.administration}" styleClass="title-text"/>
                      </f:facet>
                      <h:commandButton value = "#{messages.update}" action = "#{usersAdministrationController.updateUser(user)}" />
                      <h:commandButton value = "#{messages.delete}" action = "#{usersAdministrationController.deleteUser(user)}" />
                    </h:column>
                  </h:dataTable>
                </h:form>
              </rich:accordionItem>
            </rich:accordion>
          </td>
        </tr>
      </table>
    </ui:define>
    
  </ui:composition>
  
</html>


. ProfilesAdministration.xhtml:

<?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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>
  
  <ui:composition template="../templates/Default.xhtml">
  
    <ui:define name = "subtitle">
      <h3>#{messages.profilesAdministration}</h3>
    </ui:define>
    
    <ui:define name = "content">
      <table border = "0" align = "center">
        <tr>
          <td align = "center">
            <rich:accordion
              id = "profilesAdministrationAccordion"
              rendered = "true"
              switchType = "client"
            >
              <rich:accordionItem
                id = "addNewProfileAccordionItem"
                header = "#{messages.addNewProfile}"
                rendered = "true"
                disabled = "false"
                switchType = "client"
              >
                <h:form id = "addNewProfileAccordionItemForm" >
                  <table border = "0" align = "center">
                    <tr>
                      <th align = "center">
                        <h:outputText value = "#{messages.profileId}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.profileTitle}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.profileRoles}" styleClass="title-text"/>
                      </th>
                      <th align = "center"/>
                    </tr>
                    <tr>
                      <td align = "center">
                        <h:inputText id = "newProfileId" value = "#{profilesAdministrationController.newProfile.id}" required = "true" size = "2" maxlength = "2" />
                        <h:message for = "newProfileId" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:inputText id = "newProfileTitle" value = "#{profilesAdministrationController.newProfile.title}" required = "true" size = "50" maxlength = "50" />
                        <h:message for = "newProfileTitle" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <rich:pickList
                          id = "newProfileRoles"
                          value = "#{profilesAdministrationController.newProfile.roles}"
                          var = "role"
                          sourceCaption = "#{messages.availableRoles}"
                          targetCaption = "#{messages.ownedRoles}"
                          sourceListWidth = "450px"
                          targetListWidth = "450px"
                          listHeight = "100px"
                          orderable = "true"
                          rendered = "true"
                          disabled = "false"
                          required = "false"
                          requiredMessage = "#{messages.roleMandatory}"
                          addText  = "→"
                          addAllText  = "⇒"
                          removeText  = "←"
                          removeAllText  = "⇐"
                          upTopText  = "⇑"
                          upText  = "↑"
                          downText  = "↓"
                          downBottomText  = "⇓"
                          collectionType="java.util.ArrayList"
                        >
                          <!--
                            collectionType="java.util.ArrayList" is necessary
                            see http://manuel-palacio.blogspot.com/2011/02/hibernate-jsf-2-and-manytomany.html
                          -->
                          <f:selectItems value="#{rolesSelectItems.values}" />
                          <f:converter binding="#{roleConverter}" />
                          <rich:column
                            rendered = "true"
                          >
                            <f:facet name = "header">
                              <h:outputText
                                value = "#{messages.id}"
                                rendered = "true"
                                styleClass = "title-text"
                              />
                            </f:facet>
                            <h:outputText
                              value = "#{role.id}"
                              rendered = "true"
                              styleClass = "data-text"
                            />
                          </rich:column>
                          <rich:column
                            rendered = "true"
                          >
                            <f:facet name = "header">
                              <h:outputText
                                value = "#{messages.title}"
                                rendered = "true"
                                styleClass = "title-text"
                              />
                            </f:facet>
                            <h:outputText
                              value = "#{role.title}"
                              rendered = "true"
                              styleClass = "data-text"
                            />
                          </rich:column>
                        </rich:pickList>                                    
                      <h:message for = "newProfileRoles" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:commandButton value = "#{messages.add}" action = "#{profilesAdministrationController.addProfile()}" />
                      </td>
                    </tr>
                  </table>
                </h:form>
              </rich:accordionItem>      
              <rich:accordionItem
                id = "manageExistingProfilesAccordionItem"
                header = "#{messages.manageExistingProfiles}"
                rendered="true"
                disabled = "false"
                switchType = "client"
              >
                <h:form id = "manageExistingProfilesAccordionItemForm" >
                  <h:dataTable
                    value="#{profilesAdministrationController.profileService.findAllProfiles()}"
                    var="profile"
                    styleClass="order-table"
                    headerClass="order-table-header"
                    rowClasses="order-table-odd-row,order-table-even-row"
                  >
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.profileId}" styleClass="title-text"/>
                      </f:facet>
                      <h:outputText value = "#{profile.id}" styleClass="data-text" />
                    </h:column>
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.profileTitle}" styleClass="title-text"/>
                      </f:facet>
                      <h:inputText id = "profileTitle" value = "#{profile.title}" required = "true" size = "50" maxlength = "50" />
                      <h:message for = "profileTitle" styleClass = "error-text" />
                    </h:column>
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.profileRoles}" styleClass="title-text"/>
                      </f:facet>
                      <rich:pickList
                        id = "profileRoles"
                        value = "#{profile.roles}"
                        var = "role"
                        sourceCaption = "#{messages.availableRoles}"
                        targetCaption = "#{messages.ownedRoles}"
                        sourceListWidth = "450px"
                        targetListWidth = "450px"
                        listHeight = "100px"
                        orderable = "true"
                        rendered = "true"
                        disabled = "false"
                        required = "false"
                        requiredMessage = "#{messages.roleMandatory}"
                        addText  = "→"
                        addAllText  = "⇒"
                        removeText  = "←"
                        removeAllText  = "⇐"
                        upTopText  = "⇑"
                        upText  = "↑"
                        downText  = "↓"
                        downBottomText  = "⇓"
                        collectionType="java.util.ArrayList"
                      >
                        <!--
                          collectionType="java.util.ArrayList" is necessary
                          see http://manuel-palacio.blogspot.com/2011/02/hibernate-jsf-2-and-manytomany.html
                        -->
                        <f:selectItems value="#{rolesSelectItems.values}" />
                        <f:converter binding="#{roleConverter}" />
                        <rich:column
                          rendered = "true"
                        >
                          <f:facet name = "header">
                            <h:outputText
                              value = "#{messages.id}"
                              rendered = "true"
                              styleClass = "title-text"
                            />
                          </f:facet>
                          <h:outputText
                            value = "#{role.id}"
                            rendered = "true"
                            styleClass = "data-text"
                          />
                        </rich:column>
                        <rich:column
                          rendered = "true"
                        >
                          <f:facet name = "header">
                            <h:outputText
                              value = "#{messages.title}"
                              rendered = "true"
                              styleClass = "title-text"
                            />
                          </f:facet>
                          <h:outputText
                            value = "#{role.title}"
                            rendered = "true"
                            styleClass = "data-text"
                          />
                        </rich:column>
                      </rich:pickList>                                    
                      <h:message for = "profileRoles" styleClass = "error-text" />
                    </h:column>              
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.administration}" styleClass="title-text"/>
                      </f:facet>
                      <h:commandButton value = "#{messages.update}" action = "#{profilesAdministrationController.updateProfile(profile)}" />
                      <h:commandButton value = "#{messages.delete}" action = "#{profilesAdministrationController.deleteProfile(profile)}" />
                    </h:column>
                  </h:dataTable>
                </h:form>
              </rich:accordionItem>
            </rich:accordion>
          </td>
        </tr>
      </table>
    </ui:define>
    
  </ui:composition>
  
</html>


. RolesAdministration.xhtml:

<?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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>
  
  <ui:composition template="../templates/Default.xhtml">
  
    <ui:define name = "subtitle">
      <h3>#{messages.rolesAdministration}</h3>
    </ui:define>
    
    <ui:define name = "content">
      <table border = "0" align = "center">
        <tr>
          <td align = "center">
            <rich:accordion
              id = "rolesAdministrationAccordion"
              rendered = "true"
              switchType = "client"
            >
              <rich:accordionItem
                id = "addNewRoleAccordionItem"
                header = "#{messages.addNewRole}"
                rendered = "true"
                disabled = "false"
                switchType = "client"
              >
                <h:form id = "addNewRoleAccordionItemForm" >
                  <table border = "0" align = "center">
                    <tr>
                      <th align = "center">
                        <h:outputText value = "#{messages.roleId}" styleClass="title-text"/>
                      </th>
                      <th align = "center">
                        <h:outputText value = "#{messages.roleTitle}" styleClass="title-text"/>
                      </th>
                      <th align = "center"/>
                    </tr>
                    <tr>
                      <td align = "center">
                        <h:inputText id = "newRoleId" value = "#{rolesAdministrationController.newRole.id}" required = "true" size = "2" maxlength = "2" />
                        <h:message for = "newRoleId" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:inputText id = "newRoleTitle" value = "#{rolesAdministrationController.newRole.title}" required = "true" size = "50" maxlength = "50" />
                        <h:message for = "newRoleTitle" styleClass = "error-text" />
                      </td>
                      <td align = "center">
                        <h:commandButton value = "#{messages.add}" action = "#{rolesAdministrationController.addRole()}" />
                      </td>
                    </tr>
                  </table>
                </h:form>
              </rich:accordionItem>      
              <rich:accordionItem
                id = "manageExistingRolesAccordionItem"
                header = "#{messages.manageExistingRoles}"
                rendered="true"
                disabled = "false"
                switchType = "client"
              >
                <h:form id = "manageExistingRolesAccordionItemForm" >
                  <h:dataTable
                    value="#{rolesAdministrationController.roleService.findAllRoles()}"
                    var="role"
                    styleClass="order-table"
                    headerClass="order-table-header"
                    rowClasses="order-table-odd-row,order-table-even-row"
                  >
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.roleId}" styleClass="title-text"/>
                      </f:facet>
                      <h:outputText value = "#{role.id}" styleClass="data-text" />
                    </h:column>
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.roleTitle}" styleClass="title-text"/>
                      </f:facet>
                      <h:inputText id = "roleTitle" value = "#{role.title}" required = "true" size = "50" maxlength = "50" />
                      <h:message for = "roleTitle" styleClass = "error-text" />
                    </h:column>            
                    <h:column>
                      <f:facet name="header">
                        <h:outputText value = "#{messages.administration}" styleClass="title-text"/>
                      </f:facet>
                      <h:commandButton value = "#{messages.update}" action = "#{rolesAdministrationController.updateRole(role)}" />
                      <h:commandButton value = "#{messages.delete}" action = "#{rolesAdministrationController.deleteRole(role)}" />
                    </h:column>
                  </h:dataTable>
                </h:form>
              </rich:accordionItem>
            </rich:accordion>
          </td>
        </tr>
      </table>
    </ui:define>
    
  </ui:composition>
  
</html>


In the following we describe the different JSF components used in the three pages. To fix ideas we provide explanations only for the UsersAdministration.xhtml page as the other pages expose more or less the sames features.


b - RichFaces Accordion:

The <rich:accordion /> contains a set of panels (<rich:accordionItems />) where one panel is expanded, while the other ones are collapsed. When a collapsed panel's header is clicked, the panel is expanded and the previously expanded one is in turn collapsed (i.e. at any instant only one panel is expanded).
In the UsersAdministration.xhtml we define such a component with two panels: one that holds the adding a new user feature, the second for managing existing users. Read more about <rich:accordion /> ... Online demo.


c - JSF DataTable:


The <h:dataTable /> component is meant for rendering tables. Lines of the table are based on a underlying collection of objects and its columns can be defined using <h:column /> tag and are possibly depending on the underlying object's fields.
In the UsersAdministration.xhtml we use such a component (Lines 157-267) to display the list of all users. We discuss two important attributes of the <h:dataTable /> component:

- value: specifies the underlying collection of objects that will be displayed. Example: in Line 158, we define this value as the List of User objects returned by the ManageUserService's findAllButConnectedUser() method.

- var: specifies an alias for an iterator over the underlying collection of objects. This alias can then be called inside <h:column /> to get for example a specific field of an object in the underlying collection.

In Lines 164-169 we declare the first column to be displayed by the data table. For the <h:column /> tag a header facet (the title of the column) can be defined: in our case using a <h:outputText /> tag in Line 166. Then the content of the column itself is specified, again a <h:outputText /> in Line 168. Note how the content of the latter <h:outputText /> is using the alias: value = "#{user.id}", meaning here that the <h:outputText /> will display the id field of the current User object.

Read more about <h:dataTable /> ...


d - RichFaces Pick Lists:

<rich: pickList /> is a component permitting to handle selecting multiple options from a list of available ones. Indeed it displays two lists: the available options and the selected ones. Using dedicated buttons (copy, copyAll, remove, removeAll) the user can move elements from one list to the other thus selecting or deselecting options.
In the UsersAdministration.xhtml we use pick lists to handle the profiles owned by a user: when adding a new one (Lines 79-141) and when managing an existing one (Lines 195-257). For the latter this means that we will display as many pick lists as users displayed by the data table.
We discuss here some of the interesting attributes and children of the pick list (on the basis of the pick list in Lines 79-141): 

Attributes:

- value: tells JSF where to store the selected options of the pick list. In our case we set this attribute to #{usersAdministrationController.newUser.profiles} since we want to collect the selected profiles for the user to be created in the profiles field of the newUser field of the controller.

- var: defines an alias that will be used to refer to an item in the pick list's both available or selected options lists.

- sourceCaption: defines the title of the available options list.

- targetCaption: defines the title of the selected options list.

- requiredMessage: defines the validation message that is displayed to the user if the required 

attribute is set to true and if the user selects no item in the pick list.

- other attributes: do what their names say they do :) Ranging from styling of the pick list to providing titles of the different buttons (copy, remove, copyAll, removeAll ...)

- collectionType: In some settings (included ours) this attribute is vital ! It says to JSF the type of collection used for the available and selected options lists. If you do not specify this attribute with the correct concrete (no interfaces) type (in our case it is java.list.ArrayList and certainly not java.list.List) you'll have the following exception raised:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

It seems this is an old RichFaces bug which has been fixed in the RichFaces 4.3.0.CR1 release and that curiously reappeared in newer releases !


Children Elements:

- <f:selectItems />: tells JSF how to populate the list of available options for the pick list. The value attribute of this element should refer to a collection (in our case java.util.ArrayList) of javax.faces.model.SelectItem. In the first pick list of UsersAdministration.xhtml (Line 107) we use a dedicated controller (ProfilesSelectItems) to provide the list of all available profiles through its values field.
In fact two such controllers are needed for the super user admin features, the one for providing available profiles used here and another one for providing available roles used in the ProfilesAdministration.xhtml page. We provide their implementations below:

- Under the package mshj.tutorial.controller.si create the two following controllers:

. ProfileSelectItems.java:

package mshj.tutorial.controller.si;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.model.SelectItem;
import mshj.tutorial.model.Profile;
import mshj.tutorial.service.ProfileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class ProfilesSelectItems implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  @Autowired
  private ProfileService profileService;

  public ProfileService getProfileService() {
    return profileService;
  }
  public void setProfileService(ProfileService profileService) {
    this.profileService = profileService;
  }
  
  private List<SelectItem> values;
  
  public List<SelectItem> getValues() {
    return values;
  }
  public void setValues(List<SelectItem> values) {
    this.values = values;
  }
  
  @PostConstruct
  public void init()
  {
    setValues(new ArrayList<SelectItem>());
    
    for (Profile p : getProfileService().findAll())
    {
      getValues().add(new SelectItem(p,p.getTitle()));
    }
  }
}


. RolesSelectItems.java

package mshj.tutorial.controller.si;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.model.SelectItem;
import mshj.tutorial.model.Role;
import mshj.tutorial.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class RolesSelectItems implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  @Autowired
  private RoleService roleService;

  public RoleService getRoleService() {
    return roleService;
  }
  public void setRoleService(RoleService roleService) {
    this.roleService = roleService;
  }
  
  private List<SelectItem> values;
  
  public List<SelectItem> getValues() {
    return values;
  }
  public void setValues(List<SelectItem> values) {
    this.values = values;
  }
  
  @PostConstruct
  public void init()
  {
    setValues(new ArrayList<SelectItem>());
    
    for (Role r : getRoleService().findAll())
    {
      getValues().add(new SelectItem(r,r.getTitle()));
    }
  }
}

Comments: Note that both controllers provide a list of select items (javax.faces.model.SelectItem instances) which will be used in the <f:selectItems /> bound to rich pick lists in the corresponding pages. A javax.faces.model.SelectItem is simply constructed by providing two arguments: the value and its label.

<f:converter />: tells JSF how to convert labels to their values. This is needed to correctly fill in the target value of a rich pick list. The binding attribute refers to a bean implementing javax.faces.convert.Converter.

We need two converters one for converting profiles and another one for converting roles.

e - JSF Converters:

 JSF comes with a set of built-in converters. An example is LongConverter. It is called whenever, let's say, a <h: inputText /> is bound to a field of type Long (or long) in the controller.
Why ? Simply because at the html level only String inputs are supported. The LongConverter is then needed to convert the String value of the input to a Long (or long) one which matches the controller's expectations. By the way if the conversion is not possible (user typed alphanumerical value) the validation phase (which starts after the conversion phase) will prompt the validation message javax.faces.converter.LongConverter.LONG.

We discuss here Custom JSF Converters. So why they are also needed for our case ?
In fact at the html level select options support only mapping String labels to String values.
So what JSF will get as list of selected options will be only the list of their labels and not the list of their values. The task of the JSF converter is to restore complete objects given their labels.

- Under the package mshj.tutorial.converter create the following two converters:

. ProfileConverter.java:

package mshj.tutorial.converter;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import mshj.tutorial.model.Profile;
import mshj.tutorial.service.ProfileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("session")
public class ProfileConverter implements Converter
{
  @Autowired
  private ProfileService profileService;
  
  public ProfileService getProfileService() {
    return profileService;
  }
  public void setProfileService(ProfileService profileService) {
    this.profileService = profileService;
  }

  @Override
  public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
  {
    return(getProfileService().findByTitle(arg2));
  }

  @Override
  public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
  {
    return (((Profile)arg2).getTitle());
  }
}


. RoleConverter.java

package mshj.tutorial.converter;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import mshj.tutorial.model.Role;
import mshj.tutorial.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("session")
public class RoleConverter implements Converter
{
  @Autowired
  private RoleService roleService;
  
  public RoleService getRoleService() {
    return roleService;
  }
  public void setRoleService(RoleService roleService) {
    this.roleService = roleService;
  }

  @Override
  public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
  {
    return(getRoleService().findByTitle(arg2));
  }

  @Override
  public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
  {
    return (((Role)arg2).getTitle());
  }
}

Comments: A Faces Converter implements the javax.faces.convert.Converter interface thus it should implement two methods:
Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) and String getAsString(FacesContext arg0, UIComponent arg1, String arg2) which respectively return the object giving its label and the label given the object.
Since we used the title field (for both profile and role objects) as label (see ProfileSelectItems.java and RoleSelectItems.java above), we rely in implementing the getAsObject methods on services that provide the object given its title.
What should be noticed here is that the services are auto-wired to the converter and that the converter is annotated as a Spring component (@Component).

- Still some configuration is needed to have our pick lists working, JSF should be able to compare objects appearing in our pick lists. For both Role and Profile model classes we have to define boolean equals(Object o) and int hashCode() methods, as follows:

. Edit mshj.tutorial.model.Profile and add the following methods:

  @Override
  public boolean equals(Object o)
  {
    if (o instanceof Profile)
    {
      return(((Profile)o).getId() == getId());
    }
    else
    {
      return(false);
    }
  }
  
  @Override
  public int hashCode()
  {
    return(new Long(getId()).intValue());
  }


. Edit mshj.tutorial.model.Role and add the following methods:

  @Override
  public boolean equals(Object o)
  {
    if (o instanceof Role)
    {
      return(((Role)o).getId() == getId());
    }
    else
    {
      return(false);
    }
  }
  
  @Override
  public int hashCode()
  {
    return(new Long(getId()).intValue());
  }

f - JSF Navigation:

Now that we have four pages in our application it is time to design a menu bar to navigate from one page to another. To have the menu bar available to all pages we only have to add it once to the template Default.xhtml.

- Edit the src/main/webapp/templates/Default.xhtml 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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>

    <h:head>
    <link type="text/css" rel="stylesheet" href="../resources/css/myCSS.css" />
    </h:head>

  <body>

    <div id = "header_div">
      <div id = "banner_div" align = "center">
        <ui:insert name = "title">
          <h:graphicImage value="../resources/images/logo.png" />
        </ui:insert>
      </div>
      
      <div id = "subtitle_div" align = "center">
        <ui:insert name = "subtitle"/>
      </div>
      
      <div id = "status_div" align = "center">
        <ui:insert name = "status" />
      </div>
          
    </div>
    
    <div id = "menu_div"  align = "center">
      <ui:insert name = "menu">
        <h:form id = "menuForm">
          <table border = "0" align = "center">
            <tr>
              <td align = "center">
                <rich:menuItem action = "PasswordUpdate" >
                  <h:outputText value = "#{messages.updatePassword}" styleClass = "big-title-text-with-bg" />
                </rich:menuItem>
              </td>
              <td align = "center">
                <rich:menuItem action = "UsersAdministration" >
                  <h:outputText value = "#{messages.usersAdministration}" styleClass = "big-title-text-with-bg" />
                </rich:menuItem>
              </td>
              <td align = "center">
                <rich:menuItem action = "ProfilesAdministration" >
                  <h:outputText value = "#{messages.profilesAdministration}" styleClass = "big-title-text-with-bg" />
                </rich:menuItem>
              </td>
              <td align = "center">
                <rich:menuItem action = "RolesAdministration" >
                  <h:outputText value = "#{messages.rolesAdministration}" styleClass = "big-title-text-with-bg" />
                </rich:menuItem>
              </td>
              <td align = "center">
                <rich:menuItem action = "#{loginController.doLogout()}" >
                  <h:graphicImage value="../resources/images/logout.png" width = "64" height = "64" />
                </rich:menuItem>
              </td>
            </tr>
          </table>
          
        </h:form>      
      </ui:insert>
    </div>
    
    <div id = "messages_div"  align = "center">
      <ui:insert name = "messages">
        <table border = "0" align = "center">
          <tr>
            <td align = "center">
              <h:messages globalOnly="true" styleClass="error-text" />
            </td>
          </tr>
        </table>
      </ui:insert>
    </div>
    
    <div id = "content_div" align = "center">
      <ui:insert name = "content"/>
    </div>
    
    <div id = "footer_div"  align = "center">
      <ui:insert name = "footer">
        <h:graphicImage value="../resources/images/footer.png" />
      </ui:insert>
    </div>

  </body>
  
</html>


We added in the menu insertion different entries permitting to navigate to the different application pages. To define a menu entry we use the <rich:menuItem /> tag. Each <rich:menuItem /> has an action attribute which indicates the target of the entry (the page relative path for example) and encapsulates a JSF component which represents the element to be displayed for that menu entry (in our case simple <h:outputText />'s).

Since we do not want the Connection.xhtml to display the menu bar we will have to override its menu insertion by an empty one:

- Edit src/main/webapp/pages/Connection.xhtml 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:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:rich="http://richfaces.org/rich"
  xmlns:a4j="http://richfaces.org/a4j"
>
  
  <ui:composition template="../templates/Default.xhtml">
  
    <ui:define name = "subtitle">
      <h3>#{messages.login}</h3>
    </ui:define>
    
    <ui:define name = "menu" />
    
    <ui:define name = "content">
      <h:form id = "loginForm">
        <table align = "center" border = "0">
          <tr>
            <th align = "center">
              <h:outputText value = "#{messages.userName}" styleClass="title-text" />
            </th>
            <td align = "center">
              <h:inputText 
                id = "j_username"
                value = "#{loginController.j_username}"
                label = "J_username"
                size = "8"
                maxlength = "8"
                required = "true"
              />
            </td>
            <td align = "center">  
              <h:message for = "j_username" styleClass="error-text" />
            </td>
          </tr>
          <tr>
            <th align = "center">
              <h:outputText value = "#{messages.password}" styleClass="title-text" />
            </th>
            <td align = "center">
              <h:inputSecret
                id = "j_password"
                value = "#{loginController.j_password}"
                label = "J_password"
                size = "8"
                maxlength = "8"
                required = "true"
              />
            </td>
            <td align = "center">
              <h:message for = "j_password" styleClass="error-text" />
            </td>
          </tr>
          <tr>
            <td align = "center" colspan = "3">
              <h:commandButton
                value = "#{messages.login}"
                action = "#{loginController.doLogin()}"
              >
                <f:phaseListener type="mshj.tutorial.listener.LoginErrorPhaseListener" />
              </h:commandButton>
            </td>
          </tr>
        </table>
      </h:form>
    </ui:define>
    
  </ui:composition>
  
</html>

Line 18 : We overrode the menu insertion (by an empty insertion).

Note that in the Default.xhtml page the <rich:menuItem /> action attributes are simple String values. Do JSF know which page to navigate to given a String value ? Yes if we carefully choose the string returned values of action methods: if an action methods returns the name of page without the extension the user will be redirected to that page after the action method is executed. See here for more details about the so-called implicit navigation. Or if we configure JSF well (explicit navigation), see below:

- Edit src/main/webapp/WEB-INF/faces-config.xml as follows:

<?xml version="1.0"?>
<faces-config
    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"
    version="2.0">
    
  <!-- Resource Bundles (Messages and JSF Messages) -->
  <application>
    <locale-config>
      <default-locale>en</default-locale>
      <supported-locale>fr_FR</supported-locale>
    </locale-config>  
    <resource-bundle>
      <base-name>messages</base-name>
      <var>messages</var>
    </resource-bundle>
    <message-bundle>jsfMessages</message-bundle>
  </application>    
    
    <!-- JSF and Spring are integrated -->
    <application>
      <el-resolver>
        org.springframework.web.jsf.el.SpringBeanFacesELResolver
      </el-resolver>
    </application>
    
  <navigation-rule>
    <from-view-id>*</from-view-id>
    <navigation-case>
      <from-outcome>PasswordUpdate</from-outcome>
      <to-view-id>/pages/PasswordUpdate.xhtml</to-view-id>
      <redirect />
    </navigation-case>
    <navigation-case>
      <from-outcome>UsersAdministration</from-outcome>
      <to-view-id>/pages/UsersAdministration.xhtml</to-view-id>
      <redirect />
    </navigation-case>
    <navigation-case>
      <from-outcome>ProfilesAdministration</from-outcome>
      <to-view-id>/pages/ProfilesAdministration.xhtml</to-view-id>
      <redirect />
    </navigation-case>
    <navigation-case>
      <from-outcome>RolesAdministration</from-outcome>
      <to-view-id>/pages/RolesAdministration.xhtml</to-view-id>
      <redirect />
    </navigation-case>
  </navigation-rule>
    
</faces-config>


In Lines 29-51 we define a navigation rule (one can define many). It says that from any page (or view) (Line 30) several navigation cases are possible. A navigation case (example Lines 31-35) takes a from-outcome which specifies a String value associated to a to-view-id which specifies a page.

Given this configuration, each time JSF will encounter a String value in an action attribute (not only associated to <rich:menuItem /> but also to <h:commandLink /> or <h:commandButton />), it will check if a corresponding navigation case is concerned and if yes it will trigger a redirection to the corresponding page.

3.5 - Spring Security Access Control:


We discuss now how to control the access to the application pages by checking whether a user owns the required roles.

First let's create some additional users and grant them some privileges:

- Execute the following script:

. MySQL:

connect test/test;

USE `mshj` ;

insert into Roles values(0,'ROLE_DUMMY_0');
insert into Roles values(1,'ROLE_DUMMY_1');
insert into Roles values(2,'ROLE_DUMMY_2');
insert into Roles values(3,'ROLE_DUMMY_3');
insert into Roles values(10,'ROLE_UPDATE_OWN_PASSWORD'); 
insert into Roles values(21,'ROLE_ADMIN_USERS');
insert into Roles values(22,'ROLE_ADMIN_PROFILES');
insert into Roles values(23,'ROLE_ADMIN_ROLES');

insert into Profiles values(1,'DUMMY_PROFILE_1');
insert into Profiles values(2,'DUMMY_PROFILE_2');
insert into Profiles values(3,'DUMMY_PROFILE_3');
insert into Profiles values(10,'PROFILE_USER');
insert into Profiles values(20,'PROFILE_ADMIN');

insert into Users values(1,'user1','user1','User One');
insert into Users values(2,'user2','user2','User Two');
insert into Users values(20, 'admin','admin','Administrator');

insert into Profiles_Roles values(1,0);
insert into Profiles_Roles values(1,1);
insert into Profiles_Roles values(2,0);
insert into Profiles_Roles values(2,2);
insert into Profiles_Roles values(3,0);
insert into Profiles_Roles values(3,3);
insert into Profiles_Roles values(10,10);
insert into Profiles_Roles values(20,10);
insert into Profiles_Roles values(20,21);
insert into Profiles_Roles values(20,22);
insert into Profiles_Roles values(20,23);

insert into Users_Profiles values(1,1);
insert into Users_Profiles values(2,2);
insert into Users_Profiles values(0,10);
insert into Users_Profiles values(20,20);

commit; 


. Oracle:

connect test/test;

insert into Roles values(0,'ROLE_DUMMY_0');
insert into Roles values(1,'ROLE_DUMMY_1');
insert into Roles values(2,'ROLE_DUMMY_2');
insert into Roles values(3,'ROLE_DUMMY_3');
insert into Roles values(10,'ROLE_UPDATE_OWN_PASSWORD'); 
insert into Roles values(21,'ROLE_ADMIN_USERS');
insert into Roles values(22,'ROLE_ADMIN_PROFILES');
insert into Roles values(23,'ROLE_ADMIN_ROLES');

insert into Profiles values(1,'DUMMY_PROFILE_1');
insert into Profiles values(2,'DUMMY_PROFILE_2');
insert into Profiles values(3,'DUMMY_PROFILE_3');
insert into Profiles values(10,'PROFILE_USER');
insert into Profiles values(20,'PROFILE_ADMIN');

insert into Users values(1,'user1','user1','User One');
insert into Users values(2,'user2','user2','User Two');
insert into Users values(20, 'admin','admin','Administrator');

insert into Profiles_Roles values(1,0);
insert into Profiles_Roles values(1,1);
insert into Profiles_Roles values(2,0);
insert into Profiles_Roles values(2,2);
insert into Profiles_Roles values(3,0);
insert into Profiles_Roles values(3,3);
insert into Profiles_Roles values(10,10);
insert into Profiles_Roles values(20,10);
insert into Profiles_Roles values(20,21);
insert into Profiles_Roles values(20,22);
insert into Profiles_Roles values(20,23);

insert into Users_Profiles values(1,1);
insert into Users_Profiles values(2,2);
insert into Users_Profiles values(0,10);
insert into Users_Profiles values(20,20);

commit; 


- Now edit src/main/webapp/WEB-INF/applicationContext-security.xml and between the two comment lines:

    <!-- Begin Handling of Security Permission per Page -->
    <!-- End Handling of Security Permission per Page -->


add the following lines:

    <security:intercept-url pattern="/pages/PasswordUpdate.xhtml" access="ROLE_UPDATE_OWN_PASSWORD" />
    <security:intercept-url pattern="/pages/UsersAdministration.xhtml" access="ROLE_ADMIN_USERS" />
    <security:intercept-url pattern="/pages/ProfilesAdministration.xhtml" access="ROLE_ADMIN_PROFILES" />
    <security:intercept-url pattern="/pages/RolesAdministration.xhtml" access="ROLE_ADMIN_ROLES" />


Every <security:intercept-url /> specifies here that every page (or pattern of pages) defined by the pattern attribute should only be accessed if the connected user has the roles (or the set of roles) defined in the attribute access.

- Finally let's update MyAuthenticationSuccessHandler to take into account the new pages and access policy:

package mshj.tutorial.handler;

import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException
    {
    Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
    
    if (roles.contains("ROLE_ADMIN_USERS"))
      {
      response.sendRedirect("UsersAdministration.xhtml");
      return;
      }
      
    if (roles.contains("ROLE_ADMIN_PROFILES"))
      {
      response.sendRedirect("UsersAdministration.xhtml");
      return;
      }
    
    if (roles.contains("ROLE_ADMIN_ROLES"))
      {
      response.sendRedirect("UsersAdministration.xhtml");
      return;
      }
    
    if (roles.contains("ROLE_UPDATE_OWN_PASSWORD"))
      {
      response.sendRedirect("PasswordUpdate.xhtml");
      return;
      }
    
    if (roles.isEmpty() || (roles.contains("ROLE_DUMMY_0")) || (roles.contains("ROLE_DUMMY_1")) || (roles.contains("ROLE_DUMMY_2")))
      {
      response.sendRedirect("../index.jsp");
      return;
      }
    
        roles = null;
    }
}



Now you can compile and deploy the application and test the freshly created new users access rights.
If you are not allowed to access a page and try to display it you'll see the AccessDenied.xhtml page.

Here we reach the end of this tutorial which, i hope, was useful for you. I can point you to some perspective work which you can take as an exercise:

The menu bar displays all the application pages even the ones to which the currently connected user has not access. Modify the code such that only the menu entries that will not lead to a denial of access are displayed. (Tip: use the set of roles owned by the connected user to condition the visibility of the <rich:menuItem />'s).


Below the links to download the final Eclipse projects. After importing the project you'll probably have to fix some properties to match your local settings (such as the JDK and the Apache Tomcat Server).

If you need to quickly rename the project and its packages to your custom values take a look at this tutorial.





Previous | Next

1 comment: