In this short tutorial we are going to build a Java Server Faces Web-Application using JSF2.0, Facelets, Maven and Hibernate as ORM Mapper.
The goals for this first step are: Setting up the project structure using Maven, defining a frame template/decorator and a registration facelet, creating a managed bean and mapping it’s values to the facelet, adding some basic validation, displaying validation errors and finally adding a navigation structure.
In step2 of this tutorial we are going to add persistence using Hibernate, add some security, create a custom UI component and add some AJAX.
The Mojarra JSF implementation is used for this tutorial – perhaps I’m going to post more about the MyFaces implementation in another tutorial.
Creating the Application
There we go ..
-
Create a Maven webapp project structure using the following command or using your IDE e.g. Eclipse and the Maven Plugin:
mvn archetype:create -DgroupId=com.hascode.jsf -DartifactId=registration-demo -DarchetypeArtifactId=maven-archetype-webapp
-
Alternatively use this command and chose maven-archetype-webapp
mvn archetype:generate
-
The pom.xml should look similiar to this:
<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>com.hascode.jsf</groupId> <artifactId>jsf2-tutorial</artifactId> <packaging>war</packaging> <version>0.1</version> <name>hasCode.com -Java Server Faces 2 Maven Tutorial</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>jsf2-tutorial</finalName> </build> </project>
-
Add the Sun/Oracle Maven and the JBoss repositories to the pom.xml we are going to need it for the JSF API and implementation
<repositories> <repository> <id>maven2-repository.dev.java.net</id> <name>Java.net Repository for Maven</name> <url>http://download.java.net/maven/2</url> </repository> <repository> <id>JBoss repository</id> <url>http://repository.jboss.com/maven2/</url> </repository> </repositories>
-
Add some dependencies to your pom.xml – please note that there is a difference whether the app is deployed to a Java EE Application Server or a simple Servlet Container, so choose the corresponding dependencies for your pom.xml For a JEE AppServer:
<dependencies> <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> </dependencies>
For a Servlet Container:
<dependencies> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.0.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-impl</artifactId> <version>2.0.2</version> <scope>compile</scope> </dependency> </dependencies>
-
Finally, your pom.xml could look like this:
<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>com.hascode.jsf</groupId> <artifactId>jsf2-tutorial</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>hasCode.com -Java Server Faces 2 Maven Tutorial</name> <url>http://maven.apache.org</url> <repositories> <repository> <id>maven2-repository.dev.java.net</id> <name>Java.net Repository for Maven</name> <url>http://download.java.net/maven/2</url> </repository> <repository> <id>JBoss repository</id> <url>http://repository.jboss.com/maven2/</url> </repository> </repositories> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.0.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-impl</artifactId> <version>2.0.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <finalName>jsf2-tutorial</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>
-
Optional: Create a project file for Eclipse IDE with WebToolkit integration using the following command
mvn eclipse:eclipse -Dwtpversion=2.0
-
Get your IDE running and import the project if you wish to
-
Delete the index.jsp generated____by maven – we won’t need it..
-
Now you need to register the FacesServlet to a defined URL pattern and set the project stage to development. The URL Pattern *.xhtml is mapped to the JSF Servlet – so everything with a .xhtml suffix goes to the FacesServlet. Create a file named web.xml in src/main/webapp/WEB-INF/ and add the following declaration:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>hasCode.com - Java Server Faces 2 Tutorial</display-name> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> </web-app>
-
In the next step we want to define the default-page – for now the registration form will be shown per default – so add the following declaration to the <web-app> Element in the web.xml:
<welcome-file-list> <welcome-file>/registration.xhtml</welcome-file> </welcome-file-list>
-
Create the directory src/main/java if it doesn’t exist – if you use the eclipse maven plugin you might want to choose Update Project Configuration after that or add the directory as a source directory
-
Now create a new package – something like com.hascode.tutorial.jsf2_tutorial.bean
-
We need a managed bean for the registration form so create a class named UserBean in the package com.hascode.tutorial.jsf2_tutorial.bean, add some members, getter and setter
package com.hascode.tutorial.jsf2_tutorial.bean; import java.util.Date; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; @ManagedBean @SessionScoped public class UserBean { protected String nickname; protected String email; protected Date birthday; /** * @return the nickname */ public String getNickname() { return nickname; } /** * @param nickname * the nickname to set */ public void setNickname(String nickname) { this.nickname = nickname; } /** * @return the email */ public String getEmail() { return email; } /** * @param email * the email to set */ public void setEmail(String email) { this.email = email; } /** * @return the birthday */ public Date getBirthday() { return birthday; } /** * @param birthday * the birthday to set */ public void setBirthday(Date birthday) { this.birthday = birthday; } }
-
Create a frame template called decorator.xhtml in src/main/webapps - define two sections the subtemplates may overwrite – the body and title + heading
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title><ui:insert name="title">hasCode.com - Default Title</ui:insert></title> </h:head> <h:body> <h1><ui:insert name="title">hasCode.com - Default Heading</ui:insert></h1> <ui:insert name="body">Default Body</ui:insert> </h:body> </html>
-
Afterwards we create a registration facelet in src/main/webapp named registration.xhtml – there are some really nice tools from the JBoss Eclipse Tools helping you edit the Facelets (Component: RichFaces) – screenshot:
-
Afterwards we create a registration facelet in src/main/webapp named registration.xhtml – there are some really nice tools from the JBoss Eclipse Tools helping you edit the Facelets (Component: RichFaces) – screenshot:
-
Insert markup into the registration facelet
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> <ui:composition template="/decorator.xhtml"> <ui:define name="title">hasCode.com - Java Server Faces Tutorial - Registration</ui:define> <ui:define name="body"> <h:form> <div id="registration"> <label>Nickname</label> <h:inputText label="Nickname" id="nname" value=""/> <br/> <label>E-Mail</label> <h:inputText label="E-Mail" id="email" value=""/> <br/> <label>Birthday</label> <h:inputText label="Birthday" id="birthday" value=""/> (yyyy-mm-dd) <br/> <h:commandButton action="success" value="Register" /> </div> </h:form> </ui:define> </ui:composition> </html>
-
If you run the application now – e.g. mvn package tomcat:run and open it in your browser (e.g. http://localhost:8080/jsf2-tutorial) you should see the decorated registration facelet – mine looks like this:
-
If you run the application now – e.g. mvn package tomcat:run and open it in your browser (e.g. http://localhost:8080/jsf2-tutorial) you should see the decorated registration facelet – mine looks like this:
-
Map fields to bean by referencing the field value attributes to the managed UserBean
<h:inputText label="Nickname" id="nname" value="#{userBean.nickname}"/> <h:inputText label="E-Mail" id="email" value="#{userBean.email}"/> <h:inputText label="Birthday" id="birthday" value="#{userBean.birthday}"/> (yyyy-mm-dd)
-
Add some basic validation – change the fields to
<h:inputText label="Nickname" id="nname" value="#{userBean.nickname}" required="true"/> <h:inputText label="E-Mail" id="email" value="#{userBean.email}" required="true" validator="#{userBean.validateEmail}"/> <h:inputText label="Birthday" id="birthday" value="#{userBean.birthday}" required="true"/> (yyyy-mm-dd)
-
Add the validation method to the UserBean
public void validateEmail(FacesContext context, UIComponent validated, Object value) { // simple stupid validation String mail = (String) value; if (!mail.matches(".+\\@.+\\..+")) { FacesMessage msg = new FacesMessage("This is not an e-mail!"); throw new ValidatorException(msg); } }
-
Adjust the time format
<h:inputText label="Birthday" id="birthday" value="#{userBean.birthday}" required="true"> <f:convertDateTime pattern="yyy-MM-dd"/> </h:inputText> (yyyy-mm-dd)
-
Add the display of errors to the registration form, the for attribute of the message element references to the id attribute of the form element. We are going to display each error after each corresponding form element and to display a list of all errors at the end of the document
<h:inputText label="Nickname" id="nname" value="#{userBean.nickname}" required="true"/> <h:message for="nname"/> <h:inputText label="E-Mail" id="email" value="#{userBean.email}" required="true" validator="#{userBean.validateEmail}"/> <h:message for="email"/> <h:inputText label="Birthday" id="birthday" value="#{userBean.birthday}" required="true"> <f:convertDateTime pattern="yyy-MM-dd"/> </h:inputText> (yyyy-mm-dd) <h:message for="birthday"/><hr/> <h:messages/>
-
If you run the app now (eg. mvn package tomcat:run-war) and try to submit the empty form you’re going to see some error messages – if you try to enter an invalid date you will notice the specialized error message for this field
-
If you run the app now (eg. mvn package tomcat:run-war) and try to submit the empty form you’re going to see some error messages – if you try to enter an invalid date you will notice the specialized error message for this field
-
Now let’s add some control flow/navigation to the app – if the registration action is successful and no validation errors occurred we want to redirect the user to a new view – first we add a new facelet: registration_success.xhtml in src/main/webapp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> <ui:composition template="/decorator.xhtml"> <ui:define name="title">hasCode.com - Java Server Faces Tutorial - Registration Successful</ui:define> <ui:define name="body"> Registration was successful </ui:define> </ui:composition> </html>
-
We also add the navigation structure to the faces-config.xml in src/main/webapp/WEB-INF – create if the config does not exist yet
<?xml version="1.0" encoding="UTF-8"?> <faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xi="http://www.w3.org/2001/XInclude" 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"> <name>app</name> <navigation-rule> <from-view-id>/registration.xhtml</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/registration_success.xhtml</to-view-id> </navigation-case> </navigation-rule> </faces-config>
-
If you enter valid data to all form fields you should be redirected to the success page
-
Compile and deploy – run the following command and deploy the war to your application server
mvn package
-
Alternatively you might test your application in an embedded app server using the Maven Tomcat Plugin
mvn package tomcat:run-war
Tutorial Sources
I have put the sources on GitHub.org – you’re able to check them out if you have Mercurial installed
git clone http://github.com/hascode/hascode-tutorials.git
Additional Tools
The JBoss Tools for Eclipse offer some nice features for your work with Java Server Faces – for more information – take a look at the installation page for the JBoss Tools. Be sure to install the RichFaces feature – it adds some improvements for the configuration of your faces-config.xml e.g. an editor for the navigation between the views
An alternative using Java EE 6
If you’d like to see how a JSF web app is implemented using the Java EE 6 stack and an application server, take a look at my article: “Creating a sample Java EE 6 Blog Application with JPA, EJB, CDI, JSF and Primefaces on GlassFish”
Troubleshooting
-
“Java compiler level does not match the version of the installed Java project facet.” – If this error occurs in Eclipse: Go Project Properties > Project Faces > Java and select Java 6 (or 5). Sometimes if you imported the project via Import > Existing Project and then enable dependency management using the Eclipse Maven Plugin – the plugin resets the Java Compiler settings – go Project Properties > Java Compiler and set the compile settings back to Java 6 (or 5).
-
Maven tries to build with an oooold target or source JVM and complains about missing annotation support, specify the target VM in the pom.xml
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins>
-
java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.. You app server is missing the JSTL libs – add the dependency to your pom.xml
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> <scope>compile</scope> </dependency>
-
java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config – The dependency to the Standard Tag Library is missing – insert this into your pom.xml
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
-
StackOverFlowError / Infinite Loop – make sure that you map the FacesServlet via <url-pattern>*.xhtml</url-pattern> and also name the Facelet with *.xhtml. If found some information regarding this problem thanks to BalusC on stackoverflow.com.
-
javax.el.PropertyNotFoundException: Target Unreachable, identifier ‘userBean’ resolved to null - if you are testing your app with the tomcat maven plugin – be sure to run mvn tomcat:run-war instead of mvn tomcat:run
Resources
Article Updates
-
2015-03-03: Table of contents added.