Web technologies/2014-2015/Laboratory 9

Java Server Faces

edit

Java Server Faces (JSFs) is a Web application framework intended to simplify development integration of user interfaces and the operation on the values the UI hold for Java EE application usage activity.

The current version is 2.3.2 as of August 20, 2019.

It includes:

  • Java Web APIs for:
    • user interface components and managing their state
    • handling events (an example is javax.faces.event.ActionEvent)
    • input validation
    • defining page navigation
  • A default set of UI components
  • Two custom tag libraries for interfacing with JSP
  • Managed beans
  • ...

IMPORTANT: JSF cannot handle Get requests

Creating a simple JSF

edit

A typical JSF adheres to the following control flow:

  • display form
  • submit form to itself
  • instantiate bean
  • action controller method is invoked
  • the method returns a condition
  • a result page is displayed

NetBeans offers the possibility of creating a JSF projects: go to New Project -> Web -> Web Application -> select Java Server Faces from the Frameworks tab -> Finish.

Creating the form

edit

JSF offers several tags for handling content:

  • <f:view>: used to create top level view and is a container for all JSF component tags on a page
  • <h:form>: used to create the HTML form
  • <h:panelGrid>: used to create an HTML table with specified number of columns
  • <h:outputText>: used to create a component for displaying formatted output as text
  • <h:inputText>: used to create text input control (single line)
  • <h:inputSecret>: used to create password fields
  • <f:validator>: used for validating HTML fields
  • ...

The following example (login.jsp) shows a simple login form:


<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<html>
	<head>
	</head>
	<body>
		<f:view>
			<h:form id="LoginForm">
				<h:panelGrid id="lpg" columns="3">                   
					<h:outputText value="UserName"/>
					<h:inputText id="username" value="#{LoginBean.username}" required="true">
						<f:validateLength minimum="1" maximum="25"/>
					</h:inputText>
					<h:message for="username" style="color:red"/>
					<h:outputText value="Password"/>
					<h:inputSecret id="password" value="#{LoginBean.password}" required="true">
						<f:validateLength minimum="6" maximum="25"/>
					</h:inputSecret>
					<h:message for="password" style="color:red"/>
					<h:outputText value=""/>
					<h:commandButton value="Login" action="#{LoginBean.handleForm}"/>
				</h:panelGrid>
			</h:form>
		</f:view>
	</body>
</html>


Form validation
edit

IMPORTANT: In order to display the validation messages use either <h:messages> or <h:message for="inputTextID"> tags.

Form validation can be achieved in many ways:

by using the <f:validator> tag together with the custom Validator interface
edit

The following steps are required:

  • Create a class implementing Validator:


package mybeans;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

public class MyValidator implements Validator {
	// Used for checking whether the username is set or not
	public void  validate(FacesContext facesContext, UIComponent uIComponent, Object object) throws ValidatorException {
		String username = (String)object;
		if (username == null || username.trim().length() < 1 || username.trim().length() > 25 ) {
			FacesMessage message = new FacesMessage();
			// This message will be displayed near the invalid field
			message.setSummary("Empty username");
			throw new ValidatorException(message);
		}
	}
}


  • Modify faces-config.xml to register the custom validator:


<?xml version='1.0' encoding='UTF-8'?>

<faces-config version="1.2" 
    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_1_2.xsd">
	[...]
	<!-- multiple validator tags can exist one for each validation. The classes can be different from the managed-bean class -->
	<validator>
		<validator-id>requiredusername</validator-id>
		<validator-class>mybeans.MyValidator</validator-class>
	</validator>
</faces-config>


  • use the <f:validator> inside an <f:inputText> tag to validate your input:


	<h:inputText size="25" value="#{LoginBean.username}">
		<f:validator validatorId="requiredusername" />
	</h:inputText>


by using a Backing bean
edit

The following steps are required:

  • create a method for validation inside the bean as follows:


	public validateUserName (FacesContext context, UIComponent component, Object value) {
		String username = (String)object;
		if (username.trim().length() == 0) {
			FacesMessage message = new FacesMessage("Invalid username");
			context.addMessage(component.getClientId(context), message);

		}		
	}


  • set the validator attribute associated with the inputText tag requiring validation to call the validation method:


	<h:inputText id="username" value="#{LoginBean.username}" validator="#{LoginBean.validateUserName}"/>


IMPORTANT: Sometimes the error messages do not get displayed. Instead in your Tomcat log you get the following info message: INFO: WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.. The problem could be related to certain platform versions.

by using the in-built validator tags
edit
	<h:inputText id="username" value="#{LoginBean.username}">
		<f:validateLength minimum="1" maximum="25"/>
	</h:inputText>

Creating the Java bean

edit
package mybeans;

public class LoginBean {
	String username;
	String password;
    
	public String getUsername() {
		return this.username;
	}

	public void setUsername(String username) {
		this.username = username;
	}
    
	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}	
    
	// Used for validating the login
	public String handleForm() {
	
		if (exists()) {            
			return "success";
		}
		else {
			return "fail";
		}
	}

	private boolean exists() {
		// Connect to DB and check username and password exist
		[...]	
	}

}


IMPORTANT: when using several validation you can put them in different beans each extending the Validator class.

Adding navigation rules

edit

Navigation are used for redirecting users after their form is processed. For example they can be redirected to an error page in case their login failed, or to a success page otherwise.

The two pages could look as follows:

  • the success.jsp page:


<html>
	<head>
	</head>
	<body>
		<h1>Login successfully</h1>
	</body>
</html>


  • the failed.jsp page:


<html>
	<head>
	</head>
	<body>
		<h1>Login failed</h1>
	</body>
</html>


Editing the faces-config.xml file

edit

Every JSF has a faces-config.xml file attached. users usually will have to modify this file to match their bean and navigation rules. A typical file could look as following:


<?xml version='1.0' encoding='UTF-8'?>

<faces-config version="1.2" 
    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_1_2.xsd">

	<!-- the bean used by the JSF i.e. for handling the form data -->
	<managed-bean>
		<managed-bean-name>LoginBean</managed-bean-name>
		<managed-bean-class>mybeans.LoginBean</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
	</managed-bean>

	<navigation-rule>
		<from-view-id>/login.jsp</from-view-id>
		<navigation-case>
			<from-action>#{LoginBean.validateForm}</from-action>
			<from-outcome>success</from-outcome>
			<to-view-id>/success.jsp</to-view-id>
		</navigation-case>
		<navigation-case>
			<from-action>#{LoginBean.validateForm}</from-action>
			<from-outcome>fail</from-outcome>
			<to-view-id>/failed.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>
</faces-config>


Using the bean in another JSP

edit

After the JSF has taken care of the form by validating the data and populating the bean one could also require to reuse the bean data in a different JSP. You can accomplish that by using the <jsp:useBean> tag for importing a bean:


<html>
	<head>
		<title>User Info</title>
	</head>
	<body>

		<jsp:useBean id="lbean" scope="session" class="mybeans.LoginBean"/>
		<!--- Or:
		<% mybeans.LoginBean lbean = (mybeans.LoginBean) request.getSession().getAttribute("LoginBean"); %>
		-->
		Username: <%= lbean.getUsername() %><br>
		Password: <%= lbean.getpassword() %><br>
	</body>
</html>


Links:

Exercises

edit
  • The same exercise as in Laboratory 7 but with JSFs instead of servlets.
    • Validate fields using JSF and not Javascript!