Wednesday, April 24, 2013

Maven / Spring / Hibernate / JSF2 Tutorial - Second Part (Skeleton JEE Web Application)

In this part we build and describe a skeleton (minimal) JEE Web Application. This application relies on Spring Security to filter access to dummy Hello World ! page to only authenticated users.
First we discuss the tutorial prerequisites then we give instructions to build the Web Application.


Outline:

2.1 - Tutorial Prerequisites.
  a - JDK
  b - Eclipse
  c - Tomcat
  d - Maven
  e - Oracle (XE)
  f - MySQL

2.2 - Design of a skeleton test project.
  a - Creating a Maven Project
  b - Creating a Dynamic Web Project with Eclipse
  c - Creating a "Mavenised" Eclipse Dynamic Web Project
  d - Creating the database underlying the Project
  e - Creating the Model Java Beans
  f - Implementing the UserDetails
  g - Implementing the (Data Access Object) DAO or Repository layer
  h - Implementing the (Business Object) BO or Service layer
  i - Implementing the Presentation Layer
  j - Configuring the Skeleton Application
    - Configuring the Database Connection
    - Telling Spring about the Database Connection
    - Configuring Hibernate
    - Configuring Logs
    - Configuring the Application General Context
    - Configuring the Application Transaction Management Context
    - Configuring the Application (Spring) Security Context
    - Implementing an AuthenticationSuccessHandler bean
    - Handling Login issues
    - Configuring JSF
    - Putting all together
  k - Deploying and testing the Skeleton Application


2.1 - Tutorial prerequisites:

We need the Java Developement Kit (JDK), an IDE (Eclipse Juno) supporting JSF and the Java Persistence API (JPA), an application server (Apache Tomcat), a library manager (Apache Maven) and a database server (Oracle XE or MySQL).

Please follow carefully the install and configuration instructions given below:

a - JDK:

. If no JDK is installed on your machine, download and install the last version of the JDK suitable for your system and processor architecture.
. If not yet defined, define a system environment variable named JAVA_HOME and set it to the path of your JDK.

b - Eclipse:

. If Eclipse IDE is not installed on your machine, download ans install the last version of Eclipse (Eclipse IDE for Java EE Developers) suitable for your system and processor architecture.


- Eclipse JSF support
. Download the following .jar files:
- jsf-api.jar
- jsf-impl.jar

. Open Eclipse IDE and go to the sub-menu Window>Preferences>Java>Build Path>User Libraries>
and click the New button. As the User Library Name choose: JSF 2.0 (Mojarra 2.0.3-FCS)
. Select the newly created User Library and click Add External Jars ... then add the two files downloaded earlier.

- Eclipse JPA support
. Download the following .jar files:
javax.persistence_2.0.4.v201112161009.jar
eclipselink.jar

. Open Eclipse IDE and go to the sub-menu Window>Preferences>Java>Build Path>User Libraries>
and click the New button. As the User Library Name choose: JPA 2.0 (EclipseLink 2.4.1 - Juno)
. Select the newly created User Library and click Add External Jars ... then add the two files downloaded earlier.

c - Tomcat:

. If Apache Tomcat (7.0) is not installed on your machine, download it and install it.
. The Windows distribution proposes configuration of Apache Tomcat during install. In this case make sure you do the following:
- Select the port 8081 for the Apache Tomcat Manager (GUI).
- Define a user authorized to use Apache Tomcat Manager. Add these roles to the user: tomcat, admin, manager-gui, manager-script, manager-jmx, manager-status.
. For Linux distribution you'll have to manually configure Apache Tomcat. In this case make sure you do the following:
- Edit the Apache Tomcat configuration file conf/server.xml and look for the following code:

<connector connectiontimeout="20000" port="8080" protocol="HTTP/1.1" redirectport="8443">

and change the 8080 value of the port to 8081.
- Edit the Apache Tomcat Configuration file conf/tomcat-users.xml and modify it to have the following result:

<tomcat-users>
    <!--
        Some XML Code ...
    -->
    <role rolename="tomcat"/>
    <role rolename="manager-gui"/>
    <role rolename="manager-script"/>
    <role rolename="manager-jmx"/>
    <role rolename="manager-status"/>
    <role rolename="admin"/>
    <user password="mot_de_passe" roles="tomcat,admin,manager-gui,manager-script,manager-jmx,manager-status" username="nom_utilisateur"/>
</tomcat-users>

d - Maven:

. If Maven is not installed on your machine download it and install it (just extract the archive at some location).
. If not yet defined, define a system environment variable named M2_HOME and set it to the path of the resulting Maven directory.
. If not yet done, edit the system environment variable named PATH and concatenate to its values ;%M2_HOME%/bin if you are running Windows or ;$M2_HOME/bin if you are running a Linux distribution.
. If you connect to the internet via a proxy server edit the Maven conf/settings.xml file and make sure to insert the following code inside the <proxies/> tag:


    optional id
    true
    http
    optional proxy user name
    optional proxy user password
    proxy server IP address
    proxy server port
    localhost


In the following you can install one or both Database servers: Oracle XE and/or MySQL.

e - Oracle (XE):

. If Oracle Database Express Edition is not installed on your machine download it and install it.
At the time of writing this tutorial only 64 bits versions of Oracle XE for both Windows and Linux distributions are available. If your machine is 32 bits try downloading full Oracle Database distributions or switch to MySQL.
. On Linux distributions you must run a configuration script right after the install is done. This script particularly defines the port of the Database Management Web Interface which you must set to 8080 (in fact any other value different from the one set for Apache Tomcat Manager). On Fedora the script is launched using the following command /etc/init.d/oracle-xe configure.

f - MySQL:

. If MySQL is not installed on your machine download it and install it.
. For Windows' you should be asked for the MySQL root password during the install.
. For Linux you must define a root password and make sure your MySQL server is running after install. Instructions depend on whether you installed your MySQL Server as a service or not. Try one of these two sets of instructions being logged as root:

/etc/init.d/mysql start
mysqladmin -u root password Your_MySQL_root_New_Password
/etc/init.d/mysql restart

or

/sbin/chkconfig mysqld on
/sbin/service mysqld start
mysqladmin -u root password Your_MySQL_root_New_Password
/sbin/service mysqld restart

2.2 - Design of a skeleton test project:

The result is a JEE Web Application with a functional authentication page (the entry point of any secured JEE Web Application). We design the skeleton test project in three steps:

We create a Maven Project.
We create a Dynamic Web Project with Eclipse having the same name.
We merge the two projects and write down: configuration files for Spring, Hibernate and JSF and some java code for handling user authentication. In this last step we also create a Database user owning some tables needed by Spring Security.

Important: In the remainder pay a "religious" attention to the character case (upper an lower).

a - Creating a Maven Project:

. Open a command console (under windows use cmd.exe) and set your working directory to a known path:

cd /someFolder/.../someSubFolder/

(in the remainder we'll refer to this path as $MAVEN_PROJECT_PATH).
. Type the following command:

mvn archetype:generate -DgroupId=mshj.tutorial -DartifactId=Test -Dpackagename=mshj.tutorial.test -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.0 -DinteractiveMode=false

. Under $MAVEN_PROJECT_PATH you have now a new folder named Test. It contains your newly created Maven Project.

b - Creating a Dynamic Web Project with Eclipse:

. In Eclipse go to the menu File>New>Other>Web> and choose the option Dynamic Web Project then follow the wizard while ensuring the following:
. In the Project field type Test (i.e. the same name as the for the Maven Project).
. In Target Runtime choose Tomcat V7.0 (you may be asked to add a Tomcat V7.0 server to Eclipse's context if this is not already done. For this you'll need to specify the install directory path of Tomcat), then click next button.
. Leave the page entitled Java unchanged and click next button again.
. Change the value of the field Content Directory from WebContent to src/main/webapp (this is the standard location where Maven Project stores its Web Content).
. Check the box generate web.xml deployment descriptor and click finish.
. You have now a new Dynamic Web Project named Test in your Eclipse Workspace.

c - Creating a "Mavenised" Eclipse Dynamic Web Project:

. Using your File Management System cut the Test directory under $MAVEN_PROJECT_PATH and paste it under your Eclipse Workspace, overwriting the existing Eclipse Project Test.
. In Eclipse right-click the Test project and choose refresh in the contextual menu.
. Right-click again the Test project and follow Build Path> Configure Build Path> in the contextual menu. In the Source remove the existing entry, choose Add Folder, select the sub-folder: src/main and click the Create New Folder button. Name it java then click the button finish and validate your choice (you have to click two OK buttons, one for validating the new folder creation and two for validating your build path new configuration). src/main/java is the standard location where Maven Project stores source code.

Results and Discussion: At this point we obtain an Eclipse Dynamic Web Project which can be compiled and packaged into a Web Archive (to be deployed under an Application Server like Tomcat) using Maven facilities. We will see below how to use Maven for this purpose.
There exists a more direct solution to make Eclipse-ready a Maven Project (having as working directory $MAVEN_PROJECT_PATH and using the command mvn eclipse:eclipse).

. Let's tell Maven what Java libraries we need for our project. This is done by configuring the pom.xml file. In Eclipse edit this file to obtain the following result:

<project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

 <modelVersion>4.0.0</modelVersion>
 <groupId>mshj.tutorial</groupId>
 <artifactId>Test</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Test</name>
 <url>http://maven.apache.org</url>

 <repositories>
  <repository>
   <id>prime-repo</id>
   <name>PrimeFaces Maven Repository</name>
   <url>http://repository.primefaces.org</url>
   <layout>default</layout>
  </repository>
 </repositories>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <org.springframework.version>3.1.1.RELEASE</org.springframework.version>
  <org.springframework.security.version>3.1.1.RELEASE</org.springframework.security.version>
  <com.sun.faces.version>2.1.14</com.sun.faces.version>
  <org.primefaces.version>3.4.2</org.primefaces.version>
  <org.richfaces.bom.version>4.2.3.Final</org.richfaces.bom.version>
  <org.hibernate.version>4.2.0.Final</org.hibernate.version>
  <c3p0.version>0.9.1.2</c3p0.version>
  <log4j.version>1.2.16</log4j.version>
 </properties>
 
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.richfaces</groupId>
                <artifactId>richfaces-bom</artifactId>
                <version>${org.richfaces.bom.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement> 
 
 <dependencies>
 
  <!-- Spring dependencies -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${org.springframework.version}</version>
  </dependency> 
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>
   
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>
            
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>
  
  <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aspects</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>${org.springframework.security.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${org.springframework.security.version}</version>
  </dependency> 
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-core</artifactId>
   <version>${org.springframework.security.version}</version>
  </dependency>
            
  <!-- JSF library -->
  <dependency>
    <groupId>com.sun.faces</groupId>
    <artifactId>jsf-api</artifactId>
    <version>${com.sun.faces.version}</version>
  </dependency>
  
  <dependency>
    <groupId>com.sun.faces</groupId>
    <artifactId>jsf-impl</artifactId>
    <version>${com.sun.faces.version}</version>
  </dependency>
  
  <!-- Facelets -->
  <dependency>
   <groupId>com.sun.facelets</groupId>
   <artifactId>jsf-facelets</artifactId>
   <version>1.1.14</version>
  </dependency>
  <!-- Facelets -->  
  
  <!-- RichFaces Library -->
  <dependency>
   <groupId>org.richfaces.ui</groupId>
   <artifactId>richfaces-components-ui</artifactId>
  </dependency>
  <dependency>
   <groupId>org.richfaces.core</groupId>
   <artifactId>richfaces-core-impl</artifactId>
  </dependency>  
  
  <!-- Primefaces library -->
   <dependency>
   <groupId>org.primefaces</groupId>
   <artifactId>primefaces</artifactId>
   <version>${org.primefaces.version}</version>
  </dependency>

  <!-- Hibernate library -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>${org.hibernate.version}</version>
  </dependency>

  <!-- Servlets -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>provided</scope>
  </dependency>
  <!-- Servlets -->

  <!-- Expression Language -->
  <dependency>
   <groupId>javax.el</groupId>
   <artifactId>el-api</artifactId>
   <version>2.2</version>
   <scope>provided</scope>
  </dependency>
  <!-- Expression Language -->
  
  <!-- JDBC -->
  <dependency>
     <groupId>com.oracle</groupId>
     <artifactId>ojdbc14</artifactId>
     <version>10.2.0.4.0</version>
  </dependency>
  
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.13</version>
  </dependency>  
  <!-- JDBC -->
  
  <dependency>
   <groupId>c3p0</groupId>
   <artifactId>c3p0</artifactId>
   <version>${c3p0.version}</version>
  </dependency>            
   
  <!-- Log4j library -->
  <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>${log4j.version}</version>
  </dependency>

  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjtools</artifactId>
   <version>1.7.0</version>
  </dependency>
  
  <dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>2.2.2</version>
  </dependency>
  
  <dependency>
   <groupId>com.itextpdf</groupId>
   <artifactId>itextpdf</artifactId>
   <version>5.3.4</version>
  </dependency>  

 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
     <source>1.5</source>
     <target>1.5</target>
    </configuration>
   </plugin>

   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-eclipse-plugin</artifactId>
    <version>2.4</version>
    <configuration>
     <downloadSources>false</downloadSources>
     <wtpversion>1.5</wtpversion>
    </configuration>
   </plugin>
 
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1.1</version>
    <configuration>
     <archive>
      <manifest>
       <addClasspath>true</addClasspath>
       <classpathPrefix>lib/</classpathPrefix>
      </manifest>
     </archive>
    </configuration>
   </plugin>
  </plugins>
 </build>   
 
</project>

Not all the dependencies are needed for the skeleton project but the former pom.xml is a quite complete and up to date example. It allows for using both MySQL and Oracle databases (so feel free to comment or remove the dependency you do not need), and two JSF enrichment libraries RichFaces and PrimeFaces in addition to Spring and Hibernate.
. Now we are going to tell Maven to compile and deploy the project. In a command line console set your working directory to the Test Eclipse Project path and type :

mvn compile

The latter command compiles the Java source. To do that it needs to download the project dependencies. To generate the Web Archive of the project you need to type:

mvn war:war


You'll possibly get a problem for downloading the Oracle JDBC driver (BUILD FAILED message). The problem with Oracle is that its JDBC driver is commercial and could not be integrated to Maven's central repository without breaking the law.
In this case try installing manually this dependency to your local Maven Repository:
. Locate the earlier downloaded ojdbc14-10.2.0.4.0.jar file and set your working directory to this path.
. Execute the following command:

mvn install:install-file -Dfile={ojdbc14-10.2.0.4.0.jar} -DgroupId=com.oracle 
-DartifactId=ojdbc14 -Dversion=10.2.0.4.0 -Dpackaging=jar

. Set back you working directory to the Test Eclipse Project path and retry:

mvn compile
mvn war:war

- We finally have to tell Eclipse about the libraries needed by the project (the ones downloaded by Maven). This is not really needed for deploying the project, but very convenient to avoid class not found errors while editing you code using Eclipse :
. Right-click your Eclipse Project and choose Refresh in the contextual menu. A sub-folder target is now part of your project tree.
. Select all the jar files in Test/target/Test-1.0-SNAPSHOT/WEB-INF/lib/ than do a right-click and choose Build Path>Add to Build Path in the contextual menu.

d - Creating the database underlying the Project:

We create here the database and a set of tables that will hold the application users and the privileges they own.

- MySQL: Connected with root to MySQL, create a schema (i.e. a database) named mshj and a user named test and identified by the password test, authorized to manipulate the database (select, insert, update, delete, create, drop ...). You only have to execute the following script after connection with root:

USE `mysql` ;

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

CREATE SCHEMA IF NOT EXISTS `mshj` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON mshj.* TO test@localhost IDENTIFIED BY 'test';

USE `mshj` ;

-- -----------------------------------------------------
-- Table `mshj`.`Users`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mshj`.`Users` (
  `id` DECIMAL(4) NOT NULL ,
  `login` VARCHAR(8) NOT NULL ,
  `password` VARCHAR(8) NOT NULL ,
  `identity` VARCHAR(256) NOT NULL ,
  PRIMARY KEY (`id`) ,
  UNIQUE INDEX `login_UNIQUE` (`login` ASC) )
ENGINE = InnoDB;
INSERT INTO `mshj`.`Users` VALUES(0,'user','user','Some User');
COMMIT;
-- -----------------------------------------------------
-- Table `mshj`.`Profiles`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mshj`.`Profiles` (
  `id` DECIMAL(2) NOT NULL ,
  `title` VARCHAR(50) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `mshj`.`Roles`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mshj`.`Roles` (
  `id` DECIMAL(2) NOT NULL ,
  `title` VARCHAR(50) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `mshj`.`Users_Profiles`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mshj`.`Users_Profiles` (
  `User_id` DECIMAL(4) NOT NULL ,
  `Profile_id` DECIMAL(2) NOT NULL ,
  PRIMARY KEY (`User_id`, `Profile_id`) ,
  INDEX `fk_Users_Profiles_Profiles` (`Profile_id` ASC) ,
  INDEX `fk_Users_Profiles_Users` (`User_id` ASC) ,
  CONSTRAINT `fk_Users_Profiles_Users`
    FOREIGN KEY (`User_id` )
    REFERENCES `mshj`.`Users` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_Users_Profiles_Profiles`
    FOREIGN KEY (`Profile_id` )
    REFERENCES `mshj`.`Profiles` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `mshj`.`Profiles_Roles`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mshj`.`Profiles_Roles` (
  `Profile_id` DECIMAL(2) NOT NULL ,
  `Role_id` DECIMAL(2) NOT NULL ,
  PRIMARY KEY (`Profile_id`, `Role_id`) ,
  INDEX `fk_Profiles_Roles_Roles` (`Role_id` ASC) ,
  INDEX `fk_Profiles_Roles_Profiles` (`Profile_id` ASC) ,
  CONSTRAINT `fk_Profiles_Roles_Profiles`
    FOREIGN KEY (`Profile_id` )
    REFERENCES `mshj`.`Profiles` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_Profiles_Roles_Roles`
    FOREIGN KEY (`Role_id` )
    REFERENCES `mshj`.`Roles` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

USE `mshj` ;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

- Oracle: Connected with SYSTEM or SYS (as SYSDBA) create a user named test and identified by the password test, authorized to manipulate the database (select, insert, update, delete, create, drop ...). You only have to execute the following script after connection with SYSTEM or SYS as SYSDBA:

CREATE USER test IDENTIFIED BY test;

--create session lets a user connect to Oracle db
GRANT CREATE session TO test; 
--grant privilege to a user to create tables inside it’s own schema
GRANT CREATE table TO test; 
--grant privilege to a user to create views inside it’s own schema
GRANT CREATE view TO test; 
--grant privilege to a user to create procedures inside it’s own schema
GRANT CREATE procedure TO test;
--grant privilege to a user to create synonyms inside it’s own schema
GRANT CREATE synonym TO test;
--grant privilege to a user to create triggers inside it’s own schema
GRANT create trigger TO test;
--grant privilege to a user to create types inside it’s own schema
GRANT create type TO test;
--grant privilege to a user to create sequences inside it’s own schema
grant create sequence to test;
--grant privilege to a user to create libraries inside it’s own schema
grant create library to test;
--grant privilege to a user to create database links inside it’s own schema
grant create DATABASE LINK to test;
--grant privilege to a user to create jobs inside it’s own schema
grant create job to test;
--grant privilege to a user to create materialized views inside it’s own schema
grant CREATE MATERIALIZED VIEW to test;
--grant privilege to a user to create indextypes inside it’s own schema
grant CREATE INDEXTYPE to test;

ALTER USER test QUOTA 50M ON SYSTEM;

connect test/test;

CREATE  TABLE  Users (
  id NUMERIC(4) NOT NULL ,
  login VARCHAR2(8) NOT NULL ,
  password VARCHAR2(8) NOT NULL ,
  identity VARCHAR2(256) NOT NULL ,
  PRIMARY KEY (id) ,
  CONSTRAINT Users_Login_Unique UNIQUE (login) )
;
INSERT INTO Users VALUES(0,'user','user','Some User');
COMMIT;
CREATE  TABLE  Profiles (
  id NUMERIC(2) NOT NULL ,
  title VARCHAR2(50) NOT NULL ,
  PRIMARY KEY (id) )
;

CREATE  TABLE  Roles (
  id NUMERIC(2) NOT NULL ,
  title VARCHAR2(50) NOT NULL ,
  PRIMARY KEY (id) )
;

CREATE  TABLE  Users_Profiles (
  User_id NUMERIC(4) NOT NULL ,
  Profile_id NUMERIC(2) NOT NULL ,
  PRIMARY KEY (User_id, Profile_id) ,
  CONSTRAINT fk_Users_Profiles_Users
    FOREIGN KEY (User_id )
    REFERENCES Users (id )
    ,
  CONSTRAINT fk_Users_Profiles_Profiles
    FOREIGN KEY (Profile_id )
    REFERENCES Profiles (id )
    
    )
;

CREATE INDEX fk_Users_Profiles_Profiles ON Users_Profiles (Profile_id ASC) ;
CREATE INDEX fk_Users_Profiles_Users ON Users_Profiles (User_id ASC) ;

CREATE  TABLE  Profiles_Roles (
  Profile_id NUMERIC(2) NOT NULL ,
  Role_id NUMERIC(2) NOT NULL ,
  PRIMARY KEY (Profile_id, Role_id) ,
  CONSTRAINT fk_Profiles_Roles_Profiles
    FOREIGN KEY (Profile_id )
    REFERENCES Profiles (id )
    ,
  CONSTRAINT fk_Profiles_Roles_Roles
    FOREIGN KEY (Role_id )
    REFERENCES Roles (id )
    
    )
;

CREATE INDEX fk_Profiles_Roles_Roles ON Profiles_Roles (Role_id ASC) ;
CREATE INDEX fk_Profiles_Roles_Profiles ON Profiles_Roles (Profile_id ASC) ;

The tables we just created are illustrated with the picture below:

Database Tables (click to enlarge)
We create a table named Users that will hold all the application users. We associate each user to a set of Profiles (via the Join-Table Users_Profiles) where each Profile is associated to a set of Roles (via the Join-Table Profiles_Roles). In next steps we will explain how Roles can be used (By Spring Security) to control access to the application pages.
Initially we also insert a dummy User (not yet associated to any profile) which we will use later for testing authentication to the skeleton application.

e - Creating the Model Java Beans:

We use the JPA (under Eclipse) to generate for each Table in the Database a corresponding Java bean that maps all the fields of the table to properties of the bean.

- Creating a new JPA Project associated to a database Connection:

. Download the MySQL JDBC Driver Implementation (mysql-connector-java-5.1.20-bin.jar) (respectively the Oracle JDBC Driver Implementation (ojdbc14-10.2.0.4.0.jar)) if you use a MySQL database (respectively an Oracle database) and store it in some permanent directory of your machine's file system.
. In Eclipse go to the menu File>New>Other>JPA> and choose the option JPA Project then follow the wizard while ensuring the following:
. In the Project name filed type TestJPAMySQL if you use MySQL or TestJPAOracle if you use Oracle. Leave all other fields to their defaults and verify that JPA Version is set to 2.0. Click next.
. Click next in the Java page without changing anything.
. In the JPA Facet page configure the fields as described below:
- Platform: Generic 2.0
- JPA Implementation : Type: User Library, in the window just below the field select the entry: JPA 2.0 (EclipseLink 2.4.1 - Juno). (This is one of the the users library we added to Eclipse in the Tutorial Prerequisites)
- Connection: Click the Add Connection link then choose your Database type in the Connection Profile Types and fill in at least the new connection Name filed with TestMySQLConnection (respectively TestOracleConnection) if you use MySQL (respectively Oracle).
-- In the Specify a Driver and Connection Details page you'll have to first specify a driver implementation to be used by your connection to access your database. Click the Add New Driver Definition button (a star-form button next to the Drivers select menu) and choose your driver version.
-- For MySQL (respectively for Oracle) choose MySQL JDBC Driver 5.1 (respectively Oracle Thin Driver 10).
-- If the error Unable to locate JAR/zip in file system shows up, go to the Jar List tab and click Add JAR/Zip.
-- Locate and choose mysql-connector-java-5.1.20-bin.jar (respectively ojdbc14.jar if you use Oracle). You are redirected to the connection details page.

--- Connection details for MySQL:


Database = mshj
URL = jdbc:mysql://localhost:3306/mshj
User name = test
Password = test

--- Connection details for Oracle:

SID = xe
HOST = localhost
Port Number = 1521
User name = test
Password = test

-- Check the box Save Password and click the Test connection button to verify that your connection is available.
-- Click the finish button to close the Connection dialog.
- Click the finish button again to close the new JPA project creation wizard. A new JPA project is now available on your Eclipse workspace.

- Generating Model Beans from the Database Tables:

. Right-click the new JPA project and select the New>Other>JPA>JPA Entities from Tables> menu.
. In the Select Tables page select your connection from the select menu (TestMySQLConnection or TestOracleConnection) then select all the listed tables and click the next button.
. The Table Associations page is displayed. It illustrates all the dependencies (foreign keys constraints) between the tables. Click the next button again.
. In the Customize Defaults page do the following :
- Check the Always generate optional JPA annotations and DDL parameters box.
- Set the Package field to mshj.tutorial.model then click the finish button.

Take a look to the content of the package mshj.tutorial.model of the JPA Project.
There are 3 Java beans : User.java, Profile.java and Role.java.

Having a look to the User.java bean code we notice several annotations preceding properties declarations. These annotations will later address Hibernate and explain how the User bean is mapping the USERS table.
For instance:
@Entity
@Table(name="USERS")
public class User implements Serializable

Later in the bean's code some annotations specify how exactly the USERS table fields are mapped to the User bean properties. Examples:
@Id
@Column(unique=true, nullable=false, precision=4)
private long id;

Here we specify that the id property of the bean is mapping the field of the table having the same name which is also its primary key (thanks to the @Id annotation). We also provide some constraints for the property which are inherited from the corresponding table field, such as the fact it should be unique, not set to null and a 4 digits number.

An important annotation is the following:
@ManyToMany
@JoinTable(
 name="USERS_PROFILES"
 , joinColumns={
  @JoinColumn(name="USER_ID", nullable=false)
  }
 , inverseJoinColumns={
  @JoinColumn(name="PROFILE_ID", nullable=false)
  }
 )
private List<profile> profiles;

It specifies that the property profiles will hold all the Profiles' instances associated to a User instance as specified by the many to many relation between the Users and Profiles tables.
The annotation specifies also the name of the Join-Table (USERS_PROFILES) instantiating the many to many relation and even the names of the join columns fields. This explains why no java bean mapping the USERS_PROFILES table is needed (nor generated) and similarly for the table PROFILES_ROLES.

Finally the rest of code declares the different properties of the bean. As expected every generated Model Bean implements the Serializable interface. This is necessary since Model Beans instances are meant to store descriptions of tables entries and to be further exchanged between different layers of the application which requires serialization.

Now we are done with the Model Beans generation. We'll have to copy these beans from the JPA Project to our main Test Project:

. In the Eclipse JPA Project right-click the package mshj.tutorial.model and copy it.
. Go to to your Test Eclipse Project and paste it in the src/main/java source folder.


We proceed now to the implementation of the 3 application layers. The application we design requires a page through which users should try to connect to the application (Presentation Layer). This authentication trial should then be handled by the application (Business Layer). For instance the authentication trial should be logged (simply in some log file) and most important the pair username and password provided by the user should be validated against entries in the Users table (Data Access Layer). Whether a corresponding entry exists or not is decided by the Data Access Layer that relays the authentication result to the Business Layer which in turn informs back the Presentation Layer whether the authentication trial succeeded or not.

Since we are designing here the secure entry point of the application (which is handled by Spring) we need to stick to Spring's rules. In one of its simple settings Spring requires: 
(i) an extra model bean implementing org.springframework.security.core.userdetails.UserDetails that will describe an application User;
(ii) a service bean (which can be seen as some Business Object) implementing org.springframework.security.core.userdetails.UserDetailsService permitting to retrieve a given user details knowing her username.

In the following we are implementing the two beans described above while ensuring that the user details service relies on a Data Access Object (DAO) to retrieve the user details knowing some username.

f - Implementing the UserDetails:

Here we implement the org.springframework.security.core.userdetails.UserDetails interface.
- In your Eclipse project create the following bean under the new package mshj.tutorial.security:

package mshj.tutorial.security;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Transient;
import mshj.tutorial.model.Profile;
import mshj.tutorial.model.Role;
import mshj.tutorial.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class MyUserDetails implements UserDetails
{
 private static final long serialVersionUID = 1L;

 private User user;
 
 public User getUser() {
  return user;
 }
 public void setUser(User user) {
  this.user = user;
 }
 
 public MyUserDetails(User user)
 {
  setUser(user);
 }
 
 @Override
 @Transient
 public Collection<? extends GrantedAuthority> getAuthorities()
 {
  Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
  
  for (Profile profile : getUser().getProfiles())
  {
   for (Role role : profile.getRoles())
   {
    authorities.add(new SimpleGrantedAuthority(role.getTitle()));
   }
  }
  
  return(authorities);
 }
 
 @Override
 @Transient
 public String getUsername()
 {
  return(getUser().getLogin());
 }
 
 @Override
 @Transient
 public String getPassword()
 {
  return(getUser().getPassword());
 }
 
 @Override
 @Transient
 public boolean isAccountNonExpired()
 {
  return true;
 }

 @Override
 @Transient
 public boolean isAccountNonLocked()
 {
  return true;
 }
 
 @Override
 @Transient
 public boolean isCredentialsNonExpired()
 {
  return true;
 }
 
 @Override
 @Transient
 public boolean isEnabled()
 {
  return true;
 }

 @Override
 public boolean equals(Object o)
 {
  try
  {
   return(((MyUserDetails)o).getUser().getLogin().equals(getUser().getLogin()));
  }
  catch(Exception e)
  {
   return(false);
  }
 }
 
 @Override
 public int hashCode()
 {
  return(getUser().getLogin().hashCode());
 }
}

Comments :
. The bean encapsulates a User object field (initialized upon construction) which we use to implement a part of the required methods, such as public String getUserName() and public String getPassword().
. The public Collection<? extends GrantedAuthority> getAuthorities() method informs Spring about the privileges owned by the user. In this part of the tutorial we do not yet need the privileges of a user to be returned: returning an empty new HashSet<GrantedAuthority>() would also be OK. We will see in Part 3 of this tutorial how these privileges are used to control the access to the application pages (and even to condition some components behavior in a page) . In this tutorial we choose to group privileges (roles in our case) by profiles and to affect this profiles to users. This explains the method implementation : we run through the user's profiles then through their respective roles and return the resulting set of roles. Keep in mind that you can manage privileges differently (adding or removing some hierarchy in the database) but then you have just to update the getAuthorities() method implementation accordingly.
N.B: Overriding the two methods public boolean equals(Object o) and public int hashCode() is essential for configuring the maximum number of the application sessions a user is allowed to open.
We will see further in this tutorial (in the section Configuring the Application (Spring) Security Context) how this number this can be configured.

g - Implementing the (Data Access Object) DAO or Repository layer:

Here we implement the DAO that will retrieve some user's details knowing his username. In fact we need an interface MyUserDetailsRepositoryInterface and a MyUserDetailsRepository class implementing it. This is needed to take advantage of proxying mechanisms (you can disable proxying thus getting rid of the interface and writing directly a class in which case you'll need to read more about it here).

- In your Eclipse project create the following interface under the package mshj.tutorial.security:

package mshj.tutorial.security;

import org.springframework.security.core.userdetails.UserDetails;

public interface MyUserDetailsRepositoryInterface
{
  UserDetails loadUserByUsername(String login);
}


- In your Eclipse project create the following bean under the package mshj.tutorial.security:

package mshj.tutorial.security;

import java.io.Serializable;
import mshj.tutorial.model.User;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional(readOnly=true)
public class MyUserDetailsRepository implements Serializable, MyUserDetailsRepositoryInterface
{
  private static final long serialVersionUID = 1L;
  
  @Autowired
  private SessionFactory sessionFactory;
  
  public SessionFactory getSessionFactory() {
    return sessionFactory;
  }
  public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }
  
  public UserDetails loadUserByUsername(String login)
  {
    return(new MyUserDetails((User)getSessionFactory().getCurrentSession().createQuery("from User where login like '" + login + "'").list().get(0)));
  }
}

Comments:
@Repository: This annotation addresses Spring and states that this bean will access the database (DAO). If no value is given as argument to the annotation (example: @Repository("someValue")) the bean will be identified by the bean class name's with its first character set to lower case, for instance our Repository bean here is identified by "myUserDetailsRepository".
@Transactional: This annotation addresses Spring and states that methods of the DAO bean will access the database through transactions. The attribute readOnly=true states that these transactions will be read only. In our case this is OK, since the only method of the bean will be selecting some line from the Users table. We will see in Part 3 of this Tutorial how this annotation can be overridden for specific methods that will need read/write access transactions to the database: inserting a new entry, updating of deleting an existing one and what exceptions raised by the database would require a rollback.
@Autowired: This annotation addresses Spring and states that the field it precedes (here sessionFactory of type org.hibernate.SessionFactory) have to be automatically initialized by Spring when an instance of the bean MyUserDetailsRepository is created.
org.hibernate.SessionFactory: is the Hibernate facility that permits to perform usual operations on the database tables. A dependence on that Hibernate facility is injected in our DAO bean which can use this dependency to perform jobs addressing the Users table. For instance, the instruction:

getSessionFactory()
   .getCurrentSession()
     .createQuery("from User where login like '" + login + "'")
       .list()

returns the list of User objects corresponding to the Users table entries satisfying the condition about the login field. The former request is written in HQL (Hibernate Query Language). Roughly speaking HQL queries are like SQL ones with one major difference: in stead of specifying a table and its fields' names we have to specify the model class (bean) and its properties' names (and one minor difference: no need to specify a select clause, because all the fields are selected).
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException: this method returns an instance (MyUserDetails) of the UserDetails interface describing the user having the username login (there should be at most one such a user this explains why we defined the login field of the table Users to be unique).

h - Implementing the (Business Object) BO or Service layer:

Here we implement the org.springframework.security.core.userdetails.UserDetailsService.
- In your Eclipse project create the following bean under the new package mshj.tutorial.security:

package mshj.tutorial.security;

import java.io.Serializable;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class MyUserDetailsService implements Serializable, UserDetailsService
{
 private static final Logger LOGGER = Logger.getLogger(MyUserDetailsService.class);
 private static final long serialVersionUID = 1L;
 
 @Autowired
 MyUserDetailsRepositoryInterface myUserDetailsRepository;
 
 public MyUserDetailsRepositoryInterface getMyUserDetailsRepository() {
  return myUserDetailsRepository;
 }
 public void setMyUserDetailsRepository(
   MyUserDetailsRepositoryInterface myUserDetailsRepository) {
  this.myUserDetailsRepository = myUserDetailsRepository;
 }

 @Override
 public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException
 {
  LOGGER.info("Login attempt with username = " + login);
  try
  {
   return(getMyUserDetailsRepository().loadUserByUsername(login));
  }
  catch (IndexOutOfBoundsException e)
  {  
   LOGGER.info("No user with username = " + login + " was found");
   throw new UsernameNotFoundException("");
  }
  catch(Exception e)
  {  
   LOGGER.debug(e.toString());
  }
  return null;
 }
}

Comments:
@Service: This annotation addresses Spring and states that instances of this bean are Business Objects (BO) or Services. The same identification convention as for the @Repository annotation is used by default here (since no value argument is provided with the annotation) and our Service bean is then identified by "myUserDetailsService".
. Through the @Autowired annotation a dependency on MyUserDetailsRepository DAO bean is injected. Here Spring is in charge of initializing this dependency when the Service is created. For that Spring looks for a bean having the type suiting this dependency (in our case myUserDetailsRepository).
. The bean implements the Spring's public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException method of org.springframework.security.core.userdetails.UserDetailsService by simply using the injected DAO bean.
. Moreover, the login attempt is logged in the application log using the instruction in Line 38.

i - Implementing the Presentation Layer:

We need now a login form and some application logic behind to check the provided username and password. For this we design a JSF connection page permitting to input the login credentials and a Controller bean that will handle the login validation (by relying on the UserDetailsService we designed previously).

- In the Eclipse Project create the following bean LoginController.java under the new package mshj.tutorial.controller:

package mshj.tutorial.controller;

import java.io.IOException;
import java.io.Serializable;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

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

 private String j_username;
 
 public String getJ_username() {
  return j_username;
 }
 public void setJ_username(String userName) {
  this.j_username = userName;
 }
 
 private String j_password;
 
 public String getJ_password() {
  return j_password;
 }
 public void setJ_password(String password) {
  this.j_password = password;
 }
 
 public String doLogin() throws  ServletException, IOException
 {
  ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
  RequestDispatcher dispatcher = ((ServletRequest)context.getRequest()).getRequestDispatcher("/login?j_username=" + getJ_username() + "&j_password=" + getJ_password());
  dispatcher.forward(((ServletRequest)context.getRequest()), ((ServletResponse)context.getResponse()));
  FacesContext.getCurrentInstance().responseComplete();
  
  return null;
 }
 
 public void doLogout() throws IOException
 {
  ExternalContext facesContext = FacesContext.getCurrentInstance().getExternalContext();
  HttpServletResponse hres = (HttpServletResponse) facesContext.getResponse();
  hres.sendRedirect(facesContext.getRequestContextPath() + "/logout");
 } 
}

Comments:
. @Controller: This annotation addresses Spring and means that the annotated bean is in the presentation layer. Again the same identification convention as for the @Repository and @Service annotations is used by default here (since no value argument is provided with the annotation) and our Controller bean is then identified by "loginController".
. @Scope: This annotation defines the scope of the bean. In our case it is set to "session", this means that your bean will live as long as the user session is not expired. Spring allows different kind of scopes. The reader can find more about it in Spring Component Scopes.
. j_username and j_password: These two fields will respectively hold the user name and the password provided by the user.
public String doLogin(): This method will be executed when the user confirms its authentication attempt. In this method the request is simply forwarded to the Spring Security layer that will check the provided credentials:

"/login?j_username=" + getJ_username() + "&j_password=" + getJ_password()

public String doLogout(): This method will be executed when the user asks to log out from the application.

As we will see later, /login and /logout are registered as the logging in and logging out URLs by the Spring Security layer in the Spring Security configuration file. Moreover using the parameter names j_username and j_password in the login url is mandatory to have Spring Security interpret correctly the authentication attempt.

- In the Eclipse Project create the following page Connection.xhtml under the new sub-folder src/main/webapp/pages/ :

<?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"
>
 <f:phaseListener type="mshj.tutorial.listener.LoginErrorPhaseListener" />

 <h:form id = "loginForm">
  <table align = "center" border = "0">
   <tr>
    <td align = "center" colspan = "2">
     <h:messages globalOnly="true" />
    </td>
   </tr>
   <tr>
    <th align = "center">
     <h:outputText value = "User Name" />
    </th>
    <td align = "center">
     <h:inputText 
      id = "j_username"
      value = "#{loginController.j_username}"
      label = "J_username"
      size = "8"
      maxlength = "8"
      required = "true"
     />
     <h:message for = "j_username" />
    </td>
   </tr>
   <tr>
    <th align = "center">
     <h:outputText value = "Password" />
    </th>
    <td align = "center">
     <h:inputSecret
      id = "j_password"
      value = "#{loginController.j_password}"
      label = "J_password"
      size = "8"
      maxlength = "8"
      required = "true"
     />
     <h:message for = "j_password" />
    </td>
   </tr>
   <tr>
    <td align = "center" colspan = "2">
     <h:commandButton
      value = "Login"
      action = "#{loginController.doLogin()}"
     />
    </td>
   </tr>
  </table>
 </h:form>
</html>

Comments: The page declares a form (using the <h:form/> tag) defining the component types discussed below:
<f:phaseListener type="mshj.tutorial.listener.LoginErrorPhaseListener" />: JSF applications life cycle comports several phases. Here we ask some PhaseListener (LoginErrorPhaseListener) to interfere with the last one (the render response phase). The reason is given further in this tutorial (in the Handling Login issues section). In this case the Phase Listener is defined only for the login page (Connection.xhtml). If you want to declare an application level Phase Listener (and thus targeting all the pages) you'll have to declare it in the JSF configuration file, see here for further details.
<h:messages globalOnly="true" />: Books a a text output field to contain possible application level messages. (If globalOnly is set to false all validation messages for the page components are also displayed)
<h:outputText/>: Declares an output field containing the text defined in the value attribute.
<h:inputText/>: Declares an input field. The value provided by the user for this field will be bound to the variable defined in the value attribute. For example the value provided for the input having id = "j_username" will be stored in the field j_username of the controller bean loginController. Several other attributes constrain the behavior of the input field like regular html, size and maxlength or required that states whether it is mandatory to provide a value for the field.
<h:inputSecret/>: Similar to  <h:inputText/> with the only difference that the value provided for the filed will be starred (masked by stars).
<h:message/>: Books a a text output field to contain possible validation errors for some input component.
<h:commandButton/>: Declares a submission button for the form. The text of the button is defined using the value attribute and its action attribute specifies the code to be run once the button is clicked. For instance the code to be run for our page is the doLogin() method of the loginController controller bean.

j - Configuring the Skeleton Application:

Before running and testing the skeleton application we have to configure the different tools handling the different application layers. The configuration files are stored in two locations of the project: under src/main/resources/resources/ and src/main/webapp/WEB-INF/. The first location provides the proper settings for each different tool (Hibernate, Logger, ...), while the second location defines the application contexts (in separate files for the sake of clarity) and defines the integration between different tools. Since Spring is somehow the maestro of our application the second location configuration files almost exclusively addresses it.

- At each step in the remainder of this section, create the corresponding file and update its content as specified. Especially in the section about Spring Security some additional beans should also be written.

- Configuring the Database Connection:
Location: src/main/resources/resources/database/database.properties
Here we configure the connection to the database. The content of the file should be as displayed below depending on your Database type:

. MySQL:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mshj
jdbc.username=test
jdbc.password=test


. Oracle:

jdbc.driverClassName=oracle.jdbc.OracleDriver
jdbc.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe
jdbc.username=test
jdbc.password=test


- Telling Spring about the Database Connection:
Location: src/main/resources/resources/spring/config/DataSource.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 
 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location">
   <value>WEB-INF/classes/resources/database/database.properties</value>
  </property>
 </bean>

 <bean id="dataSource" 
          class="com.mchange.v2.c3p0.ComboPooledDataSource" 
          destroy-method="close">
        
        <property name="driverClass"><value>${jdbc.driverClassName}</value></property>
        <property name="jdbcUrl"><value>${jdbc.url}</value></property>
        <property name="user"><value>${jdbc.username}</value></property>
        <property name="password"><value>${jdbc.password}</value></property>
        
        <property name="initialPoolSize"><value>3</value></property>
        <property name="minPoolSize"><value>3</value></property>
        <property name="maxPoolSize"><value>50</value></property>
        <property name="idleConnectionTestPeriod"><value>200</value></property>
        <property name="acquireIncrement"><value>1</value></property>
        <property name="maxStatements"><value>0</value></property>  <!-- 0 means: statement caching is turned off.  -->
        <property name="numHelperThreads"><value>3</value></property>  <!-- 3 is default -->         
    </bean>

</beans>

Comments: The file above is valid for both database types (MySQL and Oracle). First, we start by importing the database.properties file since we will use its parameters (${jdbc.url}, ${jdbc.username} and ${jdbc.password}).
Second, we use a connection pool for the database connection, namely c3p0 through the class: com.mchange.v2.c3p0.ComboPooledDataSource. Most of the connection pool parameters meanings are intuitive (for further details please visit the official website).

If you don't need the pool change the file's content to:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 
 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location">
   <value>WEB-INF/classes/resources/database/database.properties</value>
  </property>
 </bean>

 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}" />
  <property name="password" value="${jdbc.password}" />
 </bean>

</beans>


- Configuring Hibernate:
Location: src/main/resources/resources/spring/config/Hibernate.xml
Here we provide settings for Hibernate and integrate it with Spring.

. MySQL:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

 <!-- Session Factory Declaration -->
 <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

  <property name="dataSource">
   <ref bean="dataSource" />
  </property>

  <property name="packagesToScan">
   <list>
    <value>mshj.tutorial.model</value>
   </list>
  </property>
  
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
   </props>
  </property>
 </bean>
 
</beans>

. Oracle:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

 <!-- Session Factory Declaration -->
 <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

  <property name="dataSource">
   <ref bean="dataSource" />
  </property>

  <property name="packagesToScan">
   <list>
    <value>mshj.tutorial.model</value>
   </list>
  </property>
  
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
   </props>
  </property>
 </bean>
 
</beans>

Setting up the Session Factory (org.springframework.orm.hibernate4.LocalSessionFactoryBean), i.e. the entity in charge of opening and closing connections to the database, importing the database connection elements and providing the SQL dialect to be used aside, we also declare the list of packages to be scanned for annotated Model beans, for instance our list is reduced to one entry: mshj.tutorial.model.


- Configuring Logs:
Location: src/main/resources/resources/logs/log4j.properties
Here we provide the location and parameters of the application log file.

# Log levels
# Uncomment the following line to enable full logging for every class
#log4j.rootLogger=trace, stdout, R
log4j.logger.mshj.tutorial=trace, stdout, R

# Console appender configuration
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

# Rolling File Appender
log4j.appender.R=org.apache.log4j.RollingFileAppender
# Path and file name to store the log file.
log4j.appender.R.File=Test.log
#log4j.appender.R.MaxFileSize=500KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
# Rolling File Appender layout
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d - %c - %p - %m%n

Comments: We use log4j as logging tool (the configuration file speaks for itself).
Probably one importing thing to do is to update Line 15: set up the path of your log file. The setting above uses a relative path (Test.log) and the file will be probably created under the operating system user home's directory. If you use Windows and if you want to specify an absolute path for the log file just remember to double the "\" in the path. Example: if the desired path is C:\Users\SomeUser\Test.log provide C:\\Users\\SomUser\\Test.log.


- Configuring the Application General Context:
Location: src/main/webapp/WEB-INF/applicationContext.xml

<beans
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>
 <!-- Database Configuration -->
 <import resource="classes/resources/spring/config/DataSource.xml" />
 
 <!-- Hibernate Configuration -->
 <import resource="classes/resources/spring/config/Hibernate.xml" />
 
 <!-- Spring Auto scan for components: @Repository, @Service ... -->
 <context:component-scan 
  base-package="mshj.tutorial" />
  
</beans>

Comments: Here we first inform Spring about the Database source to be used then about the Hibernate settings and last we ask Spring to scan beans annotated by @Repository, @Service, ... in all the sub-packages of our application (mshj.tutorial).


- Configuring the Application Transaction Management Context:
Location: src/main/webapp/WEB-INF/applicationContext-transaction.xml

<beans
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/tx 
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
>

 <!--Transaction management -->
 <bean id="transactionTemplate"
  class="org.springframework.transaction.support.TransactionTemplate">
  <property name="transactionManager" ref="transactionManager" />
 </bean>

 <!-- Enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="transactionManager" />

 <!-- Transaction Manager is defined -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
       <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
         
</beans>

Comments:
Here we enable database transaction management. In Line 18  we precise that @Transactional annotations should be handled. (Like in the MyUserDetailsRepository Repository).


- Configuring the Application (Spring) Security Context:
Location: src/main/webapp/WEB-INF/applicationContext-security.xml

<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:security="http://www.springframework.org/schema/security"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 http://www.springframework.org/schema/security
 http://www.springframework.org/schema/security/spring-security-3.1.xsd">
 
 <bean id="concurrencyFilter"
    class="org.springframework.security.web.session.ConcurrentSessionFilter">
   <property name="sessionRegistry" ref="sessionRegistry" />
   <property name="expiredUrl" value="/pages/Connection.xhtml" />
 </bean>
 
 <bean id = "sas" class ="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
  <constructor-arg index="0" ref="sessionRegistry"/>
  <property name="maximumSessions" value="1" />
  <property name="exceptionIfMaximumExceeded" value = "true" />
 </bean>
 
 <bean id = "sessionRegistry" class ="org.springframework.security.core.session.SessionRegistryImpl" />
 
 <bean id="myAuthenticationSuccessHandler" class="mshj.tutorial.handler.MyAuthenticationSuccessHandler"/> 
 
 <security:http pattern="/pages/Connection.xhtml" security="none" />
 
 <security:http access-denied-page="/pages/AccessDenied.xhtml" >
  
   <security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
  <security:session-management  session-authentication-strategy-ref="sas"/>

  <security:intercept-url pattern="/**/*.js" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.css" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.gif" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.png" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.jpg" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.js.faces" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.properties" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/services/*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/services" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.wsdl" access="IS_AUTHENTICATED_ANONYMOUSLY" />
  <security:intercept-url pattern="/**/*.swf" access="IS_AUTHENTICATED_ANONYMOUSLY" />

  <!-- Begin Handling of Security Permission per Page -->
  <!-- End Handling of Security Permission per Page -->
    
  <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" /> 
  
  <security:form-login 
   login-processing-url="/login"
   login-page="/pages/Connection.xhtml"
   authentication-success-handler-ref="myAuthenticationSuccessHandler"
  />   
  
  <security:anonymous />
      
  <security:logout
   logout-url="/logout"
   logout-success-url="/pages/Connection.xhtml" 
  />
  
 </security:http>
 
 <security:authentication-manager>
  <security:authentication-provider user-service-ref="myUserDetailsService" /> 
 </security:authentication-manager>

</beans>

Comments: We are not going too far in explanations here. The reader may refer to the Spring Security Reference Manual or to this excellent (yet short) ticket about Customization of Spring Security Authentication.
. In our tutorial we rely on the default Spring Authentication Provider which we feed with our implementation of UserDetailsService, namely MyUserDetailsService (remember this is the service in charge of loading User Details being given the User's name). (Lines 65-67)
. In Lines 10-14, we define a filter for the application sessions concurrency handling. In Line 13 Spring is told to redirect to the login page (Connection.xhtml) whenever a session is expired. To have this filter enabled some further configuration is to be added to src/main/webapp/WEB-INF/web.xml (see further below).
. In Lines 16-20, we define the application sessions concurrency strategy. In Line 18 the maximum number of concurrent sessions allowed for one user is declared (here 1). Use the value -1 to have an unbounded number of concurrent sessions. In Line 19 we state whether an exception is raised when a user reaches the maximum number of allowed concurrent sessions (here set to true). If this property is set to true, the user will not be able to create the new application session. If it is set to false a new session will be created but after expiring the already opened sessions.
. In Line 22 we declare the session registry. Note that the application sessions concurrency filter and strategy refer to it.
. In Line 24 an AuthenticationSuccessHandler bean is declared. This bean will tell Spring the code to be executed after a successful authentication trial. One of the classical tasks to perform in such a handler is redirecting the connected user to a page depending on some criteria, for example: its privileges. This bean is further referred to in Line 53. A sample AuthenticationSuccessHandler is given at the end of this section.
. In Line 26, we give a pattern for all the pages that will be accessed without restrictions (here only and obviously the login page Connection.xhtml).
. In Lines 28-63, we define the access control policies. For instance in Lines 33-43 we state that some URL patterns can be accessed by the user without being authenticated (access="IS_AUTHENTICATED_ANONYMOUSLY"). In Line 48 we state that all remaining URL patterns require a successful authentication before being accessed. In order to condition access to a certain page (or to a group of pages matching some pattern) one should insert between the two comment lines 45 and 46 an entry similar to the one below:

<security:intercept-url
  pattern="/pages/SomePage.xhtml"
  access="ROLE_SOME_PRIVILEGE"
/>

where ROLE_SOME_PRIVILEGE is the title of some Roles' table entry which is associated (in our case via some Profiles' entry) to the users authorized to view the /pages/SomePage.xhtml.
. Finally in Lines 50-54 (respectively in Lines 58-61) we define properties of the login (respectively the logout) page. Note that since the authentication-failure-url attribute of the <security:form-login/> is not mentioned the users stays in the login page (Connection.xhtml) if its authentication trial fails. Off course this could be customized by adding this attribute and giving it a different value.

Before proceeding with the remaining configuration files and because of the Spring Security configuration we have above we have to write some additional java code:


- Implementing an AuthenticationSuccessHandler bean:
Under the package mshj.tutorial.handler create the following MyAuthenticationSuccessHandler bean:

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.isEmpty())
  {
   response.sendRedirect("../index.jsp");
  }
  
  roles = null;
 }
}

The Set<String> roles will contain here all the roles associated to the authenticated user.
Note that here we redirect all the users having no associated roles to the index.jsp page (this page is generated automatically by Eclipse for every Dynamic Web Project and displays a simple Hello World ! message).


- Handling Login issues:
Here we implement a PhaseListener that will (i) relay a bad authentication result from the Spring Security AuthenticationManager to the Connection.xhtml page and (ii) relay a maximum concurrent session number exceeded whenever this exception occurs.
Under the package mshj.tutorial.listener create the LoginErrorPhaseListener java class as follows:

package mshj.tutorial.listener;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
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);
      FacesContext.getCurrentInstance().addMessage(null, 
        new FacesMessage(FacesContext.getCurrentInstance().getApplication()
          .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
            .getString("loginFailure")));
  }
  else
  {
    if (e instanceof SessionAuthenticationException)
    {
      FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
        WebAttributes.AUTHENTICATION_EXCEPTION, null);
      FacesContext.getCurrentInstance().addMessage(null, 
        new FacesMessage(FacesContext.getCurrentInstance().getApplication()
          .getResourceBundle(FacesContext.getCurrentInstance(), "messages")
            .getString("sessionConcurrency")));
    }
  }
 }
 
 @Override
 public PhaseId getPhaseId()
 {
  return PhaseId.RENDER_RESPONSE;
 }
 
}

The exception raised by Spring Security when an authentication trial fails is BadCredentialsException. The one raised when the maximum number of concurrent sessions is exceeded is SessionAuthenticationException. In both cases a JSF message is added, this message is then displayed by the <h:messages globalOnly="true" /> tag of Connection.xhtml page (remember that our Spring Security configuration reloads the login page when an authentication trial fails).


- Configuring JSF:
Location: src/main/webapp/WEB-INF/faces-config.xml

<?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">

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

</faces-config>

Comments: Here we integrate Spring and JSF. Roughly speaking we tell JSF that Spring will take care of handling the Controllers beans intervening in the JSF pages. Example: in Line 20-28 of Connection.xhtml:

     <h:inputText 
      id = "j_username"
      value = "#{loginController.j_username}"
      label = "J_username"
      size = "8"
      maxlength = "8"
      required = "true"
     />

When this page is displayed the first time, Spring creates a new instance of the Bean LoginController (identified here by loginController). When the user submits the login form the property j_username of that instance is set to the value provided by the user in the input text.

Spring's @Component/@Autowired  vs JSF's @ManagedBean/@ManagedProperty annotations:
We naturally chose here to tell Spring to directly handle beans in stead of telling JSF to later delegate this task to Spring. Indeed we could annotate all our beans using JSF annotations (see this tutorial)
but this would result in more XML configuration files.


- Putting all together:
Location: src/main/webapp/WEB-INF/web.xml

<?xml version="1.0"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns="http://java.sun.com/xml/ns/javaee" 
   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
   id="WebApp_ID" 
   version="2.5">
 <display-name>Test</display-name>
 <context-param><param-name>webAppRootKey</param-name><param-value>mshj.tutorial</param-value></context-param>

 <!-- Spring Context Configuration's Paths definition -->
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/applicationContext*.xml </param-value>
 </context-param>
 
 <!-- Needed by Spring's Session Concurrency Filter -->
 <listener>
  <listener-class>
   org.springframework.security.web.session.HttpSessionEventPublisher
  </listener-class>
 </listener>
 
 <!-- The Bootstrap listener to start up and shut down Spring's root WebApplicationContext. It is registered to Servlet Container -->
 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>
 <listener>
  <listener-class>
   org.springframework.web.context.request.RequestContextListener
  </listener-class>
 </listener>

 <!-- filter enforcing charset UTF-8 - must be first filter in the chain ! -->
 <filter>
     <filter-name>characterEncodingFilter</filter-name>
     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
     <init-param>
         <param-name>encoding</param-name>
         <param-value>utf-8</param-value>
     </init-param>
     <init-param>
         <param-name>forceEncoding</param-name>
         <param-value>true</param-value>
     </init-param>
 </filter>
 <filter-mapping>
     <filter-name>characterEncodingFilter</filter-name>
     <url-pattern>/*</url-pattern>
 </filter-mapping>
 <!-- filter enforcing charset UTF-8 - must be first filter in the chain ! -->
 
 <!-- Spring security filter -->
 <filter> 
  <filter-name>springSecurityFilterChain</filter-name> 
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
 </filter> 
 <filter-mapping> 
  <filter-name>springSecurityFilterChain</filter-name> 
  <url-pattern>/*</url-pattern> 
  <dispatcher>FORWARD</dispatcher> 
  <dispatcher>REQUEST</dispatcher> 
 </filter-mapping>    
 <!-- Spring security filter --> 

 <!-- OpensessionInViewFilter filter -->
 <filter>
  <filter-name>OpensessionInViewFilter</filter-name>
  <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
 </filter>
 
 <filter-mapping>
  <filter-name>OpensessionInViewFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
 <!-- OpensessionInViewFilter filter -->
  
 <!--  log4j Begin -->
 <context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>/WEB-INF/classes/resources/logs/log4j.properties</param-value>
 </context-param>
 <listener>
  <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
 </listener>
 <!--  log4j End -->
   
 <!-- Project Stage Level -->
 <context-param>
  <param-name>javax.faces.PROJECT_STAGE</param-name>
  <param-value>Development</param-value>
 </context-param>

 <!-- JSF RichFaces context params -->
 <context-param>
  <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
  <param-value>.xhtml</param-value>
 </context-param>
 <context-param>
  <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
  <param-value>com.sun.facelets.FaceletViewHandler</param-value>
 </context-param>
 
 <context-param>
  <param-name>com.sun.faces.enableRestoreView11Compatibility</param-name>
  <param-value>true</param-value>
 </context-param>
 
 <!-- JSF RichFaces context params -->
 
 <!-- JSF Servlet is defined to container -->
 <servlet>
  <servlet-name>Faces Servlet</servlet-name>
  <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <!-- Mapping with servlet and url for the http requests. -->
 <servlet-mapping>
  <servlet-name>Faces Servlet</servlet-name>
  <url-pattern>*.jsf</url-pattern>
  <url-pattern>*.xhtml</url-pattern>
 </servlet-mapping>
    
</web-app>

Comments:
. Lines 12-15: All the other application context configuration files are imported.
. Lines 18-22: Publishes http session application events (session created, session destroyed) to the Spring Root WebApplicationContext. This listener is used for solving the concurrent session problem.
. Lines 25-29: Bootstrap listener to start up and shut down Spring's root WebApplicationContext.
. Lines 30-34: Servlet 2.4+ listener that exposes the request to the current thread. This listener is mainly for use with third-party servlets, e.g. the JSF FacesServlet.
. Lines 37-52: Forces requests and responses to abide by the UTF-8 character encoding. This is pretty useful when the application uses accentuated characters or non Latin alphabet languages. This filter should be the first one in the filtering chain. 
. Lines 56-65: The Spring Security filter. Read more about it here.
. Lines 69-77: Solves the problem of a prematurely closed Hibernate Session. Roughly speaking when Hibernate (after a DAO call) loads an object o mapping a database entry, it does not (by default) populate all the other objects referenced by o under some association constraints unless it is explicitly told to do it. Example: we have two tables Students and Classes where each Student is associated to one and exactly one Class and where a Class is associated to one or more Students. In the Model Bean mapping the table Classes a property (let's call it students) is defined to hold all the students attending the Class. Now say we have an Classes object c returned in our controller (which was returned by some Service calling the Classes Repository with some class identifier) and that we want to display the list of its corresponding students by running through c.getStudents(). In the default setting the list is empty because Hibernate has closed the database session before populating the students !
Adding the OpenSessionInViewFilter permits to keep the database session alive from the moment the client sends its request and till the response is sent back to her.
Another (risky) solution is to force Hibernate to populate certain references (add the attribute fetch=FetchType.EAGER to your @OneToMany or @ManyToOne or @Column annotation of the corresponding property in the Model Bean). The fetch attribute is by default set to fetch=FetchType.LAZY. Setting it to fetch=FetchType.EAGER may be problematic if you don't need all the time these references to be populated, example: if the number of references to fetch is big.
. Lines 81-87: Loads the Logger listener (provided by log4j).
. Lines 91-94: Defines the JSF project stage. This configuration affects among others the validation messages that are shown by JSF (at development stage you'll be notified that your are using a command button without an enclosing form tag). See here for more details about JSF project stages.
. Lines 106-109: These lines asks JSF to create a new view when it expires. This scenario can be useful when you submit a form in a JSF page after your session is expired (because you were inactive too long).
. Line 114-125: Here we tell the Servlet container that the JSF servlet is in charge of handling .jsf and .xhtml pages.

k - Deploying and testing the Skeleton Application:

- 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)
- Try dummy/dummy as user name and password: note the error message displayed.
- Try now user/user as user name and password: do you see a Hello World ! message ?
- Now open a different browser and try to login again to the application: you are notified that the maximum number of concurrent sessions is reached.


Below the links to download the complete 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).
Then create the database tables using the scripts provided above and start from the last section of this tutorial k - Deploying and testing the Skeleton Application.

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





4 comments:

  1. Votre tutoriel est excellent,
    Vous avez pas la suite ?

    ReplyDelete
  2. Merci d'apprécier :) Je suis toujours entrain de rédiger. D'ici la fin de la journée il y aura du nouveau et d'ici une semaine le plus gros sera fait. Si tu as une question précise tu peux toujours la poser et je me ferais un plaisir de te répondre.

    ReplyDelete
  3. Merci, tout est clair pour le moment. Vivement la suite :)

    ReplyDelete