JSFSkript

68
Berner Fachhochschule Software Schule Schweiz JavaServer Faces java.sun.com/javaee/javaserverfaces Dr. Beatrice Amrhein May 08

Transcript of JSFSkript

Page 1: JSFSkript

Berner FachhochschuleSoftware Schule Schweiz

JavaServer Facesjava.sun.com/javaee/javaserverfaces

Dr. Beatrice Amrhein

May 08

Page 2: JSFSkript

2

Page 3: JSFSkript

Table of Contents1 Introduction ...............................................................................................................................5 1.1 JSF in Comparison to other Web Frameworks....................................................................................................5 1.1 The JSF Application Structure..............................................................................................................................8 1.2 The JSF Concepts................................................................................................................................................9 1.3 UIComponents.....................................................................................................................................................9 1.4 Renderers...........................................................................................................................................................10 1.5 Validators...........................................................................................................................................................10 1.6 Backing Beans...................................................................................................................................................10 1.7 Converters..........................................................................................................................................................10 1.8 Events and Event Listeners................................................................................................................................10 1.9 Navigation..........................................................................................................................................................10 1.10 The JSF expression language............................................................................................................................11

2 Easy Example: Hello World! ..................................................................................................13 2.1 Hello.jsp..............................................................................................................................................................13 2.2 HelloBean.java...................................................................................................................................................15 2.3 faces-config.xml.................................................................................................................................................16 2.4 web.xml..............................................................................................................................................................16

3 JSF Application Configuration .............................................................................................17 3.1 The Directory Structure......................................................................................................................................17 3.2 The Deployment Descriptor................................................................................................................................17 3.3 The JSF Configuration File.................................................................................................................................18 3.4 Configuration and Navigation Example..............................................................................................................21 3.5 Hello.jsp..............................................................................................................................................................21 3.6 PersonDetails.jsp...............................................................................................................................................22 3.7 Goodbye.jsp.......................................................................................................................................................23 3.8 faces-config.xml.................................................................................................................................................23 3.9 Navigation..........................................................................................................................................................25 3.10 HelloBean.java...................................................................................................................................................26

4 The Standard JSF Components ............................................................................................27 4.1 The Layout of the Components..........................................................................................................................27 4.2 The Output Components....................................................................................................................................28 4.3 The Basic Input Components.............................................................................................................................28 4.4 Choice Input Components..................................................................................................................................29 4.5 Data Tables........................................................................................................................................................31 4.6 Processing Row-Specific Events........................................................................................................................32 4.7 The Command Components..............................................................................................................................34

5 Messaging ...............................................................................................................................35 5.1 Messages...........................................................................................................................................................35 5.2 Overriding standard Messages..........................................................................................................................36 5.3 Constructing Application Messages...................................................................................................................37

6 Request Processing ...............................................................................................................41 6.1 Restore View .....................................................................................................................................................41 6.2 Apply Request Values .......................................................................................................................................42 6.3 Process Validation..............................................................................................................................................42 6.4 Update Model Values ........................................................................................................................................42 6.5 Invoke Application .............................................................................................................................................42 6.6 Render Response..............................................................................................................................................42

7 Validators and Converters .....................................................................................................43 7.1 Standard Validators............................................................................................................................................43 7.2 Validator Methods..............................................................................................................................................44 7.3 Validator Classes...............................................................................................................................................44 7.4 Automatic Conversion........................................................................................................................................45 7.5 Standard Converters..........................................................................................................................................45 7.6 Using Custom Converters..................................................................................................................................46

3

Page 4: JSFSkript

8 Internationalization .................................................................................................................47 8.1 Load the Resource Bundle.................................................................................................................................47 8.2 Configuring Locales............................................................................................................................................48 8.3 Creating Resource Bundles...............................................................................................................................48 8.4 Using Resource Bundles....................................................................................................................................49 8.5 Constructing Localized Messages .....................................................................................................................49 8.6 Using Localized Message Bundles for Application Messages ..........................................................................51

9 Custom Tags ...........................................................................................................................53 9.1 The different parts of a Custom Tag...................................................................................................................53 9.2 The Greater-Than Validator ..............................................................................................................................57

10 JSF Custom Components ......................................................................................................61 10.1 The Component Hierarchy.................................................................................................................................61 10.2 Component Data Collections..............................................................................................................................61 10.3 Value Holder.......................................................................................................................................................62 10.4 Example of a Custom Component ....................................................................................................................63

4

Page 5: JSFSkript

1 Introduction

1.1 JSF in Comparison to other Web Frameworks

Like the Struts framework, JSF defines a set of JSP tags that generate HTML form elements that can be bound to JavaBean properties. The greatest advantage that JSF technology has over Struts is its flexible, extensible UI component model, which includes:

• A standard component API for a wide range of components, including simple ones, such as input fields, or more complex ones, such as scrollable data tables. Developers can also create their own components based on the JSF APIs, and many third parties have already done so and have made their component libraries publicly available (e.g. MyFaces, http://www.myfaces.org/).

• A separate rendering model that defines how to render the components in various ways. For example, a component used for selecting an item from a list can be rendered as a menu or a set of radio buttons.

• An event model that defines how to handle events generated by activating a component, such as what to do when a user clicks a button or a hyper link.

• Conversion and validation.

Because the JavaServer Faces technology architecture separates the definition of a component from its rendering, you can render your components in different ways or even for different clients, such as a WML client. Moreover, the extensible component APIs of JavaServer Faces technology allow you to extend the standard set of components and create entirely new components. None of this is possible with Struts. In fact, Struts has no notion of server-side components, which also means that it has no event model for responding to component events and no facility for saving and restoring component state. While Struts does have a useful tag library for rendering components on the page, these components have no object representation on the server and they can only be rendered to an HTML client.

Another distinct advantage of JavaServer Faces technology is that it has been designed to allow easy integration into tools. As a result, JavaServer Faces technology already has wide industry support and is build in several web application development IDEs (Sun Java Studio Creator, IBM WebSphere Application Developer, Oracle JDeveloper).

5

3

Web Frameworks

HTTP request / response

Integration with Java (session management, error handling, ...)

Form handling and validation

Navigation

Type conversion

Web server

Servlets

Extensible templating mechanism JSP

Struts

Server side handling of UI events

UI component model

Declarative integration of backing beans

JSF

Automatic markup generation

Page 6: JSFSkript

Web browsers don't know anything about JSF components or events. When a user clicks a button in a JSF application, it causes a request to be sent from your web browser to the server, or more precisely to the FacesServlet. JSF is responsible for translating that request into an event that can be processed by your application logic on the server (usually by the backing bean). JSF is also responsible for making sure that every UIComponent you have used on your pages is properly displayed on your browser.JSF applications run on the server and can integrate with other subsystems like EJBs, web services or databases. faces-config.xml is the central configuration file for JSF applications. Here, we define the navigation rules, the used backing beans and so on.

6

Page 7: JSFSkript

A simple JSF PageHere is an example of a simple page with a label („Your Name“) with an input text field and a submit button („Say Hello“).

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" version="2.1"> <jsp:directive.page contentType="text/html"/> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Hello, world!</title> </head> <body> <f:view> <h:form> <h:outputText value="Your Name: "/> <h:inputText value="#{helloBean.name}"/> <h:outputText value="Your Age: "/> <h:commandButton value="Say Hello" action="#{helloBean.sayHello}"/> </h:form> </f:view> </body> </html></jsp:root>

For the web page designer, a JSF page looks similar to a normal HTML or JSP page. As soon as we define the JavaServer tag namespaces (core and html), all JSF tags can be used. These pages can also be designed by an IDE like Sun Java Studio Creator, IBM WebSphere or Oracle JDeveloper.

At runtime, the FacesServlet automatically translates these tags to a HTML form. After translation, the generated HTML page looks as follows:

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

<html><head><title>Hello, world!</title></head> <body> <form id="main" name="main" method="post" action="/helloWorld1/faces/hello.jsp" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="main" value="main" /> <input type="hidden" id="javax.faces.ViewState" value="j_id1:j_id2" /> <span id="main:o1">Your Name: </span> <input id="main:i1" type="text" /> <input id="main:submit" type="submit" value="Say Hello"/> </form></body> </html>

The outputText is converted to normal text, the inputText to a input tag and the submit button to a submit input tag.Hidden input fields are used to transmit state information about the client or server.

7

Page 8: JSFSkript

1.1 The JSF Application Structure

Developing a simple JavaServer Faces application usually requires the following tasks:

• Create the JSP Pages: The JSP Pages using html and core tags.• Develop the Model Object (business logic)• Develop the backing beans with the properties as well as the used action methods.• Add managed bean declarations to the application configuration file (Faces Config). The

configuration file contains the name of the corresponding backing bean.• Define page navigation in the application configuration resource file

In the JSP Page we use the the unified expression language (EL, cf. Section 1.10) to bind UI component values and objects to backing bean properties or to reference backing bean methods from UI component tags.

The HTML Page (with HTML Elements) as well as all the JSF Views (with the necessary UI Components) are automatically generated form the JSP Page. The Faces Servlet is provided by the JSF Application and is responsible for the communication between the server and the clients (Front Controller pattern).

8

Client Server

HTMLPage

HTMLElements

FacesServlet

HTTPJSF View

UIComponents

BackingBean

EL ModelObject

JSP Page FacesConfig

Page 9: JSFSkript

1.2 The JSF ConceptsThe following is a UML class diagram showing the different concepts of JSF. The view object contains a tree of UIComponents (output labels, command buttons, text fields, ...), which are displayed by the dedicated renderer. On user input, the backing beans are automatically updated. Converters translate and format the components value and generate an error message (if necessary). Validators verify the value of a component and generate an error message (if necessary). In general, JSF applications communicate by events. Backing beans contain event listeners and action methods. The outcome of an action method can be used to specify the next page to be shown (navigation). Event listeners consume events and can execute model objects, which perform the core application logic. In JSF applications, model objects don't know anything about the user interface. This enforces a MVC-style architecture.

1.3 UIComponentsLike Swing components, UIComponents are built as JavaBeans. This means, they have properties, methods, and support event handling. Unlike Swing, JSF UIComponents live on the server side, not on the client. Providing these UI elements as components makes development much easier, because the core functions are encapsulated within a reusable package. In order to use a UIComponent, you just have to configure some properties, such as the color or the default value, and you don't have to develop any complicated combinations of HTML, CSS and JavaScript. JSF provides all the standard components like labels, buttons, hyper links, text fields, list boxes, panels or data tables.

9

Page 10: JSFSkript

1.4 RenderersNot all JSF components are responsible for their own rendering. JSF allows separate classes to handle the rendering process. Those classes are called renderers. JSF provides a standard render kit for its UIComponents. However, JSF supports the development of custom renderers as well. Renderers create a visual representation for the client (encoding) and, when JSF receives a response form the user, the renderer extracts the request parameters (decoding).

1.5 ValidatorsValidators are used to examine the correctness of the given input values. JSF handles validation in three ways:

• at the UIComponent level• via validator methods in backing beans • in validator classes

UIComponents usually handle simple validation, such as whether a value is required or not. Validator methods are useful when you need to validate one or more fields on a form (and you don't need this validator for other components). External validators are useful for generic cases. You can attach one or more validators to any input component.

1.6 Backing BeansIn JSF applications the interaction between the model and the user interface is performed by backing beans. They contain properties you want to retrieve from the user and provide event listener methods which can act on these properties and perform some application processing.JSF allows you to declaratively associate backing beans with UIComponents. You can use a JSF EL expression to bind the value of a UIComponent with a specific backing bean property. Whenever the value of the UIComponent changes, the associated backing bean property is automatically updated.

1.7 ConvertersConverters translate an object to a string for display and from an input string to a Java object. Furthermore, they handle formatting and localization. JSF ships with converters for common types like dates, boolean or numbers, but anybody can develop additional converters which are applied internally during encoding and decoding.

1.8 Events and Event ListenersIn JSF applications there is no need of thinking in terms of HTTP requests and responses. Events capture the way the user interacts with UI components. Like Swing, JSF uses JavaBeans to handle events with event listeners and handlers. Any component may fire one or more events, which means you register one or more event listeners with it. There are four standard events: value-change events (value change of a component), action events (clicking on a button or link), data model events (select a row for processing) and phase events (at the beginning and at the end of each phase in the processing life cycle, see chapter 6).

1.9 NavigationJSF provides an elegant navigation system. The navigation handler is responsible for deciding which page to load depending on the outcome of an action method. Navigation rules define what outcomes are understood and which page to load on what outcome. These rules are defined in the JSF configuration file (faces-config.xml).

10

Page 11: JSFSkript

1.10 The JSF expression languageThe main point of JSF expressions is to allow you to reference JavaBean methods, to read or update bean properties, or to evaluate simple statements, without writing any Java code.JSF expressions are used to associate UIComponent properties with backing beans, but you can use them for logical and mathematical statements as well. They are evaluated at runtime.

JSF 1.2 uses the Unified Expression Language (EL), i.e. it uses the same expression language as JSP 2.1. JSF 1.2 depends on the unified EL API (javax.el).

• The EL uses the number sign (#) to mark the beginning of an expression.• EL expressions can be two way: they can retrieve a properties value or update it.• EL expressions can also reference object methods.

Value Expressions:• #{myBean.myProperty} returns the value of myProperty of the object myBean.• #{myBean.myList[2]} returns the second element of (the list or array) myList of

myBean.• #{myBean.myMap['key']} returns the object stored under the key 'key' of the map

myMap.

Method Expressions:• #{myBean.myMethod} assigns the method myMethod of the bean myBean.

Expressions with operators• #{myBean.myProperty > 10} returns true, if the (actual) value of myProperty in

myBean is greater than 10.• #{myBean.myProperty != 20 && myBean.myProperty > 10} returns true, if the

(actual) value of myProperty is not equal to 20 and greater than 10.• #{not empty myBean.myProperty}

returns true, if myProperty is not null, the empty string or an empty collection.

Usage of EL expressions in tags:

• <h:outputText value="#{helloBean.helloText}"/> associates the helloText property of the helloBean backing bean with the output text component.

• <h:commandButton value="Say Hello" action="#{helloBean.sayHello}"/> assigns the action method „sayHello“ of the helloBean backing bean to the command button.

• <h:inputText value="#{helloBean.name}" rendered="#{not empty helloBean.name}" /> renders this input text component only if helloBean.name is not null.

11

Page 12: JSFSkript

12

Page 13: JSFSkript

2 Easy Example: Hello World!We start with an easy but complete example, where we learn to know all the key pieces which belong to a JSF application.

We start with the JSP page:

2.1 Hello.jsp1. <?xml version="1.0" encoding="UTF-8"?>2. <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"3. xmlns:f="http://java.sun.com/jsf/core"4. xmlns:h="http://java.sun.com/jsf/html" version="2.1">5. <jsp:directive.page contentType="text/html"/>6. 7. <html xmlns="http://www.w3.org/1999/xhtml">8. <head>9. <link href="styles.css" rel="stylesheet" type="text/css"/>10. <title>Hello, world!</title>11. </head>12. <body>13. <f:view>14. <h:form id="main">15. <h:outputText value="#{helloBean.helloText}"16. styleClass="header"/>17. <p>18. <h:outputText value="Your Name: "/>19. <h:inputText id="helloInput"20. value="#{helloBean.name}"/>21. </p>22. <p>23. <h:outputText value="Your Age: "/>24. <h:inputText id="helloAge"25. value="#{helloBean.age}"/>26. </p>27. <h:commandButton id="sayHello" value="Say Hello"28. action="#{helloBean.sayHello}"/>29. </h:form>30. </f:view>31. </body>32. </html>33.</jsp:root>

13

Page 14: JSFSkript

Lines 1 to 5: First, we declare the namespaces for the core JSF tag libraries. These provide custom tags like text boxes, output labels and forms. Usually the prefixes „f“ and „h“ are used for these namespaces.The „h“ tag library elements usually render HTML elements, whereas the core tags are independent of a particular render kit (like <f:view>, <f:validator>, <f:converter> and so on). The jsp:directive is used to define the content type of the target HTML page.

Lines 8-11: The header of the HTML page.

Line 13: The <f:view> tag must enclose all other JSF related tags. Line 14: The <h:form> tag represents an HtmlForm component, which is a container for other components and is used for posting information back to the server. We can have more than one HtmlForm on the same page, but all input controls must be nested within a <h:form> tag.

Line 15: The <h:outputText> tag creates an HtmlOutputText component, which displays read-only text on the screen. The value attribute #{helloBean.helloText} specifies an JSF Expression Language reference to the property helloText of the backing bean. JSF automatically synchronizes the value of the outputText with the helloText property. JSF tags can use the usual CSS-styles of HTML.

Lines 19: <h:inputText> creates a new HtmlInputText component that accepts text input. The value attribute #{helloBean.name} references the property name of the backing bean.

Line 27: The <h:commandButton> specifies an HtmlCommandButton component. The value attribute defines the button label, here "Say Hello". HtmlCommandButtons send action events to the application when they are clicked by a user. The action attribute references the action method that computes the new helloText. The outcome of the action method can be used to handle navigation.

14

Page 15: JSFSkript

2.2 HelloBean.java

package helloWorld;public class HelloBean { /** The name of the person. */ private String name; /** The age of the person. */ private int age; /** The hello text. */ private String helloText = "Hello World!"; /** Get the age. */ // property methods for age public int getAge() { return age; } /** Set the age. */ public void setAge(int age) { this.age = age; } /** Get the person name. */ // property methods for name public String getName() { return name; } /** Set the person name. */ public void setName(String name) { this.name = name; } /** Get the hello text. */ // property method for hello text public String getHelloText() { return helloText; } /** Define the text to say hello. */ // action method for „Say Hello“ public String sayHello() { // button if(age < 11) helloText="Hello " + name + "!"; else helloText="Good Morning " + name + "!"; return "hello"; }}The sayHello() method is called, whenever the „Say Hello“ button is pressed. It computes the new helloText. Its return value can be used to define the next page (navigation).

15

Page 16: JSFSkript

2.3 faces-config.xml

<?xml version='1.0' encoding='UTF-8'?> <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_1_2.xsd" version="1.2"> <managed-bean> <description>The HelloBean.</description> <managed-bean-name>helloBean</managed-bean-name> <managed-bean-class>helloWorld.HelloBean </managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean></faces-config>

In the faces-config.xml file we declare all of the managed beans. Those are automatically instantiated. Here the only managed bean is helloWorld.HelloBean. The name of the bean object is helloBean. This name is also used in the EL expressions in hello.jsp (e.g. #{helloBean.name}).It is good practice to choose similar names for the bean class and its object.

2.4 web.xml<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>Hello, World!</display-name> <description>Welcome to JavaServer Faces</description> <!-- Faces Servlet : Front Controller --> <servlet> <servlet-name>FacesServlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet> <!-- Faces Servlet Mapping --> <servlet-mapping> <servlet-name>FacesServlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> <!-- Welcome File --> <welcome-file-list> <welcome-file>faces/hello.jsp</welcome-file> </welcome-file-list> <!-- jsp pages are xml documents --> <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <is-xml>true</is-xml> </jsp-property-group> </jsp-config></web-app>

All J2EE web applications are configured by a deployment descriptor file (web.xml). Furthermore, JSF applications require that you specify the FacesServlet, which is the main servlet (front controller) of the application. All JSF requests are mapped to the FacesServlet.

16

Page 17: JSFSkript

3 JSF Application Configuration

3.1 The Directory Structure

Because JSF applications are standard Java web applications, all of the necessary files must be packed in a directory structure that can be deployed to the web container. For JSF applications, there are two additions: the faces-config.xml file (where all the JSF configuration is placed) and (if your web container doesn't already support JSF) the jar files of your JSF library.For the reference implementation (we use for this course), the required jar files for all JSF applications are jsf-api.jar, jsf-impl.jar, jstl.jar and standard.jar.

3.2 The Deployment DescriptorJSF applications require a servlet, which acts as a front controller for the entire application (FacesServlet). This servlet is configured in the web.xml deployment descriptor file.

<?xml version="1.0"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>Display Name used by GUI Tools</display-name> <description>Description of the Web application </description> <!-- Configuration of faces-config file(s) (comma separated) --> <context-param> <param-name>javax.faces.CONFIG_FILES</param-name> <param-value>/WEB-INF/faces-config.xml</param-value> </context-param> <!-- FacesServlet configuration and mapping --> <servlet> <servlet-name>Name of the Front Controller for your JSF Application,

usually FacesServlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Name of the Front Controller (the same as

above!)</servlet-name> <url-pattern>path/*</url-pattern> </servlet-mapping> <!-- Welcome File --> <welcome-file-list> <welcome-file>path/firstPage.jsp</welcome-file> </welcome-file-list>

17

Page 18: JSFSkript

<!-- all pages ending with .jsp are xml docs --> <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <is-xml>true</is-xml> </jsp-property-group> </jsp-config></web-app>

The value of servlet-name is arbitrary, but the servlet-class must be javax.faces.webapp.Faces- Servlet. The servlet-mapping makes sure, that every request to a resource of the form http://myURL/pattern is handled by the JSF servlet. The faces servlet will then handle the request with the corresponding JSP page.

3.3 The JSF Configuration FileThe main configuration file for a JSF application is the faces-config.xml file. All elements of the configuration file must be enclosed within a <faces-config> element. All top level elements (application, managed-bean, ..., factory) are optional and can be included once or more than once.The first group of elements are used for general application configuration, to declare the backing beans and for the navigation configuration. The second group of elements are used to define custom behaviour (e.g. custom validators or converters). The third group of elements is used to define advanced features like the registration of phase listeners.Usually, you only need the first three configuration elements. The other elements are used for advanced programming.

The faces-config elementsThe application element contains basic configuration information, such as the supported locales and message resource bundle. Strings defined in this bundle can replace standard validation and conversion error messages (cf. Chapter 8).

18

Page 19: JSFSkript

The component, render-kit, validator and converter elements are used to declare custom components, custom renderers, custom validators and custom converters (cf. Chapter 7).

The life cycle element allows you to register one or more phase listeners, which are executed before or after each phase of the Request Processing Life cycle (cf. Chapter 6).

The factory element provides a mechanism to define custom Factories like a custom Application- or FacesContextFactory.

The managed-bean element is used to configure a managed bean, which will be created and initialized by the Managed Bean Creation Facility the first time it is requested by a JSF EL expression. Every managed bean must be declared in a separate entry.

The managed-bean element has a subelement named managed-property which can be used to initialize any read/write properties (Setter Injection pattern).

The following figure shows the full hierarchy of the managed bean element. <description>, <display-name>, <icon>, ... are optional elements, generally used for tools.

19

Page 20: JSFSkript

The managed property element can even be used to assign a reference to a previously defined managed bean. You must be careful about the scope of these objects, because a managed bean can’t reference an object with a shorter life span than the managed bean itself. For an example of a bean reference confer to the faces-config.xml of the next chapter.

Web applications usually have multiple pages, and we must have some way to move between them. For any given page, a navigation-rule defines what outcomes are understood, and what pages to load based on those outcomes. Each specific mapping between an outcome and a page is a navigation-case.

<navigation-rule> <description>description of navigation cases</description> <from-view-id>fromPage</from-view-id>

<navigation-case><from-action>action</from-action><from-outcome>a return value of the action</from-outcome><to-view-id>toPage</to-view-id>

</navigation-case><navigation-case>

<from-action>action</from-action><from-outcome>other return value of the action</from-outcome><to-view-id>toPage</to-view-id>

</navigation-case> . . . (other navigation cases for this page)

</navigation-rule>

The from-action element can be left out, which means that the outcome may be produced by an arbitrary action method or even be a constant value.

The fromPage value can also be an asterisk ( * ), which means that this rule applies to all pages. The same result is achieved by omitting the <from-view-id> element.

<description>, <display-name> and <icon> elements are optional and are generally used for tools.

The next chapter shows a full example of a managed bean with initial values for (some of) its properties and different navigation rules.

20

Page 21: JSFSkript

3.4 Configuration and Navigation ExampleWe extend our first easy example by introducing two new features: navigation rules and managed properties. The hello.jsp page obtains two more buttons (Details / Goodbye). By pressing the Details button, the user loads the persons detail page, the Goodbye button leads to the goodbye page.

3.5 Hello.jsp<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" version="2.1"> <jsp:directive.page contentType="text/html"/> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link href="styles.css" rel="stylesheet" type="text/css"/> <title>Hello, world!</title> </head> <body> <f:view> <h:form> <h:outputText value="#{helloBean.helloText}" styleClass="header"/> <p> <h:panelGrid columns="2" cellpadding="3"> <h:outputText value="Your Name:"/> <h:outputText value="#{personDetails.name}"/> <h:outputText value="Your Age:"/> <h:outputText id="helloAge" value="#{personDetails.age}"/> </h:panelGrid> </p> <h:commandButton value="Say Hello" action="#{helloBean.sayHello}"/> <h:commandButton value="Details" action="details"/> <h:commandButton value="Goodbye" action="#{helloBean.sayGoodbye}"/> </h:form> </f:view> </body> </html></jsp:root>

When we load the hello.jsp page for the first time, the outputText fields for name and age get their values from the personDetails backing bean. The default values for name and age in the backing bean are set by the Managed Bean Creation facility (managed-property in the faces configuration file) at creation time (cf. faces-config.xml below).

21

Page 22: JSFSkript

3.6 PersonDetails.jspThe PersonDetails page can be used to change the properties for name, age and the favorite numbers of the given person. The favorite numbers are saved in an array or vector of integers in the person details backing bean. All of these properties read their initial value at creation time from the faces-config.xml file.

<?xml version="1.0" encoding="UTF-8"?><jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" version="2.1"> <jsp:directive.page contentType="text/html"/> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link href="styles.css" rel="stylesheet" type="text/css"/> <title>Person Details</title> </head>

<body> <f:view> <h:form> <h:outputText value="Person Details" styleClass="header"/> <p> <h:panelGrid columns="2" cellpadding="3"> <h:outputText value="Your Name:"/> <h:inputText value="#{personDetails.name}"/> <h:outputText value="Your Age:"/> <h:inputText id="helloAge" value="#{personDetails.age}" size="4"/> </h:panelGrid> </p> <p> <h:outputText value="Enter your favorite Numbers:"/> <h:panelGrid columns="3" cellpadding="3"> <h:inputText value="#{personDetails.favoriteNumbers[0]}" size="4"/> <h:inputText value="#{personDetails.favoriteNumbers[1]}" size="4"/> <h:inputText value="#{personDetails.favoriteNumbers[2]}" size="4"/> </h:panelGrid> </p>

<h:commandButton value="Back" action="back"/> </h:form> </f:view> </body> </html></jsp:root>

The panelGrid components above is used to align the two input text fields for name and age. The back button leads back to the hello page.

22

Page 23: JSFSkript

3.7 Goodbye.jspThe goodbye.jsp page doesn't need a backing bean for its own, as it only prints out the goodbye helloText.

<?xml version="1.0" encoding="UTF-8"?><jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" version="2.1"> <jsp:directive.page contentType="text/html"/> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link href="styles.css" rel="stylesheet" type="text/css"/> <title>Hello, world! </title> </head> <body> <f:view> <h:form> <h:outputText value="#{helloBean.helloText}" styleClass="header"/> </h:form> </f:view> </body> </html></jsp:root>

3.8 faces-config.xmlThe Managed Bean Creation Facility uses the <managed-bean> element to create an instance of helloWorld.PersonDetails and and store it in the session under the name personDetails the first time the bean is accessed. As long as the same session is in use, the same instance of personDetails will be used. This instance can be referenced with the expression #{personDetails} (or #{sessionScope.personDetails }). The same holds for the helloWorld.helloBean backing bean.

The properties are initialized with <managed-property> elements. The <value> element contains the default value of the property specified in the <property-name> element. The Bean Creation facility uses the associated setter method to assign the initial values to the right properties (Property Injection: cf. Setter Injection pattern).

favoriteNumbers is a vector of (three) numbers. The vector is initialized by the <list-entries> element and its three <values> children. The corresponding setFavoriteNumbers method takes a Vector or a List of values as input argument.

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

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

23

Page 24: JSFSkript

<managed-bean> <description>Detail Information of a person</description> <managed-bean-name>personDetails</managed-bean-name> <managed-bean-class>helloWorld.PersonDetails</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> <managed-property> <property-name>name</property-name> <value>John</value> </managed-property> <managed-property> <property-name>age</property-name> <value>17</value> </managed-property> <managed-property> <property-name>favoriteNumbers</property-name> <property-class>java.util.Vector</property-class> <list-entries> <value-class>java.lang.Integer</value-class> <value>3</value> <value>17</value> <value>347</value> </list-entries> </managed-property> </managed-bean>

<managed-bean> <managed-bean-name>helloBean</managed-bean-name> <managed-bean-class>helloWorld.HelloBean </managed-bean-class> <managed-bean-scope>session</managed-bean-scope> <managed-property> <property-name>personDetails</property-name> <value>#{personDetails}</value> </managed-property> <managed-property> <property-name>helloText</property-name> <value>Hello World!</value> </managed-property> </managed-bean>

To print out the name and the age of the given person, the helloBean backing bean needs a reference to the personDetails backing bean. The reference to the previously defined managed bean (PersonDatails) can be assigned to the helloBean via a normal JSF EL expression (Dependency Injection).

24

Page 25: JSFSkript

3.9 Navigation

In the faces configuration file, we find three navigation cases for hello.jsp and one for the personDetails.jsp page:

• By pressing the „Say Hello“ button, the sayHello action is processed which produces the outcome „hello“. Therefore the hello.jsp page will be reloaded (with the new values entered by the user).

• Pressing the „Details“ button leads us to the personDetails.jsp page.• Pressing the „Goodbye“ button executes the sayGoodbye action method which produces

the outcome „goodbye“, which leads us to the goodbye.jsp page.• From the personDetails page we can go back to the hello.jsp page by pressing the „Back“

button.

<!-- navigation rules --> <navigation-rule> <description>Navigation from the hello page.</description> <from-view-id>/hello.jsp</from-view-id> <navigation-case> <from-action>#{helloBean.sayHello}</from-action> <from-outcome>hello</from-outcome> <to-view-id>/hello.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>details</from-outcome> <to-view-id>/details.jsp</to-view-id> </navigation-case> <navigation-case> <from-action>#{helloBean.sayGoodbye}</from-action> <from-outcome>goodbye</from-outcome> <to-view-id>/goodbye.jsp</to-view-id> </navigation-case> </navigation-rule>

<navigation-rule> <description>Navigation from the Person Details page.</description> <from-view-id>/details.jsp</from-view-id> <navigation-case> <from-outcome>back</from-outcome> <to-view-id>/hello.jsp</to-view-id> </navigation-case> </navigation-rule>

</faces-config>

25

Page 26: JSFSkript

3.10 HelloBean.javaThe helloBean.java file has a new action method for the Goodbye button: sayGoodbye():

package helloWorld;

public class HelloBean {

/** The reference to the person deatils backing bean */ private PersonDetails personDetails;

/** The hello text. */ private String helloText;

/** Set the person details reference. */ public void setPersonDetails(PersonDetails personDetails) {

this.personDetails = personDetails; }

/** Get the hello text. */ public String getHelloText() {

return helloText; }

/** Set the hello text. */ public void setHelloText(String text) {

helloText = text; }

/** Define the text to say hello. */ public String sayHello() { if (personDetails.getAge() < 11) helloText = "Hello " + personDetails.getName() + "!";

elsehelloText = "Good Morning " + personDetails.getName() + "!";

return "hello"; }

/** Define the text to say goodbye. */ public String sayGoodbye() {

if (personDetails.getAge() < 11)helloText = "See you, " + personDetails.getName() + "!";

elsehelloText = "Goodbye " + personDetails.getName() + "!";

return "goodbye"; }}

26

Page 27: JSFSkript

4 The Standard JSF ComponentsIn this chapter, we cover the standard components that all JSF implementations must support. They provide the basic set of functionality necessary for building HTML-based web applications. You can expect third-party developers to provide additional components as well. Here we will focus on the most important components only.We start with the basic attributes, which are applicable to most of the components.

• id: An identifier for a component• rendered: A boolean value, if set to false suppresses rendering of this component• value: The components value, typically a value binding• required: A boolean value, if true a value is required for this input field• styleClass The CSS class name which should be used for this component

4.1 The Layout of the ComponentsHtmlPanelGrid is useful for creating arbitrary, static component layouts (it maps to the <table> element). You can also configure header and footer with facets that map to the <thead> and <tfoot> table subelements, respectively. Child components are organized according to the specified number of columns. When we specify two columns, the first two components form the first row (one per column), the next two form the second row, and so on. The width, border, and cellpadding properties are passed through to the HTML table. Unlike the HTML table, you don’t have to explicitly denote columns and rows.

<h:panelGrid columns="2" headerClass="page-header" cellpadding="1" border="1" width="40%">

<f:facet name="header"> <h:outputText value="This is the table header"/> </f:facet> <h:outputText value="(1,1)"/> <h:outputText value="(1,2)"/> <h:outputText value="(2,1)"/> <h:outputText value="(2,2)"/> </h:panelGrid>

The HtmlPanelGroup component groups a set of components together, so that they can be treated as a single entity.

<h:panelGrid columns="2" headerClass="page-header" cellpadding="1" border="1" width="40%">

<h:outputText value="(1,1)"/> <h:panelGroup> <h:outputText value="(1,2.1)"/> <h:outputText value="(1,2.2)"/> </h:panelGroup> <h:panelGroup> <h:outputText value="(2.1,1)"/> <h:outputText value="(2.2,1)"/> </h:panelGroup> <h:outputText value="(2,2)"/> </h:panelGrid>

27

Page 28: JSFSkript

4.2 The Output Components

The purpose of the output components is to display data. You can specify literally the data that they display or have them display properties from backing beans. The most basic output component is HtmlOutputText (<h:outputText>). HtmlOutputText converts the given value to a string and displays it with optional CSS style support. Example: <h:outputText value="#{helloBean.helloText}" styleClass="header"/> <h:outputText value="#{helloBean.goodbyeText}" styleClass="header"/>

The HtmlOutputLabel component is used for associating labels with form elements (InputText, ChoiceButton, ...). HtmlOutputLabel is translated to the HTML <label> element. This allows target devices to define shortcuts for input elements.

Example<h:panelGroup> <h:outputLabel for="helloInput" accesskey="N"> <h:outputText value="Your Name:"/> </h:outputLabel> <h:inputText id="helloInput" value="#{helloBean.name}"

required="true" accesskey="N"/></h:panelGroup>

4.3 The Basic Input Components

The HtmlInputText component is used for basic user input. It maps to a simple text field—the <input> element with type “text”.

<h:inputText id="helloInput" value="#{helloBean.name}" size="30" required="true"/> <h:inputText id="helloAge" value="#{helloBean.age}" size="4" disabled="true"/>

The size and disabled properties are passed through to the HTML input tag. In this example, the component is associated via a value-binding expression with the name property of helloBean. HtmlInputText is the most common input control, but it is restricted to a single line.

If you need a multi line input field, you can use the HtmlInputTextarea component.<h:inputTextarea value="#{helloBean.helloText}" rows="5" />

28

Page 29: JSFSkript

The HtmlInputSecret component is used to display a password input field. Any text the user types is displayed using the asterisk or some other character (depending on the browser).

<h:inputSecret value="#{user.password}" size="10" maxlength="10"/>

For the input of boolean values, we can use a HtmlSelectBooleanCheckbox component.

<h:outputLabel for="accepted" accesskey="A"> <h:outputText value="Accepted"/> </h:outputLabel><h:selectBooleanCheckbox value="#{helloBean.accepted}" id="accepted"/>

The selectBooleanCheckbox component works fine for a single check box on a form. If you want to associate multiple check boxes with a single property, you preferably use a HtmlSelectMany-Checkbox (see next chapter).

4.4 Choice Input ComponentsUser interfaces often allow a user to select one or more items from a set of possible choices. In JSF, a selection item represents a single choice, and has a value, a description, and a label. Components in the SelectMany and SelectOne families, like HtmlSelectManyCheckbox and HtmlSelectOneListbox, display lists of items.

A UISelectItem component are used to display items in a list and to select one or more items from this list. You can either use the value itemDescription, itemLabel, and itemValue properties or a value-binding expression.

<f:selectItem itemValue="0" itemLabel="cats"/><f:selectItem value="#{myBean.animal[3]}"/>

Alternatively, you can use a selectItems tag with a value binding to a list or array of elements. <f:selectItems value="#{myBean.animalList}"/>

These selectItem(s) tag can be used to configure another component’s selection list (e.g. for components like HtmlSelectManyCheckbox, HtmlSelectOneRadio, HtmlSelectOneListbox or HtmlSelectOneMenu).

All of these choice components work in a similar way. Therefore, only one of these components is described here: HtmlSelectOneMenu.

HtmlSelectOneMenu displays its child items in a list box using the HTML <select> element. The number of items displayed is always one, so the result is a combo box (also called a drop-down list box).

29

Page 30: JSFSkript

<h:outputText value="Your Favorite Animal "/><h:selectOneMenu id="animal" value="#{helloBean.animal}" accesskey="A"><f:selectItems value="#{helloBean.animals}"/>

</h:selectOneMenu>

In the corresponding JavaBean, you have to provide an array or a list property named animals, which can either be initialized in the backing bean (e.g. as list of strings) or in the faces configuration file.Initialization in the faces configuration file (faces-config.xml):

<managed-bean> <description>The one and only HelloBean.</description> <managed-bean-name>helloBean</managed-bean-name> <managed-bean-class>helloWorld.HelloBean </managed-bean-class> <managed-bean-scope>session</managed-bean-scope> <managed-property> <property-name>animals</property-name> <list-entries> <value>Cat</value> <value>Dog</value> <value>Bird</value> </list-entries> </managed-property></managed-bean>

In the backing bean, you have to provide the corresponding get and set methods for the favorite animal and the selection list of animals:

private String animal; private List<SelectItem> animals;

public String getAnimal() {return animal;

} public void setAnimal(String a) {

this.animal = a; } public List<SelectItem> getAnimals() {

return animals; } public void setAnimals(List<String> items) {

animals = new ArrayList<SelectItem>();for (String item : items)

animals.add(new SelectItem(item)); }

30

Page 31: JSFSkript

4.5 Data TablesThe HtmlDataTable component displays an HTML <table>. The columns are specified with column components. For each row, HtmlDataTable uses the column components as atemplate for each column. If the facet tag has a name=“header“ attribute, it is translated to a <thead> (table header) HTML tag. The var property defines the name of the iterator variable (here person). If the row property is specified, it displays that many rows (starting with the first).

<h:dataTable value="#{helloBean.friends}" var="person" first="0" rows="2"> <h:column> <f:facet name="header"> <h:outputText value="Last Name"/> </f:facet> <h:inputText value="#{person.lastName}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="First Name"/> </f:facet> <h:inputText value="#{person.firstName}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Age"/> </f:facet> <h:inputText value="#{person.age}"/> </h:column></h:dataTable>

In the backing bean, you have to provide a corresponding list of friends (here Person) with a firstName, a lastName and an age attribute.

private Person[] friends = { new Person("Hans", "Muster", 33), new Person("Peter", "Meier", 44), new Person("Heidi", "Schmid", 22)};

public Person[] getFriends(){return friends;

}

The Person class provides the corresponding entries:public class Person {

String firstName;String lastName;int age;Person(String firstName, String lastName, int age){

this.firstName = firstName;this.lastName = lastName;this.age = age;

}

31

Page 32: JSFSkript

public String getFirstName(){return firstName;

}public void setFirstName(String firstName){

this.firstName = firstName;}

public String getLastName(){return lastName;

} . . .

}

4.6 Processing Row-Specific EventsA very common application feature is to have a table with summery information where each row is linked to the details about the item in that row. To implement this feature, we must figure out which table row the user selected to make the corresponding details available for display.

In the JSF table we have to change the following entries:

<h:dataTable value="#{personHandler.personModel}" var="person" border="1" cellspacing="0" rows="2"> <h:column> <f:facet name="header"> <h:outputText value="Last Name"/> </f:facet> <h:commandLink action="#{personHandler.select}" immediate="true"> <h:outputText value="#{person.lastName}"/> </h:commandLink> </h:column> <h:column> <f:facet name="header"> <h:outputText value="First Name"/> </f:facet> . . . </h:column></h:dataTable>

The table looks similar to the previous one, but there are some important differences. The Last- Name column uses an <h:commandLink> component instead of an outputText and the value of the <h:dataTable> is now an instance of javax.faces.model.DataModel instead of the previous array of Person.

How can we determine which row of the table has been selected when handling the action event?You could find it out by using a query string parameter or a hidden field. However, JSF offers a much easier way to represent tabular data through the abstract DataModel class. DataModel has two subclasses: ListDataModel and ArrayDataModel. These are convenience implementations of the abstract DataModel class with the following properties:

32

Page 33: JSFSkript

• boolean: rowAvailable is true, when the rowIndex property holds the index of an existing row, false otherwise.

• int: rowCount is the number of rows (or -1 if unknown)• Object: rowData the object at rowIndex (or null if rowIndex = -1)• int: rowIndex gives the currently selected row (or -1, if no row is selected)• Object: wrappedData is the object representing the table.

The <h:dataTable> uses the personModel DataModel to read the table entries from. There are no changes necessary in the HelloBean or Person class. All we need is a new PersonHandler class as a wrapper for the array of Person objects.

package helloWorld;import javax.faces.model.*;

public class PersonHandler { private DataModel personModel = new ArrayDataModel(); private HelloBean helloBean; private Person selectedPerson;

public DataModel getPersonModel() {personModel.setWrappedData(getPersons());return personModel;

}

public Person[] getPersons() {return helloBean.getFriends();

}

public String select() {selectedPerson = (Person) personModel.getRowData();return "success";

}

public Person getSelectedPerson() { return selectedPerson; }

public void setHelloBean(HelloBean helloBean) { this.helloBean = helloBean; }}

The getPersonModel() method creates an instance of the javax.faces.model.ArrayDataModel class which is a subclass of DataModel. Then, it populates the ArrayDataModel with the Person array returned by getPersons(). Other sublasses of DataModel are ListDataModel (for Lists) or ResultSetDataModel (for a JDBC ResultSet).

The <h:commandLink> in each table row is bound to the PersonHandlers select() method. The renderer together with the UIData component ensure that the DataModels rowIndex is positioned to the selected row when the action method is invoked. Therefore, we can use getRowData() in the select() method to read the data of the selected person.

33

Page 34: JSFSkript

4.7 The Command ComponentsThe command components represent an action initiated by the user. The two standard command components are HtmlCommandButton (for displaying a button) and HtmlCommandLink (for displaying a hyper link). Because they perform the same operation, their usage is quite similar.

<h:commandButton value="Say Hello" action="#{helloBean.sayHello}"/>

<h:commandButton value="Goodbye" action="#{helloBean.sayGoodbye}"/>

The corresponding action Methods are defined in the backing bean:

public String sayHello() {if (age < 11)

helloText = "Hello " + name + "!";else

helloText = "Good Morning " + name + "!";return "hello";

}

34

Page 35: JSFSkript

5 Messaging

5.1 MessagesMultiple parts of a JSF application (validators, converters, application exceptions ... ) can generate messages. Usually, these messages have to be displayed to the user.

HtmlMessage is designed to display a message for a specific UI component, and is useful whenever you need to inform the user of any errors. It is typically used with input controls, but that’s not a requirement. Messages contain a summary and a detail text.

Example: Message for input control helloInput.

<h:message for="helloInput" showDetail="true" showSummary="true"errorStyle="color: red"/>

The attributes showDetail and showSummary are optional. The default value for showDetail is true, that for showSummary is false.

Every message has a severity level which is equal to one of the following values.• Info - Represents an informational text to be send back to the user. You can insert an

infoClass or an infoStyle attribute to define a CSS class / CSS style for messages with Info severity.

• Warn - Indicates that an error may have occurred.You can insert a warnClass or a warnStyle attribute to define a CSS class / CSS style for messages with Warn severity.

• Error - Indicates an error like validation or conversion error. You can insert an errorClass or an errorStyle attribute to define a CSS class / CSS style for messages with error severity.

• Fatal - Indicates a serious (application) error. You can insert an fatalClass or a fatalStyle attribute to define a CSS class / CSS style for messages with fatal severity.

Whenever possible, the error is placed next to the component (so the user knows where the problem arises). If more than one message is registered for a component (which can happen if more than one validator is registered, or if a validator has problems with conversion), HtmlMessage displays only the first one.

If you need to display multiple messages for a page, you should use HtmlMessages instead.

Example: all messages for this page:<h:messages showDetail="true" showSummary="true" layout="table"

errorStyle="color: red"/>

35

Page 36: JSFSkript

5.2 Overriding standard MessagesAll of the standard validators and converters have default messages. These are configured using a normal Java message bundle. In the reference implementation, for example, we would find the following key/value pair, creating a message for an empty input field with a required=“true“ attribute:

javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required.javax.faces.component.UIInput.CONVERSION={0}: Conversion error occurred.

The used parameters are defined in the JSF documentation for the corresponding messages.

In general, these default messages are not very user friendly. Therefore we often would like to override these texts with custom messages. One possibility is to define an error message in the tag of the corresponding input component. Possible values are – requiredMessage for required input,– converterMessage for conversion errors and – validatorMessage for validation errors.

<h:inputText id="helloInput" value="#{helloBean.name}" required="true" requiredMessage="Please enter your name into this input field">

Furthermore, the details message texts are often missing. We can override the default messages by a custom message bundle. Each JSF application can be associated with a single message bundle (e.g. messages.properties), which is configured in the faces-config.xml file:

<faces-config> <application> <!-- messages.properties file at /WEB-INF/classes --> <message-bundle>messages</message-bundle> </application> . . . </faces-config>Once, you have defined a message bundle, you can selectively override the standard messages, as long as you use the correct keys (see java.sun.com/javaee/javaserverfaces/1.2_MR1/docs/api/index.html for the list of message keys).

The messages.properties file then might look as follows:

javax.faces.converter.IntegerConverter.INTEGER=The given input value '{0}' is not an integer.javax.faces.converter.IntegerConverter.INTEGER_detail=The input value should be an integer like 17.javax.faces.component.UIInput.REQUIRED=Input value for {0} is required.javax.faces.component.UIInput.REQUIRED_detail=Input value for {0} must not be void.

Whenever a JSF component, validator or converter looks for an error message, it will look for them in the JSP page and in the custom messages bundle (messages.properties). Only if there is no user defined message, it will take the predefined standard message.

36

Page 37: JSFSkript

5.3 Constructing Application MessagesWhenever one of your backing beans throws an exception or when an error occurs in your application logic (e.g. system or connection errors), you want to inform the users about this.For this, you can create a message in your backing bean (or validator or converter class or method).

Creating a Faces Message in the Backing BeanIn the following action method we create a FacesMessage to inform the users about errors in the name input field. The FacesMessage constructor takes the arguments severity, the summary string and the detail string. /** Define the text to say hello. */ public String sayHello() { if (!isValidName(name)) { // create faces message

FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Name is not valid!",

"Your name must not be empty and not start with letter 'A'");

// add message to faces context FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, message); helloText = "Hello!";

} else { // name is valid if (age < 10) helloText = "Hello " + name + "!"; else helloText = "Good Morning " + name + "!"; } return "hello"; }

/** test whether given string is a valid name. */ private boolean isValidName(String s) { return (name.length() > 0 && name.charAt(0) == 'A'); }

You have to add this messages to the FacesContext, from where the JSP page will read it afterwards: context.addMessage(null,message));

The addMessage function takes two parameters: the message id (either the component id or null, if the message does not belong to any component) and the FacesMessage.

Application messages do not belong to any component on the jsp page. If we want to retrieve the application messages only (the messages, which belong to no component) we use the globalOnly attribute:

<h:messages globalOnly=“true“>.

37

Page 38: JSFSkript

Retrieving Message Texts from the Message Bundle

In the previous example, we used hard coded message texts from our backing beans. Usually, you want to read those messages from a message bundle, instead. Therefore, you preferably use the custom message bundle to define your application messages and read these message texts from there. In the following example we take this improved approach. In your backing bean, you create a message (of type FacesMessage) by first extracting the name of the message bundle and from this the summary and detail string of the desired message.

The following MessageFactory provides us with the desired FacesMessage:

public class MessageFactory { public static FacesMessage getMessage(FacesMessage.Severity severity, String key, Object... params) { String summary = "?? key " + key + " not found ??"; String detail = null; FacesContext context = FacesContext.getCurrentInstance(); String name = context.getApplication().getMessageBundle(); // ( 1 ) if (name == null) name = FacesMessage.FACES_MESSAGES; ResourceBundle bundle = ResourceBundle.getBundle(name); // ( 2 ) try { summary = bundle.getString(key); // ( 3 ) summary = MessageFormat.format(summary, params); detail = bundle.getString(key + "_detail"); // ( 3 ) detail = MessageFormat.format(detail, params); } catch (MissingResourceException e) {} return new FacesMessage(severity, summary, detail); // ( 4 ) }}

38

Page 39: JSFSkript

Example: Print out an information message for invalid name values (cf. HelloWorld4).

helloBean.java /** Define the text to say hello. */ public String sayHello() { FacesContext context = FacesContext.getCurrentInstance(); if (!isValidName(name)) { // create hello message and add it to faces context context.addMessage(null, MessageFactory.getMessage( FacesMessage.SEVERITY_ERROR, "helloWorld.nameMessage", name));

helloText = "Hello!"; } else { if (age < 10) helloText = "Hello " + name + "!"; else helloText = "Good Morning " + name + "!"; } return "hello"; }

/** test whether given string is a valid name. */ private boolean isValidName(String s) { return (name.length() > 0 && name.charAt(0) == 'A'); }

The messages.properties file for the application message contains messages of the form:javax.faces.component.UIInput.REQUIRED= This value is required.javax.faces.component.UIInput.REQUIRED_detail={0}: Input value must not be void.javax.faces.converter.IntegerConverter.INTEGER=The given input value ''{0}'' is not an integer.javax.faces.converter.IntegerConverter.INTEGER_detail={2}: The input value should be an integer like 17.

helloWorld.nameMessage= Name ''{0}'' is not valid!

hello.jsp<h:form id="main"> <h:outputText value="#{helloBean.helloText}" styleClass="header"/> <h:panelGrid columns="3"> <h:outputText value="Your Name"/> <h:inputText id="name" label="Name" value="#{helloBean.name}" required="true" requiredMessage="Please enter your name"/> <h:message for="name" errorClass="errorStyle" showDetail="false" showSummary="true"/>

39

Page 40: JSFSkript

<h:outputText value="Your Age"/> <h:inputText id="age" label="Age" value="#{helloBean.age}" required="true"/> <h:message for="age" errorClass="errorStyle" showDetail="false" showSummary="true"/>

<h:outputText value="Country"/> <h:inputText id="country" label="Country" value="#{helloBean.country}" required="true"/> <h:message for="country" errorClass="errorStyle" showDetail="false" showSummary="true"/> </h:panelGrid>

<h:commandButton value="Hello" action="#{helloBean.sayHello}"/> <h:commandButton value="Goodbye" action="#{helloBean.sayGoodbye}"/> <p> <h:messages layout="table" showDetail="true" showSummary="false" infoClass="infoStyle" errorClass="errorStyle" globalOnly="false"/> </p></h:form>

We then obtain either one or more error messages (in case of any input errors):

or the application message created by the MessageFactory:

40

Page 41: JSFSkript

6 Request ProcessingWhen a JSF view is requested, the components in the view are asked to perform tasks in a certain order, defined as phases of a request processing life cycle. The following graphic shows the main tasks for each phase.

6.1 Restore View • Extract the view ID and find current view • Look up the components for the current view, create them if necessary • Restore or create the component values, event listeners, validators and converters • If there is no (new) user input, skip to render response phase (6)

41

Page 42: JSFSkript

6.2 Apply Request Values • Decoding: extract all user input from the input fields (and save them in the submittedValue

attribute of the component).• Create action events and add them to the faces context for later processing.• If the immediate attribute of a component is set to true, validation takes place in this phase

instead of the process validation phase.• If there is a command button or a hyper link with an immediate property set to true, the

corresponding actions will be fired in this phase instead of the invoke application phase. This can be used for example for Cancel buttons, because it prevents the validation of any input fields.

6.3 Process Validation• Convert submitted values: Convert the value of the submittedValue attribute of the

component from string type to object. • Validate submitted values: After conversion, the value is validated and set as the local value

of this component.• If the conversion or the validation process throws an exception, the control will skip to the

render response phase (6).• If the local value has changed, a valueChangeEvent is fired and consumed by the registered

listeners.

6.4 Update Model Values • Evaluate the JSF EL expressions in the components' value attribute to find the associated

backing beans and properties (depending on the beans' scope: request, session, application).• The set methods of these properties are called, which update the backing bean with the

(new) validated and converted local values of the components.

6.5 Invoke Application • Handle all fired events.• Execute default action listener method.• Call the event handler methods of all registered event listeners.• Choose the next page based on the action method's outcome.

6.6 Render Response• Save the state of the new view for later use in the restore view phase (1).• Invoke the converters (object to string).• Display the next view with the new values from the backing beans.

42

Page 43: JSFSkript

7 Validators and ConvertersJSF supports validation and conversion through validator or conversion methods of backing beans and validator and converter objects.

Validation checks the value of a control to see if its current value is acceptable. The associated object in the backing bean is updated only if the value is accepted. Otherwise, an error message is generated for that specific component, and the associated object is not modified. Theseerror messages can then be displayed back to users with a <h:message> or <h:messages> tag.

When users see an object on the screen, they see it in terms of recognizable text, like May 23, 1980. A Java Date object, on the other hand, is much more than just a string. Two Date objects can be compared and we can compute the elapsed time between the two dates.This is the purpose of a converter - to create a string representation of an object, and to create an object representation of a string.JSF provides a set of standard converters to satisfy the basic type conversion needs. You can also write your own converters, and third-party vendors will provide them as well. As with validators, converters create converter exceptions and send them back to the user as faces messages.

7.1 Standard ValidatorsJSF includes a few standard validators for the most common validation problems:

<f:validateLength> can be used to ensure that the number of letters given in the input field is greater/smaller than or equal to the specified minimum/maximum value.

<h:outputText value="Your Name:"/><h:inputText id="helloInput" value="#{helloBean.name}">

<f:validateLength minimum="2" maximum="10"/></h:inputText><h:message for="helloInput" styleClass="errorMessage"/><f:validateLongRange> and <f:validateDoubleRange> can be used to ensure that the (long/double) value inserted in the input field is greater than or equal to the specified maximum (long/double) value (smaller than or equal to the minimum value).

<h:inputText id="helloAge" value="#{helloBean.dateOfBirth}"><f:validateLongRange minimum="0" maximum="120"/>

</h:inputText><h:message for="helloAge" styleClass="errorMessage"/>

43

Page 44: JSFSkript

7.2 Validator MethodsAn input field can also be associated with a validation method on a backing bean. Validation methods are generally used for application specific validation and can not be reused for different applications. For any input component you can register one or more validators.

<h:inputText id="helloInput" value="#{helloBean.name}" validator="#{helloBean.nameCheck}"> <f:validateLength minimum="2" maximum="10"/>

</h:inputText>

The corresponding nameCheck method in the backing bean:

public void nameCheck( FacesContext context, UIComponent component, Object value)

{ if (!value.toString().trim().matches("[A-Z][a-z]*")) { FacesMessage message = MessageFactory.getMessage( FacesMessage.SEVERITY_ERROR, "NameValidator.INVALID_NAME"); throw new ValidatorException(message); }}

7.3 Validator ClassesValidator classes raise some additional work. On the other hand, validator classes are generic and designed for use in different applications.The validator interface demands for one method: validate().

public class NameValidator implements Validator { /** The identifier of the validator. */ public static final String VALIDATOR_ID = "NameValidator"; /** The identifier of the error message. */ public static final String INVALID_NAME_MESSAGE_ID

= "NameValidator.INVALID_NAME"; public void validate(FacesContext context,

UIComponent component, Object value) { if (!value.toString().trim().matches("[A-Z][a-z]*")) { FacesMessage message = MessageFactory.getMessage( FacesMessage.SEVERITY_ERROR, INVALID_NAME_MESSAGE_ID); throw new ValidatorException(message); } }}

To ensure that this validator class is instantiated, we have to declare it as validator in the faces-config.xml file:

44

Page 45: JSFSkript

<faces-config> <managed-bean> . . . </managed-bean> <navigation-rule> . . . </navigation-rule> <validator> <validator-id>NameValidator</validator-id> <validator-class>helloWorld.NameValidator</validator-class> </validator></faces-config>

This class can then be used as validator in any JSP page:

<h:inputText id="helloInput" value="#{helloBean.name}"> <f:validator validatorId="NameValidator"/> </h:inputText>

7.4 Automatic ConversionIf you don’t specify a converter, JSF will pick one for you. The JSF framework has standard converters for all the basic Java types: BigDecimal, BigInteger, Boolean, Byte, Character, Integer, Short, Double, Float, and Long. All primitive types are converted to their object counterparts. So for all these types, conversion is performed automatically.

7.5 Standard ConvertersThere are two converter tags: <f:convertDateTime> and <f:convertNumber>

<h:inputText value="#{helloBean.dateOfBirth}"> <f:convertDateTime type="date"/></h:inputText>f:convertDateTime can be used to convert a Date object to a string (and vice versa). The type attribute is optional and can be either date or time or both, depending on what you want to print out.

The number converter is useful for displaying numbers in basic formats like a currency or a percentage.

<h:inputText value="#{helloBean.price}"> <f:convertNumber pattern="SFr. #,##0.00"/> </h:inputText>We would obtain a similar result by the code:

<h:inputText value ="#{helloBean.price}"> <f:convertNumber maxFractionDigits="2"/>

</h:inputText>

45

Page 46: JSFSkript

7.6 Using Custom ConvertersIn many cases, the standard converters will be sufficient; they handle all of the standard Java data types, and also provide sophisticated formatting functionality for dates and numbers. You have to develop custom converters any time you want to make it easy for a front-end developer to display a special data type, or accept input for that special data type.

In our example, the user can insert a two letter country code, which is automatically converted into the corresponding country name.

public class CountryConverter implements Converter { /** The identifier of the converter. */ public static final String CONVERTER_ID = "CountryConverter"; /** The identifier of the error message. */ public static final String COUNTRY_CONVERTER_MESSAGE_ID =

"CountryConverter.INVALID_COUNTRY_KEY";

HashMap<String, String> countries; public Object getAsObject(FacesContext context, UIComponent component, String value)

throws ConverterException { if(value.trim().length() < 2) {

FacesMessage message = MessageFactory.getMessage( ... ); throw new ConverterException(message);

} if(countries.containsKey(value))

return countries.get(value); return value; } public String getAsString(FacesContext context,

UIComponent component, Object value) throws ConverterException {return value.toString();

}}

You can use this converter in the JSP page: <h:inputText id="country" value="#{helloBean.country}">

<f:converter converterId="CountryConverter"/></h:inputText>

The converter has to be configured in the faces-config.xml file:

<converter> <converter-id>CountryConverter</converter-id> <converter-class>helloWorld.CountryConverter

</converter-class></converter>

46

Page 47: JSFSkript

8 Internationalization

Enabling an application to support multiple locales is called internationalization. The process of modifying an application to support a (new) locale is called localization. Some implementations, like the reference implementation, provide localized messages for validator and converter errors for certain languages (English, French and German).

8.1 Load the Resource BundleUsing a resource bundle in a JSF application is as simple as using a value-binding expression. All you have to do is to define the proper resource bundle in the faces-config file.

<faces-config . . . > <application> <locale-config> <default-locale>en</default-locale> <supported-locale>de</supported-locale> </locale-config> <message-bundle>messages</message-bundle> <resource-bundle> <base-name>texts</base-name> <var>label</var> </resource-bundle> </application>

If the bundles are placed in WEB-INF/classes, no prefix is needed. However, if we had nested them deeper, like in the WEB-INF/classes/org/jia directory, the basename would be “org.jia.texts”.

Once the bundle has been loaded, you can grab a particular string from that bundle with an ordinary value-binding expression such as #{label.theKey}.If the resource bundle can not find a key, it will return the string “??? key ???” instead.

With JSF 1.1 you had to define the texts with the <f:loadBundle> tag. The following tag loads the bundle and stores it in the request scope.

<f:view ... > <f:loadBundle basename="texts" var="label"/> . . . . <f:view>

The basename attribute specifies the name of the resource bundle, prefixed by its location in your classpath. The var attribute specifies the name which is used in your JSF component tags. Both attributes (basename and var) are required.

47

Page 48: JSFSkript

8.2 Configuring LocalesThe first step towards supporting multiple languages is to tell your JSF application which locales it should support and which of the supported locales should be used as the default locale. You specify the supported locales with the <locale-config> element in the faces configuration file:

<faces-config> <application> <locale-config> <default-locale>en</default-locale> <supported-locale>de</supported-locale> <supported-locale>fr</supported-locale> <supported-locale>it</supported-locale> </locale-config> </application> . . .

</faces-config>

8.3 Creating Resource Bundles

Resource bundles are not a JSF feature—they are a fundamental part of the way Java handles internationalization and localization. Resource bundles are normal property files with key/value pairs. Each string has a key, which is the same for all locales. So the key „helloWorld“ points to the string that represents the welcome message, whether it is in English, French, or German.

texts_de.properties helloWorld=Hallo, ihr alle! yourName=Ihr Name dateOfBirth=Geburtsdatum country=Land hello=Hallo goodMorning=Guten Tag goodbye=Auf Wiedersehen seeYou=Tschüss

texts_en.properties helloWorld=Hello, world! yourName=Your Name dateOfBirth=Date of Birth country=Country hello=Hello goodMorning=Good Morning goodbye=Goodbye seeYou=See you

48

Page 49: JSFSkript

8.4 Using Resource BundlesOnce you have created one or more resource bundles, you will have to make sure that your JSF components use the string in the bundle instead of a hard coded text value:

<h:outputText value="#{label.yourName}"/><h:outputText value="#{label.dateOfBirth}"/><h:commandButton value="#{label.goodbye}"

action="#{helloBean.sayHello}"/>

If you need localized texts in your backing beans, a getText method like the following can be useful: private String getText(String key) { String text = "???" + key + "???"; try { FacesContext context = FacesContext.getCurrentInstance(); Locale locale = context.getViewRoot().getLocale(); ResourceBundle bundle = ResourceBundle.getBundle("texts", locale); text = bundle.getString(key); } catch (MissingResourceException e) {} return text;}You can use this method to extract the correct string from the bundle: helloText = getText("goodbye") + " " + name + "!";

8.5 Constructing Localized Messages In order to localize your error or information messages, you can configure an application-wide message bundle. The name of your message bundle (here messages) and the supported locales have to be defined in the faces-config.xml file:

<faces-config> <application> <locale-config> <default-locale>en</default-locale> <supported-locale>en</supported-locale> <supported-locale>de</supported-locale> </locale-config> <message-bundle>messages</message-bundle> </application> . . . </faces-config>

49

Page 50: JSFSkript

For each of the supported locales, you have to provide a corresponding messages_xx.properties file:

messages_de.properties (German message bundle)javax.faces.validator.LengthValidator.MAXIMUM=Die Eingabe ist zu lang.javax.faces.validator.LengthValidator.MAXIMUM_detail=Der Eingabewert darf höchstens {0} Zeichen lang sein.javax.faces.validator.LengthValidator.MINIMUM=Die Eingabe ist zu kurz.javax.faces.validator.LengthValidator.MINIMUM_detail=Der Eingabewert muss mindestens {0} Zeichen lang sein.NameValidator.INVALID_NAME=Ungültiger Namenswert.NameValidator.INVALID_NAME_detail=Namenswert entspricht nicht dem Pattern [A-Z][a-z]*CountryConverter.INVALID_COUNTRY_KEY=Ungültiger Ländercode.CountryConverter.INVALID_COUNTRY_KEY_detail=Land muss ein Ländercode aus 2 Buchstaben sein

messages_en.properties (English message bundle)javax.faces.validator.LengthValidator.MAXIMUM=Input value too long.javax.faces.validator.LengthValidator.MAXIMUM_detail=The input value can''t be more than ''{0}'' characters long.javax.faces.validator.LengthValidator.MINIMUM=Input value too short.javax.faces.validator.LengthValidator.MINIMUM_detail=The input value must be at least ''{0}'' characters long.NameValidator.INVALID_NAME=Invalid value for name.NameValidator.INVALID_NAME_detail=Name value does not match pattern [A-Z][a-z]*CountryConverter.INVALID_COUNTRY_KEY=Invalid country key.CountryConverter.INVALID_COUNTRY_KEY_detail=Country must be a 2 letter country code

JSF will use your bundles to override default validation and conversion error messages.

The corresponding JSP page uses a normal message tag:<h:message for="country" styleClass="errorMessage"/><h:messages showDetail="true" layout="table" styleClass="errorMessage"/>

50

Page 51: JSFSkript

8.6 Using Localized Message Bundles for Application Messages You can also use the message bundle to create your own (error) messages.

In your backing bean, validator or converter, you can create a message (of type FacesMessage) by first extracting the client's locale, the name of the message bundle and from this the summary and detail string of the dedicated message. The FacesMessage constructor takes the arguments severity (SEVERITY_INFO, SEVERITY_WARN, SEVERITY_ERROR, or SEVERITY_FATAL) summary string and detail string.

The MessageFactory from chapter has to be changed somewhat to provide us with the desired localized FacesMessage:

MessageFactory.javapublic class MessageFactory {

public static FacesMessage getMessage(FacesMessage.Severity severity, String key, Object... params) { String summary = "?? key " + key + " not found ??"; String detail = null; FacesContext context = FacesContext.getCurrentInstance(); String name = context.getApplication().getMessageBundle(); if (name == null) name = FacesMessage.FACES_MESSAGES; Locale locale = context.getViewRoot().getLocale(); ResourceBundle bundle = ResourceBundle.getBundle(name, locale); try { summary = bundle.getString(key); summary = MessageFormat.format(summary, params); detail = bundle.getString(key + "_detail"); detail = MessageFormat.format(detail, params); } catch (MissingResourceException e) { } return new FacesMessage(severity, summary, detail); }}

We can use this MessageFactory in any backing bean, custom converter or custom validator, to create localized error messages:

public class NameValidator implements Validator { /** The identifier of the validator. */ public static final String VALIDATOR_ID = "NameValidator"; /** The identifier of the error message. */ public static final String INVALID_NAME_MESSAGE_ID =

"NameValidator.INVALID_NAME"; public void validate( FacesContext context,

UIComponent component, Object value ) { if (!value.toString().trim().matches("[A-Z][a-z]*")) { FacesMessage message = MessageFactory.getMessage( FacesMessage.SEVERITY_ERROR, INVALID_NAME_MESSAGE_ID); throw new ValidatorException(message); } }}

51

Page 52: JSFSkript

52

Page 53: JSFSkript

9 Custom TagsAs we’ve already seen, JavaServer Faces has several services that enable you to build web applications quickly. Many standard components are available already, and different IDE vendors provide many additional components. However, the real power of JSF lies in its sophisticated component model and an extensible architecture that allows you to create your own user interface (UI) extensions—components, renderers, validators, and converters. In this chapter we learn how to create our own custom tags.

Before you write a new custom tag or custom component, you should make sure that there is no alternative way you can reach your goal: you can visit the JSF Central page (www.jsfcentral.com), for example, to find out about different JSF products and libraries.

With custom tags for validators or converters we are able to provide custom tag attributes. These attributes are used as custom properties (e.g. a regular expression used by a custom validator to match certain input strings <g:validateRegex pattern="[a-z]{5}"/>).

Custom tags for validators and converters are implemented in the same way. The following sections show all the details needed to implement a custom validator tag.

9.1 The different parts of a Custom TagTo create a custom validator tag, we first have to write a CustomValidator class which implements the Validator and the StateHolder interfaces. The validator Interface requires a public validate() method. By implementing the StateHolder interface, we make sure that the state of the validator (its properties) is saved between different requests.

53

Page 54: JSFSkript

The Validator Tag ClassThe validator tag class has to extend the abstract ValidatorELTag class, which means it has to implement a createValidator() method. Furthermore, the validator tag class needs setter methods for all attributes of the tag. If the custom tag provides any attributes, the tag class should implement a release() method.public class MyValidatorTag extends ValidatorELTag {

public Validator createValidator() throws JspException { . . . // create validator object return validator; }

public void release() { // release state super.release(); // set validator properties to null }

// setter methods for validator properties }

The createValidator MethodTo be able to share the same validator object between different pages, the validator class should be created in the following way.

54

Page 55: JSFSkript

The Tag Library Description FileFor the JSP integration we need a Tag-Library description of the new tag (customValidator.tld): <taglib . . . > <tlib-version>1.0</tlib-version> <short-name> ns-Prefix </short-name> <uri> tag_URI </uri>

<tag> <name> tag_name </name> <tag-class> tag_class_name </tag-class> <body-content> normally empty</body-content> <attribute> <description> . . . </description> <name>attribute name</name> <required>true | false</required> <deferred-value> <type>type of attribute</type> </deferred-value> </attribute> </tag> . . . <taglib>

The Validator Classpublic class MyValidator implements Validator, StateHolder { // Custom Validator ID as defined in faces-config.xml file public static String VALIDATOR_ID = CustomValidatorId;

/** The identifier of the error message. */ public static final String MESSAGE_ID = "name of message"; private boolean transientValue = false; // parameters are not transient // implement Validator interface public void validate( FacesContext facesContext,

UIComponent uiComponent, Object value) { if (value does not match requirements) { // raise a validator exception FacesMessage message = MessageFactory.getMessage( FacesMessage.SEVERITY_ERROR, MESSAGE_ID, msg.params...); throw new ValidatorException(message); } } // implement StateHolder interface public void restoreState(FacesContext fc, Object state) { // read parameters from state } public Object saveState(FacesContext arg0) { // return wrapped parameters in a serializable Object }

// getter and setter methods for all properties and transient value . . . }

55

Page 56: JSFSkript

In the faces-config.xml file we have to register all custom validators by their id and fully qualified class name:

<validator> <validator-id>CustomValidatorId</validator-id> <validator-class>package.CustomValidatorClass</validator-class> </validator>

Configuration Summary

The Directory StructureThe Tag-Library Description files name is arbitrary, as long as it has a *.tld file extension.

56

Page 57: JSFSkript

9.2 The Greater-Than Validator

We implement an example validateGreaterThan tag which compares the values of two components. Here, the value of the age component must be smaller than the value of the mother's age. The validateGreaterThan tag demands a „comparedToId“ attribute to define the (id of the) component, whose value is compared to this component's value:

<mytags:validateGreaterThan compareToId="main:age"/>

The GreaterThanValidator Class

public class GreaterThanValidator implements Validator, StateHolder { /** The identifier of the validator. */ public static final String VALIDATOR_ID = "GreaterThanValidator"; /** The identifier of the error message. */ public static final String GREATER_THAN_MESSAGE_ID =

"GreaterThanValidator.GREATER_THAN_ERROR";

// id of component whose value is compared to this components value private String compareToId;

// compareToId is not transient private boolean transientValue = false;

// implement Validator interface public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) { if (value == null) return; UIComponent compareToComponent = uiComponent.findComponent(compareToId); if (compareToComponent instanceof EditableValueHolder) { Object greaterThanValue = ((EditableValueHolder) compareToComponent) .getValue(); if (greaterThanValue == null) return; if (Integer.parseInt(value.toString()) <= Integer .parseInt(greaterThanValue.toString())) { // raise a validator exception FacesMessage message = MessageFactory.getMessage( FacesMessage.SEVERITY_ERROR, GREATER_THAN_MESSAGE_ID, ((HtmlInputText) compareToComponent).getLabel(), greaterThanValue); throw new ValidatorException(message); } } }

57

Page 58: JSFSkript

// value holder method public void restoreState(FacesContext fc, Object state) { compareToId = (String) state; }

// value holder method public Object saveState(FacesContext arg0) { return compareToId; }

// value holder method public boolean isTransient() { return transientValue; }

// value holder method public void setTransient(boolean value) { transientValue = value; }

// return the compareToId public String getCompareToId() { return compareToId; }

// store the value of compareToId public void setCompareToId(String compareToId) { this.compareToId = compareToId; }}

58

Page 59: JSFSkript

The GreaterThanValidatorTag ClassThe ValidatorTag class must implement the createValidator() method as well as a setter method for its properties (compareToId) and a release method.

public class GreaterThanValidatorTag extends ValidatorELTag { // attribute of the tag private ValueExpression compareToId;

public void setCompareToId(ValueExpression compareToId) { this.compareToId = compareToId; }

public void release() { super.release(); compareToId = null; }

public Validator createValidator() throws JspException { FacesContext context = FacesContext.getCurrentInstance(); GreaterThanValidator validator = (GreaterThanValidator) context .getApplication().createValidator(GreaterThanValidator.VALIDATOR_ID); String cId; if (compareToId.isLiteralText()) cId = compareToId.getExpressionString(); else cId = (String) compareToId.getValue(context.getELContext()); validator.setCompareToId(cId); return validator; }}

The Tag Library Description: taglib.tld

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

59

Page 60: JSFSkript

<taglib version="2.1" 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-jsptaglibrary_2_1.xsd">

<tlib-version>1.0</tlib-version> <short-name>myTags</short-name> <uri>http://helloWorld/taglib</uri> <tag> <name>validateGreaterThan</name> <tag-class>helloWorld.GreaterThanValidatorTag</tag-class> <body-content>empty</body-content> <attribute> <description>Id of component this field is compared to</description> <name>compareToId</name> <required>true</required> <deferred-value> <type>java.lang.String</type> </deferred-value> </attribute> </tag> . . .

</taglib>

The custom tag can then be used in the JSP page like this:<jsp:root version="2.1" xmlns:jsp="http://java.sun.com/JSP/Page"

xmlns:f="http://java.sun.com/jsf/core"xmlns:h="http://java.sun.com/jsf/html"xmlns:g="http://helloWorld/taglib">....

<f:view> <h:form id="main">

....<h:outputText value="#{label.yourAge}" /><h:inputText id="age" label="#{label.yourAge}"

value="#{helloBean.age}" required="true" /><h:message for="age" styleClass="errorMessage" />

<h:outputText value="#{label.mothersAge}" /><h:inputText id="mothersAge" label="#{label.mothersAge}"

value="#{helloBean.mage}"><g:validateGreaterThan compareToId="main:age" />

</h:inputText><h:message for="mothersAge" styleClass="errorMessage" />

.... </jsp:root>

10 JSF Custom ComponentsThe JSF framework offers a lot of standard components out of the box. The JSF component model

60

Page 61: JSFSkript

and the extensible architecture allow to create new components. But the development of custom components requires special expertise.

Writing a custom component is necessary if you want• to support special HTML features (e.g. file upload)• provide additional functionality (e.g. sortable data table)• create new types of components (e.g. tree view)• support functionality for other markup languages (e.g. progress bar)

10.1 The Component Hierarchy

10.2 Component Data CollectionsComponents have three types of data collections:

• attributes which are untyped key/value pairs• value expressions which define bindings to backing bean properties• individual properties with corresponding setter and getter methods

The put() and get() methods of the attributes indirectly access the properties and value expressions if available. If a component defines properties, it must implement the StateHolder interface to save and restore them.

61

StateHolder ValueHolder EditableValueHolder

UIComponentBase

idparentchildrenattributesvalueExpressions

encodeBegin()encodeChildren()encodeEnd()saveState()restoreState()

UIOutput

localValuevalueconverter

UIInput

submittedValuevalidators

decode()updateModel()

Page 62: JSFSkript

10.3 Value Holder

The ValueHolder interfaces support a component's values which are used during request processing:

• a submitted value which is the value entered by a user and decoded from the request• a local value which is the value after successful processing by any registered converter and

validators• a backing bean property to which the local value is transferred through a value expression

The getValue() method either returns the submitted value, the local value or the value of the associated backing bean property

Values during Request Processing

62

InputComponent

BackingBean

APPLY REQUEST VALUES1: decode()1a: setSubmittedValue()

PROCESS VALIDATIONS2: validate()2a: setValue()2b: clearSubmittedValue()

UPDATE MODEL VALUES3: updateModel()3a: getLocalValue()3c: clearValue()

3b: setProperty()

INVOKE APPLICAION4: action()

RENDER RESPONSE5: encodeBegin()5a: getSubmittedValue()5b: getValue()

5c: getProperty()

Page 63: JSFSkript

10.4 Example of a Custom Component

In order to develop a custom UI component, the following steps are needed:1. Implement the component class by subclassing an appropriate base class (e.g. UIInput)2. Register the class in the JSF application configuration file (faces-config.xml)3. Integrate the class with JSP, i.e. implement a custom tag (tag handler class, tag library

descriptor).

In this example a custom input component is developed which allows a user to select a number within a range of numbers. The custom component may be included in a JSP page using a custom tag which specifies the range of numbers and a value binding for the selected number:

<h:form> ... <h:outputText value="#{label.age}" /> <g:selectNumber id="helloAge" min="1" max="50" value="#{helloBean.age}" /> ... <h:commandButton value="#{label.hello}" action="#{helloBean.sayHello}" /> </h:from>

63

UIComponentBase

CustomComponent

JSFConfigu-

ration

registers

UIComponentELTag

CustomComponentTag

TagLibrary

Descriptor

registers

uses

Page 64: JSFSkript

The Component Class: SelectNumber

The component class • subclasses an appropriate base class (e.g. UIInput)• defines its type as a constant (COMPONENT_TYPE)• sets its renderer type (not necessary for simple components)• defines its family which is used to find an appropriate renderer (usually inherited from the

base class)• implements the methods encodeBegin(), encodeChildren() and encodeEnd(), which are

called in order to render a component• implements the decode() method, which interprets browser input and sets the submitted

value of the component

public class SelectNumber extends UIInput { /** The identifier of the component. */ public static final String COMPONENT_TYPE = "SelectNumber"; /** Sets the renderer type. */ public SelectNumber() { setRendererType(null); }

/** Renders the number selection to the response. */ public void encodeBegin(FacesContext context) throws IOException { Integer min = (Integer)getAttributes().get("min"); Integer max = (Integer)getAttributes().get("max"); ResponseWriter writer = context.getResponseWriter(); writer.startElement("select", this); writer.writeAttribute("name", "number", null); for (int val = min; val <= max; val++) { writer.startElement("option", this); writer.writeAttribute("value", val, null); if (getValue().equals(val)) writer.writeAttribute("selected", "true", null); writer.writeText(val, null); writer.endElement("option"); } writer.endElement("select"); }

/** Decodes the selected number from the request. */ public void decode(FacesContext context) {

Map<String, String> params = context.getExternalContext().getRequestParameterMap();

String value = params.get("number"); setSubmittedValue(value); }}

64

Page 65: JSFSkript

The Configuration File: faces-config.xml

In the application configuration file, the component has to be declared by specifying its type and its implementation class

<?xml version='1.0' encoding='UTF-8'?><faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" .... >

<managed-bean><description>The one and only HelloBean.</description><managed-bean-name>helloBean</managed-bean-name><managed-bean-class>helloWorld.HelloBean</managed-bean-class><managed-bean-scope>session</managed-bean-scope>

</managed-bean> . . . <component> <component-type>SelectNumber</component-type> <component-class>helloWorld.SelectNumber</component-class> </component> . . . </faces-config>

The Component Tag Handler Class: SelectNumberTag

The component tag handler class is derived from the UIComponentELTag class. The component tag handler associates the tag with a component and with a renderer (if the component does not render itself).

If the component has attributes or properties which should be exposed as tag attributes, the tag handler class has to define corresponding properties (here min, max, and value). We need setter methods as well as a release method for those properties.

The setProperties() method maps the properties of the tag class to attributes or value expressions of the component

public class SelectNumberTag extends UIComponentELTag { /** The minimum value. */ private String min; /** The maximum value. */ private String max; /** The value binding. */ private ValueExpression value; /** Returns the component type. */ public String getComponentType() { return SelectNumber.COMPONENT_TYPE; }

65

Page 66: JSFSkript

/** Returns the renderer type. */ public String getRendererType() { return null; }

/** Sets the minimum value. */ public void setMin(String min) { this.min = min; } /** Sets the maximum value. */ public void setMax(String max) { this.max = max; } /** Sets the value binding. */ public void setValue(ValueExpression value) { this.value = value; } /** Sets the properties of the component. */ public void setProperties(UIComponent component) { super.setProperties(component); component.getAttributes().put("min", new Integer(min)); component.getAttributes().put("max", new Integer(max)); if (value.isLiteralText()) component.getAttributes().put("value",

value.getExpressionString()); else component.setValueExpression("value", value); } /** Releases the resources. */ public void release() { super.release(); min = max = null; value = null; }}

66

Page 67: JSFSkript

The Tag Library Descriptor: taglib.tld

The tag library descriptor (TLD) groups different JSP tags into a tag library which is identified by a URI. A tag description maps a tag name (here selectNumber) to a tag handler class (here helloWorld.SelectNumberTag) and defines the valid tag attributes (max, min, value and id).

<?xml version="1.0"?><taglib . . . >

<tlib-version>1.1</tlib-version><short-name>myTags</short-name><uri>http://helloWorld/taglib</uri><tag>

<name>selectNumber</name><tag-class>helloWorld.SelectNumberTag</tag-class><body-content>empty</body-content><attribute> <name>id</name> </attribute><attribute> <name>min</name> <required>true</required></attribute><attribute> <name>max</name> <required>true</required></attribute><attribute>

<name>value</name><required>true</required><deferred-value>

<type>java.lang.Integer</type></deferred-value>

</attribute></tag>

</taglib>

The JSP page: index.jsp

In the JSP page, the URI of the tag library has to be defined as XML namespace of the tag element.

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:g="http://helloWorld/taglib"> <jsp:directive.page contentType="text/html"/> <html xmlns="http://www.w3.org/1999/xhtml">

<f:view> <h:form> <g:selectNumber min="1" max="10" value="#{helloBean.number}"/> <h:commandButton value="Submit"/> </h:form> </f:view> </html></jsp:root>

67

Page 68: JSFSkript

Packaging the Component

A custom UI component can be packaged together with the application's web archive or in a separate JAR file which can then be included in an application's web archive

Summary of Select Number Example

68

index.jsp.../WEB-INF web.xml faces-config.xml/WEB-INF/classes .../WEB-INF/lib jsf-component.jar

jsf-application.war

/META-INF faces-config.xml taglib.tld/jsf/examples SelectNumber.class SelectNumberTag.class

jsf-component.jar