Sie sind auf Seite 1von 279

Essential JSF,

Facelets & JBoss


Seam
By
Kent Ka Iok Tong
Copyright © 2008
TipTec Development

Publisher: TipTec Development


Author's email: freemant2000@yahoo.com
Book website: http://www.agileskills2.org
Notice: All rights reserved. No part of this publication may be
reproduced, stored in a retrieval system or transmitted, in
any form or by any means, electronic, mechanical,
photocopying, recording, or otherwise, without the prior
written permission of the publisher.
ISBN: 978-99937-929-?-?
Edition: First edition April 2008
Essential JSF, Facelets & JBoss Seam 3

Foreword

How to learn JSF, Facelets and JBoss Seam easily?


If you're required to develop an application in JSF but are just learning it (JSF),
this can be a daunting task. If you need to learn Facelets and JBoss Seam at
the same time, it will quickly become unmanageable. With this book, you will
have much better chances of hopping over the hurdles. How?
• It has a tutorial style that walks you through in a step-by-step manner.
• It is concise. There is no lengthy, abstract description.
• Many diagrams are used to show the flow of processing and high level
concepts so that you get a whole picture of what's happening.
• Free sample chapters are available on http://www.agileskills2.org. You can
judge it yourself.

Products covered in the book


This book covers JSF 1.2 (reference implementation), Facelets 1.2, JBoss
Seam 2.0, JBoss RichFaces 3.1, JBoss IDE Tools 2.0, Eclipse Europa for Java
EE.

Target audience and prerequisites


This book is suitable for those learning how to develop web-based applications
with JSF, along with Facelets and JBoss Seam.
In order to understand what's in the book, you need to know Java and HTML.
However, you do NOT need to know servlet, JSP or Tomcat.

Acknowledgments
I'd like to thank:
• Helena Lei for proofreading this book.
• Eugenia Chan Peng U for doing book cover and layout design.
4 Essential JSF, Facelets & JBoss Seam

Table of Contents
Foreword.........................................................................................3
How to learn JSF, Facelets and JBoss Seam easily?...............3
Products covered in the book.....................................................3
Target audience and prerequisites.............................................3
Acknowledgments.......................................................................3
Chapter 1 Getting Started with JSF................................................7
What's in this chapter?...............................................................8
Developing a Hello World application with JSF.........................8
Installing Eclipse.........................................................................8
Installing Tomcat........................................................................9
Installing the JSF reference implementation............................10
Creating a Hello Word application............................................11
Generating dynamic content....................................................24
Retrieving data from Java code................................................31
How the JSP file generates the HTML code............................38
Debugging a JSF application...................................................40
Summary..................................................................................41
Chapter 2 Using Forms.................................................................43
What's in this chapter?.............................................................44
Developing a stock quote application.......................................44
Getting the stock quote symbol................................................44
Displaying the stock value........................................................52
Defining the page navigation....................................................54
Using a combo box...................................................................62
Inputting a date.........................................................................65
Handling conversion errors.......................................................70
Marking input as required.........................................................76
Using the calendar component.................................................77
Hooking up the managed beans..............................................80
Summary..................................................................................83
Chapter 3 Validating Input............................................................85
What's in this chapter?.............................................................86
Postage calculator....................................................................86
What if the input is invalid?......................................................93
Null input and validators...........................................................97
Validating the patron code........................................................99
Essential JSF, Facelets & JBoss Seam 5

Displaying the error messages in red.....................................102


Displaying the error message along with the field.................103
Validating a combination of multiple input values..................108
Summary................................................................................111
Chapter 4 Creating an e-Shop....................................................113
What's in this chapter?...........................................................114
Creating an e-shop.................................................................114
Listing the products................................................................115
Showing the product details...................................................124
Implementing a shopping cart................................................130
How Tomcat and the browser maintain the session..............139
The checkout function............................................................142
Implementing the login function.............................................144
Implementing the checkout function......................................151
Implementing logout...............................................................158
Summary................................................................................160
Chapter 5 Building Interactive Pages with AJAX......................163
What's in this chapter?...........................................................164
A sample AJAX application....................................................164
Refreshing the question only..................................................166
Refreshing the answer itself...................................................169
Giving rating to a question......................................................169
Using a modal panel to get the rating....................................172
Setting the look and feel with skins........................................176
Summary................................................................................177
Chapter 6 Using Facelets..........................................................179
What's in this chapter?...........................................................180
Setting up IDE support for Facelets.......................................180
Creating a Facelets project....................................................181
Creating your own tag............................................................188
Hiding JSF tags into HTML tags.............................................191
Forbidding direct access to xhtml files...................................193
Summary................................................................................195
Chapter 7 Providing a Common Layout with Facelets..............197
What's in this chapter?...........................................................198
Providing a common layout....................................................198
Having two abstract parts.......................................................202
Having page specific navigation cases..................................204
Summary................................................................................206
6 Essential JSF, Facelets & JBoss Seam

Chapter 8 Using JBoss Seam....................................................207


What's in this chapter?...........................................................208
Recreating the e-shop with Seam..........................................208
Creating the catalog page......................................................218
Displaying product details.......................................................223
Adding a product to the shopping cart...................................230
Confirming the checkout.........................................................237
Logging in...............................................................................239
Protecting the confirm page...................................................244
Implementing logout...............................................................245
Redirect vs render..................................................................247
Summary................................................................................249
Chapter 9 Supporting Other Languages....................................251
What's in this chapter.............................................................252
A sample application..............................................................252
Supporting Chinese................................................................253
Internationalize the date display.............................................259
Letting the user change the locale.........................................260
Localizing the full stop............................................................263
Displaying a logo....................................................................266
Making the locale change persistent......................................269
Localizing validation messages..............................................271
Summary................................................................................272
References..................................................................................273
Alphabetical Index......................................................................274
7

Chapter 1
Chapter 1 Getting Started with JSF
8 Chapter 1 Getting Started with JSF

What's in this chapter?


In this chapter you'll learn how to set up a development environment and
develop a Hello World application with JSF.

Developing a Hello World application with JSF


Suppose that you'd like to develop an application like this:

Installing Eclipse
You need to make sure you have Eclipse v3.3 (or later) installed and it is the
bundle for Java EE (the bundle for Java SE is NOT enough). If not, go to
http://www.eclipse.org to download the Eclipse IDE for Java EE Developers
(e.g., eclipse-jee-europa-fall-win32.zip). Unzip it into c:\eclipse. Then, create a
shortcut to run "c:\eclipse\eclipse -data c:\workspace". This way, it will store
your projects under the c:\workspace folder. To see if it's working, run it and
make sure you can switch to the Java EE perspective:

BUG ALERT: If you're using Eclipse 3.3.1, there is a serious bug in it: When
visually editing JSF pages Eclipse will frequently crash with an
OutOfMemoryError. To fix it, modify c:\eclipse\eclipse.ini:
Chapter 1 Getting Started with JSF 9

-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
Delete them
256m
-vmargs This line must be put
-Xms40m after -vmargs
-Xmx256m
-XX:MaxPermSize=256m

Installing Tomcat
Next, you need to install Tomcat. Go to http://tomcat.apache.org to download a
binary package of Tomcat 6.x (or later). Download the zip version instead of the
Windows exe version. Suppose that it is apache-tomcat-6.0.13.zip. Unzip it into
a folder, say c:\tomcat. Note that Tomcat 6.x works with JDK 5 or above.
Before you can run it, make sure the environment variable JAVA_HOME is
defined to point to your JDK folder (e.g., C:\Program Files\Java\jdk1.5.0_02):

If you don't have it, define it now. Now, open a command prompt, change to
c:\tomcat\bin and then run startup.bat. If it is working, you should see:
10 Chapter 1 Getting Started with JSF

Open a browser and go to http://localhost:8080 and you should see:

Let's shut it down by changing to c:\tomcat\bin and running shutdown.bat.

Installing the JSF reference implementation


JSF stands for JavaServer Faces. It is just an API (some Java interfaces). To
use it, you need an implementation (Java classes implementing those
interfaces). There are mainly two implementations: the reference
implementation from SUN and MyFaces from Apache. In this book, you'll use
the former.
Chapter 1 Getting Started with JSF 11

So, go to https://javaserverfaces.dev.java.net to download a binary package of


the JSF implementation which is called Mojarra. Suppose that it is
jsf-1_2_07.zip. Unzip it into a folder, say c:\jsf.
The JSF implementation in turn needs another library called JSTL. So, go to
https://maven-repository.dev.java.net/repository/jstl/jars, download the
jstl-1.2.jar file and put it into say c:\jstl.

Creating a Hello Word application


Now, in Eclipse, choose "Windows | Preferences | Web and XML | JavaServer
Faces Tools | Libraries":

Click "New" and enter the information as below:


12 Chapter 1 Getting Started with JSF

The name doesn't really matter

It supports v1.2 of the JSF specification

Click Add and browse to c:\jsf\lib


to add these two jar files

Check this

Then while you're still in the Preferences window, choose "Server | Installed
Runtimes":

Click "Add" and choose Apache Tomcat v6.0:


Chapter 1 Getting Started with JSF 13

Click "Next". Specify c:\tomcat as the Tomcat installation directory:

Click "Finish". Next, right click in the Package Explorer and choose "New |
14 Chapter 1 Getting Started with JSF

Dynamic Web Project":

Enter the information as below:


Chapter 1 Getting Started with JSF 15

The name doesn't really matter

Run this application in Tomcat

Eclipse will set some options such as


compiling using Java 5

Keep clicking "Next" until you see the dialog below. Then choose your
implementation:
16 Chapter 1 Getting Started with JSF

Choose it

Then, if you're on the Internet, Eclipse may try to download some XML files and
may ask you to accept the licenses. Say yes. Finally, you should see the project
structure:
Chapter 1 Getting Started with JSF 17

To make the jstl-1.2.jar file available to it, copy it into the WebContent/WEB-
INF/lib folder. Right click the project and choose "Refresh" so that Eclipse see
the file.
Next, you'll create the web page. To do that, right click the WebContent folder
and choose "New | JSP":

Enter "hello" as the file name:


18 Chapter 1 Getting Started with JSF

Click "Finish". This will create a hello.jsp file in the WebContent folder with
some initial content:

To edit it visually, right click the hello.jsp file and choose "Open With | Web
Page Editor":
Chapter 1 Getting Started with JSF 19

Then you'll see:

This is the visual editing area

This is the source code

Next, in the visual editing area, type "Hello world". Note that the source code will
change automatically:
20 Chapter 1 Getting Started with JSF

Alternatively, you could edit the source code and the visual display will change
automatically.
To run your application, you need to create a so-called "Tomcat instance". To
do that, right click the "Servers" tab at the bottom and choose "New | Server":

Choose the Tomcat v6.0 runtime:


Chapter 1 Getting Started with JSF 21

Click "Next", then you'll see:


22 Chapter 1 Getting Started with JSF

Choose your Hello project and click "Add". This way it will be added to that
Tomcat instance:
Chapter 1 Getting Started with JSF 23

Click "Finish". Then you should see this Tomcat instance in the "Servers"
window:
To run it, just click here.

The instance is not running

Click the icon as shown above to run it (make sure you have indeed stopped the
Tomcat you started from the command prompt!). Then you will see some
messages in the Console window:
24 Chapter 1 Getting Started with JSF

If you see this line, it means


your application was started
successfully
This is your project name

To run your hello.jsp page, open a browser and go to this URL:


This is called the context path. By
default it is determined by the
project name.

http://localhost:8080/Hello/faces/hello.jsp

/hello.jsp is a relative path from WebContent.


It represents the JSF engine The JSF engine will use it to retrieve the
in your application hello.jsp file. In JSF such a relative path is
called the view id.

WebContent
hello.jsp
...

Then you should see it working:

Generating dynamic content


Displaying static text is not particularly interesting. Next you'll output some
dynamic text. Open hello.jsp. At the right hand side of the visual editing area,
there is a collapsed palette:
Chapter 1 Getting Started with JSF 25

A palette is
here

Move the mouse over there and the palette will appear:

Expand the "JSF Core" folder. It contains quite some items. Each item is called
a tag and the folder is called a tag library (or tag lib):
26 Chapter 1 Getting Started with JSF

It is a tag lib

Each item is
a tag

Each tag lib contains some tags in it and has a unique URL as its identifier, just
like a Java package contains some classes in it and has a unique package
name:
http://java.sun.com/jsf/core com.foo

class Product {
<view> ...
<param> }
<selectItem> class Order {
...
}

Now, drag the <view> tag and drop it onto the page (either before the "Hello
World" text or after it. It doesn't matter):
Chapter 1 Getting Started with JSF 27

A <view> element will have been created. Why it is <f:view> instead of <view>?
"f" here is used as a short hand (the "prefix") for the URL of the JSF Core tab
lib, so <f:view> means the <view> tag in the JSF Core tag lib:
This is like an import statement in
Java. It makes the tags in a tag lib
available to this JSP file. The URL for the JSF Core tag lib

In the rest of the JSP file, you can


use "f" as a short hand for the
long URL.

Note that we said a <f:view> element had been created instead of a <f:view>
tag. Here is the difference between a tag and an element:
28 Chapter 1 Getting Started with JSF

This is a tag. Or more specifically, it


is a start tag.

<f:view>
... The whole thing is called an
</f:view> element

This is another tag (the end tag).

This <f:view> element is required whenever you need to use any JSF tag. You
must put JSF tags inside it. Delete the existing "Hello world!" text. In the body of
the <f:view> element, enter "Hello !":

Then on the palette, expand the "JSF HTML" tag lib. It contains JSF tags for
generating HTML code. JSF can generate different markups such as HTML for
normal web browsers or special markups for mobile phones. The JSF HTML
tag lib contains tags that deal with HTML markup while those JSF tags having
nothing to do with any specific markup are put into the JSF Core tag lib.
Anyway, drag the "Output Text" tag and drop it before the exclamation mark:
Chapter 1 Getting Started with JSF 29

Note the "h" prefix. It is the short hand for the URL for the JSF HTML tag lib:
The URL for the JSF HTML tag lib

The <h:outputText> element will output some text. For example, if you like it to
output the string "Paul", in the source code editor, add an attribute named
"value". To do that, you use the auto-complete function in Eclipse:
30 Chapter 1 Getting Started with JSF

Choose "value" from the list:

Then input "Paul":

Note that the visual display is also updated automatically. Now, save the file, go
to the browser and reload the page. You should see:
Chapter 1 Getting Started with JSF 31

Retrieving data from Java code


Next, you'll let the Output Text tag retrieve the string from Java code. First,
create a Java class GreetingService in the hello package:

Input the content as below:


package hello;

public class GreetingService {


public String getSubject() {
return "John";
}
}
You'd like the JSF engine to create an instance of your GreetingService and
then the Output Text tag retrieve the value of the "subject" property of that
instance as the output:
32 Chapter 1 Getting Started with JSF

JSF Engine GreetingService


1: Create

Output Text
2: Tell me the value
of your "subject"
property
3: Output it

To do that, the JSF engine maintains two tables (see the diagram below). Let's
call them "instance table" and "definition table" respectively. Initially the instance
table is empty, meaning that no object has been created yet. The definition table
stores the class name for each object. If you have configured the Output Text
tag to access the "subject" property of the "foo" object, then when it needs to
find the text, it will ask the JSF engine for the "foo" object. The JSF engine will
look up the instance table but can't such an entry for "foo". Then it will look up
the definition table and find the class name (hello.GreetingService) for "foo".
Then it creates a new GreetingService object and add an entry point to the
instance table. Finally it tells the Output Text to use this object:
Instance table
Object name Object instance
... ...
2: Look it up in the table. Not
found.
... ...
... ...

foo
5: Add a new
entry
Definition table
3: Look up the Object name Object class
class name
foo hello.GreetingService
bar ...
... ...
JSF Engine

1: Give me 6: Here is
the object your "foo"
named "foo" object
GreetingService
4: Create it
Output Text

Actually there are many such object tables in a single JSF application: For
example, as shown in the diagram below, suppose that there are two browsers
(client 1 and client 2) accessing the application. Assume that client 1 has sent
totally two requests (request 1 and request 2) to the application and client 2 has
sent one (request 3). Then there will have been an object table for each HTTP
request. In addition, Tomcat will allocate a memory area for each client. Such
an area is called a session. In each session, there is another object table,
Chapter 1 Getting Started with JSF 33

meaning that there is an object table for each client. Finally, there is an object
table in the whole application. Which tables will be used by Output Text?
Suppose that it is serving request 3, it will ask the JSF engine to find the "foo"
object. The JSF engine will look up the object table associated with request 3
first. If it's there fine and it will be returned. Otherwise, it will look up the table for
client 2 (because request 3 came from client 2). If it is still not found, it will look
up the table for the whole application:

Output Text

1: Give me "foo"
1: Look up "foo"
here 3: If still not found,
JSF engine
try here.
Table for request 1 2: If not found,
try here.
Object name Object instance
Table for client 1
... ...
... ... Object name Object instance
... ... ... ...
... ...
... ...
Request 1

Client 1 Table for client 2


Object name Object instance
Request 2 ... ...
Table for request 2
... ...
... ...
Object name Object instance
... ...
... ...
... ...

Table for request 3


Object name Object instance Table for the whole application
... ... Object name Object instance
... ... ... ...
... ... ... ...
Request 3
... ...

Client 2

As mentioned before, if the object is still not found, the JSF engine will check
the definition table to find the class name and then create the object. But which
table should the new object be put into? It is specified in the definition:
34 Chapter 1 Getting Started with JSF

It should go into the table


for the current request
(request 3 in this case)

Definition table
Object name Object class Scope
foo hello.GreetingService request
bar ... session It should go into the table
... ... application in the session for the
client (client 2 in this
case)
It should go into the
table for the whole
application

If the object is put into the request, after the request is handled, the table and
that object will be discarded. If the object is put into the session, the table and
that object will be discarded when there is a say 30 minutes of inactivity.
Such named objects are officially called "managed beans" in JSF. They're
called managed because it is JSF that manages (creates and discards) them,
not you. They're called beans because they are Java beans (have a no-
argument constructor and provide properties):
public class GreetingService { No constructor defined. So the Java
compiler will give one automatically.
public String getSubject() { Such a constructor will have no
return "Paul"; argument:
} public class GreetingService {
} public GreetingService() {
Getter for property }
"subject" public String getSubject() {
return "Paul";
}
}

Now, let's implement this idea. To create the bean definition table, double click
the faces-config.xml file:

Choose the "Managed Bean" tab at the bottom of the window:


Chapter 1 Getting Started with JSF 35

Choose this tab

Choose "request" and click "Add". Browse to choose the GreetingService class:

Click "Next". Enter "foo" as the bean name:


36 Chapter 1 Getting Started with JSF

Go ahead to finish it. Save the faces-config.xml file. This file is just an XML file.
If you'd like to see the XML code, you can choose the "Source" tab at the
bottom of the window:

The bean is
defined here Choose this tab

The next step is tell the Output Text tag to access the "foo" bean. To do that,
edit hello.jsp. Enter "#{}" as the value as shown below. This #{} syntax tells the
Output Text tag that it is not a static string. Instead, there will be a "EL
expression" in it (EL stands for Expression Language):
Chapter 1 Getting Started with JSF 37

While the cursor is inside #{}, use auto-completion and choose the "foo" bean:

Then enter a dot and use auto-completion to choose the "subject" property:
38 Chapter 1 Getting Started with JSF

The result will be:

To get the real value, Output Text will evaluate the EL expression "foo.subject".
It means it will look up the "foo" bean and call getSubject() on it.
Save the file. Now, go to the browser and reload the page. It should work.
Otherwise, run the Tomcat instance again.

How the JSP file generates the HTML code


How the JSP file generates the HTML code? When the JSF engine needs to
display the hello.jsp file, it will ask the JSP (NOT JSF) engine in Tomcat to do
that. First, the <%@...> lines are read by the JSP engine and are not output at
all (see the diagram below). Then the normal HTML code is output as is. The
JSP engine will only handle JSP tags such as <f:view> and <h:outputText>
(yes, JSF tags are JSP tags). When it sees the <f:view> tag, it will ask the tag to
generate HTML code. However, this tag, like all JSF tags, will not generate any
HTML code. Instead, it will create a JSF component. For the case of <f:view>, it
will create a so-called "View root" component which represents the root of the
component tree. Then through some magical collaboration between the
<f:view> tag and the <h:outputText> tag, they create an artificial UI Output
component and put the text "Hello " into it so that it will output that text later.
Note that if this "Hello " text appeared outside of <f:view>, it would have been
output immediately and not turned into a component. The <h:outputText> will
Chapter 1 Getting Started with JSF 39

create a UI Output component. Finally the exclamation mark is put into another
artificial UI Output component. At this point the JSF component tree is built but
the tree hasn't generated any HTML code. Next, the JSP engine sees normal
HTML again and outputs it:
These are read by the
JSP engine and are not
output at all
Normal HTML code is
output as is
<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:view>Hello <h:outputText value="#{foo.subject}"></h:outputText>!</f:view>
</body>
</html>
1: Create a "view 2: Plain HTML 3: Create a UI 4: Plain HTML
Normal HTML code is
root" JSF code is put into output code again
output as is
component an artificial UI JSF component
Output JSF
component
View root

UI Output value: "Hello "

UI Output

value: "!"
UI Output

At this point, the HTML output (generated by the JSP engine) is shown below
and the JSP engine has finished its job. Next, the JSF engine will tell the view
root to generate HTML output (tell it to "encode" itself in JSF terms). The view
root will in turn tell its child components to generate HTML output. Their output
will go into the marker left by the <f:view>:
40 Chapter 1 Getting Started with JSF

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"


"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
[JSF COMPONENT TREE WAS HERE]
</body>
</html>
Conceptually
<f:view> left a
marker here

1: Render "Hello "


JSF engine View root

2: Render
UI Output

3: Render "Paul"
UI Output

4: Render UI Output "!"

Debugging a JSF application


To debug your application in Eclipse, you can set a breakpoint in your Java
code such as:

Then click the Debug icon in the Server window:


Chapter 1 Getting Started with JSF 41

This will start Tomcat in debug


mode. If it is already running, it
will be restarted.

Now go to the browser to load the page again. Eclipse will stop at the
breakpoint:

Then you can step through the program, check the variables and whatever. To
stop the debug session, just restart Tomcat.

Summary
In a JSF application, a page is defined by a JSP file and is identified by its view
id, which is the relative path to it from the web content folder.
Each JSP tag belongs to a certain tag lib. A tag lib is identified by a URL. To use
a tag in a JSP file, you need to introduce a short hand (prefix) for the URL and
then use the prefix to qualify the tag name.
To define a JSF component tree in a JSP file, you need to have a <f:view> and
put various JSF tags in it. Each JSF tag is also a JSP tag. When they're
executed by the JSP engine, they will create their respective JSF components.
42 Chapter 1 Getting Started with JSF

The root of the component tree is always the view root. To generate HTML code
from the component tree, the JSF engine will ask the view root to do that, which
in turn will ask its child components to do the same. The process of generating
markup in JSF is called encoding.
The JSF Core tag lib contains JSF tags that have nothing to do with any specific
markup. The JSF HTML tag lib contains JSF tags that knows about the HTML
markup. Usually the former uses a prefix of "f" (standing for faces) while the
latter uses a prefix of "h" (standing for HTML).
The <h:outputText> tag will create an outputText component. That component
will output the value its "value" attribute. That value can be static string or an EL
expression in #{}.
To find the value of a variable appearing in an EL expression, the JSF engine
will try to find a managed bean with that name. It will try to find it in the beans
associated with the request, in the session of the client and in the whole
application, in that order. If it is not found, it will look up the class name and
scope in the WebContent/WEB-INF/faces-config.xml file and create it, before
putting it into the right location.
For a class to be used as a managed bean class, it needs to be a Java bean,
i.e., it has a no-argument constructor and provides getters and/or setters for
certain properties.
43

Chapter 2
Chapter 2 Using Forms
44 Chapter 2 Using Forms

What's in this chapter?


In this chapter you'll learn how to use forms to get input from the user.

Developing a stock quote application


Suppose that you'd like to develop an application like this:

That is, the user can enter the stock id and click OK, then the stock value will be
displayed.

Getting the stock quote symbol


Let's do it. Create a new Dynamic Web project (with JSF enabled) named
StockQuote. Copy the jstl jar file into WEB-INF/lib. Then create a
getquotesymbol.jsp file in WebContent. Locate the <form> tag in the JSF HTML
tag lib. Drag and drop it onto the page:
Chapter 2 Using Forms 45

When you dropped the <h:form> tag, it noted that you didn't
have a <f:view> element, so it created one for you.

The <h:form> tag will create a


JSF Form component

For the text field, use the <Text Input> tag in the JSF HTML tag lib. Put it inside
the form:

For the OK button, use the <commandButton> tag in the JSF HTML tag lib:
46 Chapter 2 Using Forms

It is displaying "Submit Query". To change it to "OK", set its "value" attribute:

Here is the component tree that will be built:


Chapter 2 Using Forms 47

UI View
Root

UI Form

UI Input

UI
Command

To retrieve the symbol entered by the user, you can link the UI Input component
to a property of a Java object:
UI View
Root

UI Form
class QuoteQuery {
String sym;
}
UI Input

UI
Command

To do that, create such a QuoteQuery class in the stockquote package:


package stockquote;

public class QuoteQuery {


private String sym;

public String getSym() {


return sym;
}
public void setSym(String sym) {
this.sym = sym;
}
}
Then create a managed bean definition for it by modifying faces-config.xml:
48 Chapter 2 Using Forms

Save the file. Then in getquotesymbol.jsp, set the "value" attribute of the
<inputText> tag:

This way, when the form is submitted, the symbol entered by the user will be
stored into the "sym" property of this "quoteQuery" managed bean:
Chapter 2 Using Forms 49

class QuoteQuery {
String sym;
}

It will be set to "MSFT"

In fact, this linkage is bi-directional: When the UI Input component encodes


itself, it will get the value of the "sym" property and display it in the HTML input
field:

class QuoteQuery {
String sym = "IBM";
}

It will display "IBM"

Let's look at the whole render and submit process in details. First, the JSP
engine creates the component tree (see the diagram below). The JSF engine
gets access to the tree and ask it to encode. It will ask its child components to
encode and so on. For the UI Input component, it will try to read the "sym"
property of the "quoteQuery" bean. As the bean doesn't yet exist, the JSF
engine creates the bean. The UI Input component gets the value ("IBM") and
outputs it in the HTML <input> element. In order to be able to handle the form
submission, the JSF engine performs some extra action: It generates a unique
request id, saves the component tree into the session indexed by the request id
and includes this request id into the form as a hidden field. Finally the
"quoteQuery" bean is destroyed along with the request. This is called the
Render Response phase:
50 Chapter 2 Using Forms

JSP
engine

1: Create the tree


6: Output the value ("IBM")
UI View
Root
9: Destroy
quoteQuery
UI Form

5: Read its "sym"


property
UI Input
...
<form>
3: I need to
<input
access a bean type="input"
... named value="IBM">
"quoteQuery" <input
2: Encode type="hidden"
JSF
value="123">
engine </form>
4: Create

8: Output the request id into a hidden field

Session for this client

Request id: 123


7: Generate a
unique request id ...
and save the tree
under it ...

...

When the form is submitted (see the diagram below), the JSF engine will get
the request id from the hidden field and use to look up the component tree and
load it. This is called the Restore View phase:
Chapter 2 Using Forms 51

UI View
Root

UI Form

UI Input
MSFT
123
JSF ...
1: Form submission engine
request arrives 2: Use 123 to retrieve the tree

Session for this client

Request id: 123


...

...

...

Then the JSF engine asks the UI View Root to extract the values from the
request (strings). It will in turn ask each child component to do that and store
the value locally into itself. The purpose is that if later a value is found to be
invalid (e.g., "abc" for an int), that invalid value will still be there and can be
redisplayed to the user. The act of extracting the value and storing it locally is
called decoding. The phase of decoding is called the Apply Request Values
phase:

UI View 2: Extract the values


Root from the request

UI Form 3: Store it locally

UI Input raw value: MSFT


1: Extract the values ...
MSFT from the request
123
JSF ...
engine

Next, the JSF engine will ask the UI View Root to set the locally stored values
into the managed beans. For the UI Input component, it will set the locally
stored value into the "sym" property of the "quoteQuery" bean. As the bean
doesn't yet exist, the JSF engine creates the bean. Therefore, this bean is NOT
the same bean used in rendering. The phase of setting the locally stored values
into the managed beans is called the Update Model Values phase:
52 Chapter 2 Using Forms

UI View
Root

2: I need to access
UI Form a bean named
"quoteQuery"

UI Input raw value: MSFT


...

JSF 1: Set the values ... 4: Set the locally


engine into the beans stored value into
the bean

quoteQuery
3: Create it. It is NOT the
same bean used in
rendering!

Displaying the stock value


The next step is to display the stock value in a result page. Let's call it
quoteresult.jsp. Create it in WebContent. By the way, if you'd like to open all
JSP files using the Web Page Editor, you can set it as the default. To do that,
choose "Window | Preferences", choose "General | Editors | File Associations"
on the left hand side, choose *.jsp in the upper half of the window, choose the
Web Page Editor in the lower half and set it as the default:
Chapter 2 Using Forms 53

1: Choose it

2: Choose it

3: Choose it

4: Click here

Modify quoteresult.jsp like:

You may notice a yellow line here: It is warning


Warning that it can't find a "stockValue" property in the
bean. This is correct. It doesn't have such a
property now. You will create a getStockValue()
method in the Java class in the next step.

Create the getStockValue() method:


54 Chapter 2 Using Forms

public class QuoteQuery {


private String sym;

public String getSym() {


return sym;
}
public void setSym(String sym) {
this.sym = sym;
}
public int getStockValue() {
return sym.hashCode() % 100;
}
}
Normally you should find out the stock value for the
given symbol. Here, you just get a fake value: the
hash code of the symbol modulo 100.

Now the result page is ready. The only missing question is how to display it after
the form is submitted?

Defining the page navigation


To do that, you need to tell the JSF engine something like this:

The current view id

/getquotesymbol.jsp
Each branch is called a The next view id
navigation case
/quoteresult.jsp
If outcome is "ok"

another
If outcome is "..." view id

The whole thing is called a


navigation rule

Usually you'll tell the JSF engine the outcome in a new phase called Invoke
Application phase (see the diagram below), which occurs after the Update
Domain Values phase. Then it will use the current view id and the outcome to
look up the navigation rules to determine the next view id, which will be
rendered in the Render Response phase:
Chapter 2 Using Forms 55

2: Use the current


view id and outcome
to find the next view
id
Navigation
rules

JSF engine

1: Set the outcome

Restore Apply Update Invoke


view request values domain values application

Render 3: Render the next


response view in the Render
Response phase

Now, let's define the navigation rules. To do that, open faces-config.xml and
choose the "Navigation Rule" tab at the bottom of the window:

Choose this tab

On the palette on the right edge, choose the "Page" tool:


56 Chapter 2 Using Forms

Then click on the white area in the window. It will pop up a window to let you
choose a JSP file (see below). Choose getquotesymbol.jsp:

Then the page will appear in the window:


Chapter 2 Using Forms 57

Now do the same thing for the quoteresult.jsp page:

To link them up, choose the "Link" tool on the palette:

Click on the getquotesymbol page and then on the quoteresult page. A link will
58 Chapter 2 Using Forms

be created:

To specify the outcome for the link, choose the "Select" tool on the palette (or
just press Escape):

Then choose the link and choose the "Properties" tab. Enter the outcome there:
Chapter 2 Using Forms 59

Enter the outcome

If you're curious, you can see its XML source in the "Source" tab:
<faces-config ...>
<managed-bean>
<managed-bean-name>quoteQuery</managed-bean-name>
<managed-bean-class>stockquote.QuoteQuery</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<navigation-rule>
<display-name>getquotesymbol</display-name>
<from-view-id>/getquotesymbol.jsp</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/quoteresult.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
Save the file. Finally, modify getquotesymbol.jsp to specify the outcome:
...
<f:view>
<h:form>
<h:inputText value="#{quoteQuery.sym}"></h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>
... This is the outcome

If the button is clicked, it will note that in the Apply Request Values phase (see
the diagram below) and will register a listener to be invoked in the Invoke
Application phase. That listener will set the outcome when it is executed:
60 Chapter 2 Using Forms

Request

...
JSF engine
1: Was I
clicked?
2: Schedule a 4: Set the outcome
listener to be
executed
UI Command Listener

3: Execute

Restore Apply Update Invoke


view request values domain values application

Render
response

Why it doesn't simply execute the listener in the Apply Request Values phase?
It is because it would like to update the beans (Update Domain Values) first
before performing other any actions.
Now, you're about to run it. To do that, you need to add this project to the
Tomcat instance. So, choose the "Servers" tab and double click on the Tomcat
instance, you'll see its settings:

Double click on it

Choose the "Modules" tab at the bottom of the window. It will display all the web
applications that it is hosting. Currently it should contain only the Hello project:
Chapter 2 Using Forms 61

Click "Add Web Module" to add the StockQuote project to it:

The path determines


this part of the URL

http://localhost:8080/StockQuote/faces/getquotesymbol.jsp

Save the file. Now, start the Tomcat instance and go to http://localhost:
8080/StockQuote/faces/getquotesymbol.jsp in a browser. It should work:
62 Chapter 2 Using Forms

Using a combo box


Suppose that you'd like to change the application so that the user will choose
from a list of stock symbols instead of typing in one:

To do that, delete the <h:inputText> in getquotesymbol.jsp and put a


<h:selectOneMenu> there:
Chapter 2 Using Forms 63

This will create a UI Select One component which will allow a single item to be
selected only. It should still link to the "sym" property of the bean. So, set its
"value" attribute just like before:

To specify the available items in the combo box, choose the <selectItems> tag
in the JSF Core tag lib. Note that it is in the JSF Core tag lib instead of the JSF
HTML tag lib because selection items in JSF are generic and have nothing to
do with HTML markup.
You'd like drop the <selectItems> element into the body of the
<selectOneMenu> element, but in the visual editor you can only drop it before
the <selectOneMenu> element or after it but not inside it. Fortunately, you can
do that in the text editing area. Just make sure that you click the <selectItems>
tag and release the mouse right away, then click inside the <selectOneMenu>
element:
64 Chapter 2 Using Forms

Click here to drop

The <selectItems> element will retrieve the items from a managed bean (again,
using its "value" attribute). To do that, create a StockService class in the same
package:
...
import java.util.List;
import javax.faces.model.SelectItem;
This class is provided by JSF. It represents
public class StockService { an item for the user's selection.
private List<SelectItem> symbols;

public StockService() { This string will be displayed to


symbols = new ArrayList<SelectItem>(); the user
symbols.add(new SelectItem("MSFT"));
symbols.add(new SelectItem("IBM"));
symbols.add(new SelectItem("RHAT"));
}
public List<SelectItem> getStockSymbols() {
return symbols;
}
} It can return a List or an array

Make a managed bean from it. As it is a global thing, put it into the application
scope:
Chapter 2 Using Forms 65

Set the "value" attribute of the <selectItems> tag:


<f:view>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}"/>
</h:selectOneMenu>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>
Now run the application and it should work. However, you may wonder why you
need to provide it with a List<SelectItem> instead of just a List<String>? The
reason is, for example, instead of displaying short codes like "MSFT" to the
user, you'd like to display a longer description such as "Microsoft". Internally all
your processing will still use "MSFT" though. To do that, modify the code:
class QuoteQuery {
This string will String sym;
be set into the }
bean
public class StockService {
private List<SelectItem> symbols; This string will be
displayed to the
public StockService() { user
symbols = new ArrayList<SelectItem>();
symbols.add(new SelectItem("MSFT", "Microsoft"));
symbols.add(new SelectItem("IBM", "IBM"));
symbols.add(new SelectItem("RHAT", "Red Hat"));
}
public List<SelectItem> getStockSymbols() {
return symbols;
}
}

Inputting a date
Suppose that you'd like to allow the user to query the stock value on a particular
66 Chapter 2 Using Forms

date:

To do that, modify getquotesymbol.jsp:


<f:view>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}"/>
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}"></h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>
Provide the "quoteDate" property in the bean and use it to calculate the stock
value:
public class QuoteQuery {
private String sym;
private Date quoteDate = new Date();

public Date getQuoteDate() {


return quoteDate;
}
public void setQuoteDate(Date quoteDate) {
this.quoteDate = quoteDate;
}
public String getSym() {
return sym;
}
public void setSym(String sym) {
this.sym = sym;
}
public int getStockValue() {
return (sym + quoteDate.toString()).hashCode() % 100;
}
}
The UI Input component knows about a few common types such as
java.lang.Integer and java.lang.Double and can convert between a value of
such types and a string. Unfortunately, it doesn't know java.util.Date. To solve
this problem, you need to tell it to use a Date converter (see the diagram
below). When it needs to encode itself, it will get the value of its "value"
attribute. Here it will get a Date object. Then it will ask the converter to convert
this value into a string. Finally it will output the string into the HTML <input> field:
Chapter 2 Using Forms 67

<input
type="text"
value="6/20/2007" ...>

Date
3: The string is "6/20/2007" converter

Year: 2007
4: Output the Month: 6
string into the Day: 20
<input> field
2: Convert the object (a
Date) into a string for me class QuoteQuery {
...
UI Input Date getQuoteDate() {
1: Call getQuoteDate() to get the ...
value (a Date, but it doesn't need to }
know) void setQuoteDate(Date d) {
...
}
}

When the user submits the form (see the diagram below), the JSF engine will
initiate the Apply Request Values phase. As a result, the UI Input component
will store the raw input string stored locally. Before the JSF engine starts the
Update Domain Values phase, it will initiate a new phase called Process
Validations phase. In this phase, it will ask the UI View Root to convert the raw
input string into the desired data type (any Object) and optionally validate the
converted object. For the UI Input component, it will ask the converter to convert
its locally stored raw string to an object (a Date) and store the result locally.
Finally, the JSF engine will initiate the Update Domain Values phase to update
the beans:
Request
quote date: "7/28/2007"
...

UI View 2: Apply
Root request class QuoteQuery {
Date
values ...
converter
UI void setQuoteDate() {
Form ...
}
Raw: "7/28/2007"
UI Text 3: Convert Year: 2007 }
Converted:
the string Month: 7
into an Day: 28
Object 4: Update
1: Restore domain
view values

Restore Apply Process Update


view request values validations domain values

Therefore, all you need to do is to setup a Date converter. To do that, drop a


<convertDateTime> tag from the JSF Core tag lib into the <inputText> element
(see the diagram below). When this <convertDateTime> tag is executed by the
68 Chapter 2 Using Forms

JSP engine, it will create a Date converter. Then it will ask its parent tag
(<inputText>) to find out the JSF component its parent has created (here, it's
the UI Input component). Then it will tell the UI Input component to use that
Date converter:
2: What is the JSF component
<f:view> that you created? Oh, it's that
<h:form> UI Input.
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}"/>
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}">
<f:convertDateTime/>
</h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>
1: Create a Date converter

Date converter

UI Input 3: Tell it to use this converter

Now run it and it should work:

Why it shows "Jan 19, 2008" instead of say 1/19/2008 or 19/1/2008? This is
controlled by two factors: the most preferred language set in the browser and
the style used by the converter. Here are some examples:

Short style Medium style Long style Full style


US English 1/19/2008 Jan 19, 2008 January 19, 2008 Saturday,
January 19,
2008
UK English 19/1/2008 ... ... ...
... ... ... ... ...
As you can see, by default it uses the medium style. To tell it to use say the
Chapter 2 Using Forms 69

short style, do it this way:


<f:view>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}"/>
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>
Now, run it and it should be like:

To change the most preferred language, you can change it in the browser. For
example, in Firefox, it is set in "Tools | Options | Advanced":

Click "Choose":
70 Chapter 2 Using Forms

Handling conversion errors


What if the user enters some garbage like "abc" as the date? In the Process
Validations phase, the Date converter will try convert "abc" to a Date object but
it will fail. Then it will log an error message into a list of messages associated
with the request and tell the JSF engine to jump to the Render Response
phase, skipping any phases in between (i.e., the Update Domain Values phase
and the Invoke Application phase):

Request
abc 2: Log an error Message list
message
abc is invalid

Date
converter
UI Text

Raw: "abc"
1: Try to convert it
to a Date but fails

Restore Apply Process Update


view request values validations domain values

Invoke
Render application
response 3: Jump to the render response
phase directly
Chapter 2 Using Forms 71

This is good, because if anything is wrong, you don't want to update the beans
(Update Domain Values) and don't want to change the view id (Invoke
Application) so that the original page is redisplayed. What else would you like to
do? To display an error message in the original page. To do that, modify
getquotesymbol.jsp:
<f:view>
<h:messages></h:messages>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}"/>
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>
It will create a
UI Messages
UI View component
Root

UI
Messages

UI Form

...

UI Input

...

The UI Messages component will display all the messages in the message list
(if there is no message, it will render nothing). Now, run the application, enter
"abc" as the date and click OK, you'll see:
72 Chapter 2 Using Forms

The whole path is called the client id of the


UI Input component

UI View The id of the


Root UI Form
component
UI
Messages

The id of the
Form UI Input
component

...

UI Input

...

The client id is mainly used as the value of the id or name attribute of the HTML
element generated. If you view the source of the HTML page, you'll see how
various client ids are used:
Chapter 2 Using Forms 73

Anyway, displaying the client id is quite confusing to users. Instead, you should
display a user friend description for the text field. To do that, modify
getquotesymbol.jsp:
<f:view>
<h:messages></h:messages>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}"/>
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}" label="quote date">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>
Run it again and it will display label instead of the client id:

If you don't like this error message, you can provide your own. To do that,
create a text file named messages.properties in the stockquote package (the
name is not really significant as long as it has a .properties extension):
74 Chapter 2 Using Forms

You may specify TIME here When the line is too long, you
when you use the converter it can use a backslash to tell
This is called the resource key Java to continue to the next
to convert a time
line.

javax.faces.converter.DateTimeConverter.DATE={0} is an invalid {2}. \


Try something like {1}

The label ("quote date")


An example string that is valid
such as "12/20/08"
JSF will fill in the user
input ("abc")

Then open faces-config.xml, choose the "Others" tab, click "Message Bundle"
and then click "Add":

3: Click here

2: Click here

1: Click here

Browse to select the messages.properties file:


Chapter 2 Using Forms 75

BUG ALERT: Due to a bug in Eclipse the screen may not be updated even
though the code has been modified. In the source, you should see:
<faces-config ...>
<application>
<message-bundle>stockquote.messages</message-bundle>
</application>
<managed-bean>
<managed-bean-name>quoteQuery</managed-bean-name>
<managed-bean-class>stockquote.QuoteQuery</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>stockService</managed-bean-name>
<managed-bean-class>stockquote.StockService</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<navigation-rule>
<display-name>getquotesymbol</display-name>
<from-view-id>/getquotesymbol.jsp</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/quoteresult.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
Now the JSF engine will load messages from this file and use them to override
the default messages. Run the application and it should work:
76 Chapter 2 Using Forms

If you'd like to specify the error message for that UI Input component only, you
can do it this way:
<f:view>
<h:messages></h:messages>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}" />
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}" label="quote date"
converterMessage="the quote date is invalid">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view> This will override the message provided
by the converter. As it is the converter
that is providing values for {0}, {1} and
{2}, you can't use such placeholders in
this string.

Marking input as required


Now the UI Input component will not accept garbage. But what if the user enters
an empty string? By default the UI Text assumes that you're allowing the input
to be optional. If the target data type is a string, it will remain as an empty string.
If the target data type is not a string, it will convert be converted to null (in this
case as the Date object). Then your code will crash at the code below:
public class QuoteQuery {
private String sym;
private Date quoteDate = new Date();

public Date getQuoteDate() {


return quoteDate;
}
public void setQuoteDate(Date quoteDate) {
this.quoteDate = quoteDate;
}
public String getSym() {
return sym;
}
public void setSym(String sym) {
this.sym = sym;
}
public int getStockValue() {
return (sym + quoteDate.toString()).hashCode() % 100;
}
}
To mark it as required, do it this way:
<f:view>
<h:messages></h:messages>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}" />
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}" label="quote date"
converterMessage="the quote date is invalid"
required="true">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
Chapter 2 Using Forms 77

</f:view>
Now, run it while setting the date to empty, you'll see:

Again, if you don't like the error message, you can override it in the
messages.properties file:
javax.faces.converter.DateTimeConverter.DATE={0} is an invalid {2}. \
Enter something like {1}
javax.faces.component.UIInput.REQUIRED=You must input {0}!

The label

If you'd like to set it just for this UI Input component, you can do it this way:
<f:view>
<h:messages></h:messages>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}" />
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}" label="quote date"
converterMessage="the quote date is invalid"
required="true"
requiredMessage="Input is missing!">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>

Using the calendar component


In fact, you can also allow the user to choose a date:

Download at WoweBook.com
78 Chapter 2 Using Forms

Clicking on the calendar icon will display a calendar:

To do that, you can use a JSF component library called RichFaces from JBoss.
Go to http://labs.jboss.com/jbossrichfaces to download it. Suppose that it is
richfaces-ui-3.1.3.GA-bin.zip. Unzip it into say c:\richfaces-ui. To use it, copy all
the jar files in c:\richfaces-ui\lib into your WEB-INF/lib. RichFaces in turn needs
a few third party jar files. You can go to
http://agileskills2.org/EssentialJSF/richfaces to download them and put them
into WEB/lib. Finally refresh the project in Eclipse.
Next, modify WEB-INF/web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
<display-name>StockQuote</display-name>
<welcome-file-list>
...
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
Chapter 2 Using Forms 79

</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>org.richfaces.SKIN</param-name>
<param-value>blueSky</param-value>
</context-param>
<filter>
<display-name>RichFaces Filter</display-name>
<filter-name>richfaces</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>richfaces</filter-name>
<servlet-name>Faces Servlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
</web-app>
You don't need to know the exact meaning of this code. Basically it is used to
enable the RichFaces engine to intercept requests so that it can deliver
Javascript to the browser. Close the getquotesymbol.jsp and open it again.
Then you should see some new tag libs such as RichFaces available on the
palette:

Next, choose the <calendar> tag from the RichFaces tag lib and drop it into the
getquotesymbol.jsp file and modify the file like this:
80 Chapter 2 Using Forms

<f:view>
<h:messages></h:messages>
<h:form>
<h:selectOneMenu value="#{quoteQuery.sym}">
<f:selectItems value="#{stockService.stockSymbols}" />
</h:selectOneMenu>
on <h:inputText value="#{quoteQuery.quoteDate}" label="quote date"
converterMessage="the quote date is invalid"
required="true"
requiredMessage="Input is missing!">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<rich:calendar
value="#{quoteQuery.quoteDate}" The same attributes
converterMessage="the quote date is invalid" are supported
required="true" except the "label"
requiredMessage="Input is missing!">
</rich:calendar>
<h:commandButton value="OK" action="ok"></h:commandButton>
</h:form>
</f:view>

Now, run it and it should work:

Hooking up the managed beans


For the moment, the stock value calculation is done in the QuoteQuery class:
public class QuoteQuery {
private String sym;
private Date quoteDate = new Date();

public Date getQuoteDate() {


return quoteDate;
}
public void setQuoteDate(Date quoteDate) {
this.quoteDate = quoteDate;
Chapter 2 Using Forms 81

}
public String getSym() {
return sym;
}
public void setSym(String sym) {
this.sym = sym;
}
public int getStockValue() {
return (sym + quoteDate.toString()).hashCode() % 100;
}
}
In a real implementation, you will need to look up a database or connect to a
network service provider to get the stock value. This kind of work is best done in
the StockService class. So, to make the code more realistic, let move the
calculation logic into the StockService class:
public class StockService {
private List<SelectItem> symbols;

public StockService() {
symbols = new ArrayList<SelectItem>();
symbols.add(new SelectItem("MSFT", "Microsoft"));
symbols.add(new SelectItem("IBM", "IBM"));
symbols.add(new SelectItem("RHAT", "Red Hat"));
}
public List<SelectItem> getStockSymbols() {
return symbols;
}
public int getStockValue(QuoteQuery q) {
return (q.getSym() + q.getQuoteDate().toString()).hashCode() % 100;
}
}
Then the code in the QuoteQuery class should call the StockService to get the
stock value. But how to get access to it?
public class QuoteQuery {
private String sym;
private Date quoteDate = new Date();

public Date getQuoteDate() {


return quoteDate;
}
public void setQuoteDate(Date quoteDate) {
this.quoteDate = quoteDate;
}
public String getSym() {
return sym;
}
public void setSym(String sym) {
this.sym = sym;
}
public int getStockValue() { How to get access to
StockService stkSrv = ???; the "stockService"
return stkSrv.getStockValue(this); bean?
}
}

To let the "quoteQuery" bean get access to the "stockService" bean, you can
say so in the bean definition table. Each row in the definition table can refer to a
property initialization table (see the diagram below). When the JSF engine
creates the "quoteQuery" bean, it will check its property initialization table. It
notes that it needs to initialize the "stkSrv" property of the "quoteQuery" bean.
82 Chapter 2 Using Forms

The value is specified as an EL expression #{stockService}. So it evaluates it


(the result is the "stockService" bean) and stores the result using the
setStkSrv() setter:
Definition table
Object name Object class Scope
stockService ... application
quoteQuery ... request
... ... ...

Property initialization
table

Property Initial value


stkSrv #{stockService}
... ...
... ...

public class QuoteQuery {


private String sym; It will be
evaluated
private Date quoteDate = new Date();
and set
private StockService stkSrv; using this
setter
public void setStkSrv(StockService stkSrv) {
this.stkSrv = stkSrv;
}
...
public int getStockValue() {
StockService stkSrv = null;
return stkSrv.getStockValue(this);
}
}

To specify the property initialization table, select the "quoteQuery" bean in


faces-config.xml:

Click here
to add an
entry

This is the property


initialization table
Chapter 2 Using Forms 83

Click "Add" and enter the information:

You also specify the property class so that JSF engine can double check the
data type. Save the file. If you're curious, you can check the source:
<faces-config ...>
<application>
<message-bundle>stockquote.messages</message-bundle>
</application>
<managed-bean>
<managed-bean-name>quoteQuery</managed-bean-name>
<managed-bean-class>stockquote.QuoteQuery</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>stkSrv</property-name>
<property-class>stockquote.StockService</property-class>
<value>#{stockService}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>stockService</managed-bean-name>
<managed-bean-class>stockquote.StockService</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<navigation-rule>
<display-name>getquotesymbol</display-name>
<from-view-id>/getquotesymbol.jsp</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/quoteresult.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
You can see that such a property is called a managed property because it is the
JSF engine that initializes it. Anyway, run the application and it should continue
to work.
Note that you can let the "quoteQuery" bean access the "stockService" bean but
not vice versa because the scope of the former (request) is shorter (the same is
also fine) than the latter (application). In the life time of the "stockService" bean,
there will be multiple "quoteQuery" beans (one for each request). It just doesn't
know which one to refer to.

Summary
To handle a form submission, the JSF engine will go through the following
84 Chapter 2 Using Forms

phases: Restore View, Apply Request Values, Process Validations, Update


Domain Values, Invoke Application and Render Response. If there is any error
in the Process Validations phase or the Update Domain Values phase, the JSF
engine will jump to the Render Response phase directly.
In order to be able to restore the view, it saves the component tree along with a
unique request id in the Render Response phase. Although the component tree
is restored, if you're accessing request-scoped managed beans, the bean you
use on form submission will not be the same as the one you used to render the
page.
To let the user edit a string in a text field, use the UI Input component and set its
"value" attribute to link it to the property of a managed bean. To let the user
choose an entry from a combo box, use the UI Select One component. You
need to provide a list of SelectItem to it. Each SelectItem contains an object and
its string presentation.
To let the user edit an Object in a text field, provide a converter to the UI Input
component. If there is a conversion error, it will log an error message.
To display error messages, use the UI Messages component.
To let the user click a button, use a UI Command component. Specify the
outcome in its "action" attribute. The JSF engine will use the current view id to
look up the right navigation rule and use the outcome to look up the right
navigation case to find the next view id. The UI Command will set the outcome
in the Invoke Application phase so that if there is any conversion or validation
error, it will not set the outcome and the original page is redisplayed.
You can customize the error messages using a resource bundle. This will affect
the whole application. To customize it for a particular component, simply set the
right attribute of the component.
You can let one managed bean get access to another using a managed
property. Make sure the referring bean has a shorter (or the same) life time
(scope) than the referred one.
85

Chapter 3
Chapter 3 Validating Input
86 Chapter 3 Validating Input

What's in this chapter?


In the previous chapter you've learned some basic ways of input validation:
marking a field as required and specifying a converter. In this chapter you'll
learn more advanced ways to validate input.

Postage calculator
Suppose that you'd like to develop an application to calculate the postage for
sending a package from some place to another. The user will enter the weight
of the package in kg (check the screen shots below). Optionally, he can enter a
"patron code" identifying himself as a patron to get a certain discount. After
clicking OK, it will display the postage:

To do that, create a new JSF project named Postage as usual. Create a


getrequest.jsp file. Then choose the <panelGrid> tag in the JSF HTML tag lib:

Drop it into the getrequest.jsp page:


Chapter 3 Validating Input 87

It will output an
HTML <table> There are 2 columns per
UI Panel row. It means that the 3rd
child component will go
into the next row.
A <panelGrid>
will create a UI
Panel component

Why use a <panelGrid> instead of the plain <table>? You could use a <table>
just fine:
<f:view>
<table>
<tr>
<td><h:outputText value="item1"></h:outputText></td>
<td><h:outputText value="item2"></h:outputText></td>
</tr>
<tr>
<td><h:outputText value="item3"></h:outputText></td>
<td><h:outputText value="item4"></h:outputText></td>
</tr>
</table>
</f:view>
A major difference is that the UI Panel will work fine with HTML or non-HTML
markup. When the <panelGrid> tag creates the UI Panel, it will assign an HTML
Panel renderer to it so that it will generate HTML markup (see the diagram
below). But if you assign another renderer to it, it will be able to generate some
other type of markup:
Output HTML
Render markup <table>
HTML Panel
me ...
renderer
</table>
UI Panel
...
Some other
renderer Output some
Render
me other type of
markup

Next, input the text prompt:


88 Chapter 3 Validating Input

Select item2 and press the Del key. Then the components following item2 will
flow forward to fill its place:

Then put an <inputText> into the original location of item2 (i.e., after the
"Weight:" label). Then the following components will flow back to their original
locations:
Chapter 3 Validating Input 89

The <form> is too small.


It should contain both
<inputText> elements.

Make the <form> larger:

Delete item4 and put in another <inputText>. You could create the <inputText>
as usual, alternatively, you could copy the existing <inputText> by Ctrl-dragging
it:
90 Chapter 3 Validating Input

Ctrl-drag it to copy it

Put an <outputText> that outputs an empty string and a <commandButton>. Set


the "value" attribute of the <commandButton> properly:

Create a Request class in the postage package to act as the bean to be edited:
Chapter 3 Validating Input 91

public class Request {


private int weight;
private String patronCode;
Need to access a service
private PostageService postageService; bean. So, need a setter:

public Request() { Need a no-argument


constructor
}
public Request(int weight, String patronCode) {
this.weight = weight;
this.patronCode = patronCode; Need getters and setters
} for it to be edited
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getPatronCode() {
return patronCode;
}
public void setPatronCode(String patronCode) {
this.patronCode = patronCode;
}
public void setPostageService(PostageService postageService) {
this.postageService = postageService;
}
public int getPostage() {
return postageService.getPostage(this);
}
}
Call the service bean to
calculate the postage

The PostageService class is:


package postage; Hard code some patrons
and their respective
public class PostageService { discounts. For example p1
private Map<String, Integer> patronCodeToDiscount; has 10% off.

public PostageService() {
patronCodeToDiscount = new HashMap<String, Integer>();
patronCodeToDiscount.put("p1", 90);
patronCodeToDiscount.put("p2", 95);
}
public int getPostage(Request r) {
Integer discount = (Integer) patronCodeToDiscount.get(r.getPatronCode());
int postagePerKg = 10;
int postage = r.getWeight() * postagePerKg;
if (discount != null) {
postage = postage * discount.intValue() / 100;
}
return postage; Assume that the postage is
} 10 dollar per kg
}

Create the bean definitions for them and hook them up:
92 Chapter 3 Validating Input

<faces-config ...>
<managed-bean>
<managed-bean-name>postageService</managed-bean-name>
<managed-bean-class>postage.PostageService</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean> Note that you must NOT
<managed-bean> call it "request" because
<managed-bean-name>req</managed-bean-name> there is a pre-defined bean
<managed-bean-class>postage.Request</managed-bean-class> named "request"
representing the HTTP
<managed-bean-scope>request</managed-bean-scope> request.
<managed-property>
<property-name>postageService</property-name>
<property-class>postage.PostageService</property-class>
<value>#{postageService}</value>
</managed-property>
</managed-bean>
</faces-config>

Link the beans and the UI Input components:


<f:view>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:inputText value="#{req.weight}"></h:inputText>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Next, create the result page. Let's call it showpostage.jsp:
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:view>
The postage is: <h:outputText value="#{req.postage}"></h:outputText>
</f:view>
</body>
</html>
Set the outcome in the <commandButton>:
<f:view>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:inputText value="#{req.weight}"></h:inputText>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Define the navigation rule:
Chapter 3 Validating Input 93

<faces-config ...>
<managed-bean>
<managed-bean-name>postageService</managed-bean-name>
<managed-bean-class>postage.PostageService</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>req</managed-bean-name>
<managed-bean-class>postage.Request</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>postageService</property-name>
<property-class>postage.PostageService</property-class>
<value>#{postageService}</value>
</managed-property>
</managed-bean>
<navigation-rule>
<display-name>getrequest</display-name>
<from-view-id>/getrequest.jsp</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/showpostage.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
Double click the Tomcat instance and add the project as a module. Then, run
the application by going to http://localhost:8080/Postage/faces/getrequest.jsp. It
should work:

What if the input is invalid?


At the moment if the user enters a negative number as the weight (e.g., -5), it
will go ahead and return a negative postage:
94 Chapter 3 Validating Input

This is no good. Instead, you'd like the application to tell the user that the weight
is invalid:

Similarly, it should also check if the patron code is valid or not. For example, if
the user enters "p3", it should tell him that this code is not found:

Note that as the patron code is optional, if he doesn't enter anything, it should
NOT be treated as an error. In order to validate the user input, you can add one
or more validator objects to a UI Input component (see the diagram below).
When the form is submitted, as mentioned before, in the Apply Request Values
the UI Input component will store the raw input string ("-5") locally. In the
Process Validations phase, it will convert it into an Object (an int -5 here). Then
Chapter 3 Validating Input 95

it will ask each of its validators (if any) in turn to validate the converted value (-5
here). If a validator fails, it will log an error message and will tell the JSF engine
to jump to the Render Response phase directly:
Message list
...

... 3: Log an
error
message
weight: "-5"

Validator 1
Read it
and store
2: Validate
it locally
UI Input UI Input the converted
value (-5)
raw: "-5" raw: "-5"
converted: -5
1: Convert

Restore View Apply Request Values Process Validations Update Domain Values

4: Go to the Render Response phase directly


Render Response Invoke Applications

In order to create such a validator, choose the <validateLongRange> tag in the


JSF Core tag lib:

Drop it into the body of the <inputText> element and the modify the code as
shown below:
96 Chapter 3 Validating Input

Display error messages Provide a label to be used in


the error message. Otherwise
<f:view> it will display the client id.
<h:messages></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:inputText label="weight" value="#{req.weight}">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view> Set the minimum value to 0 so that anything less
than 0 is an error. You could set the maximum
value too, but here there is no need to.

Now run the application again and it should work:

Again, you can customize the error message using a message bundle. For
example, create Postage.properties in the postage package:
The value is less than the
minimum value

javax.faces.validator.LongRangeValidator.MINIMUM={1} must be at least {0}!

The name of the


It is a validator validator The label ("weight" The minimum value (0
here) here)

Specify the message bundle in faces-config.xml:


<faces-config ...>
<application>
<message-bundle>postage.Postage</message-bundle>
</application>
...
</faces-config>
Chapter 3 Validating Input 97

Make sure the application is reloaded. Then run it and it should work:

In addition to this validator, there are similar ones for checking doubles and
strings:
The length of the string
should be between 3
and 20

<f:validateLength minimum="3" maximum="20"></f:validateLength>

<f:validateDouble minimum="0" maximum="999999"></f:validateDouble>

The double value should


be >= 0 and <= 999999

Their resource keys are:


javax.faces.validator.LengthValidator.MINIMUM=...
javax.faces.validator.LengthValidator.MAXIMUM=...
javax.faces.validator.DoubleRangeValidator.MINIMUM=...
javax.faces.validator.DoubleRangeValidator.MAXIMUM=...

Null input and validators


What if the user doesn't input anything as the weight? As mentioned in the
previous chapter, the UI Input field will treat it as an empty string (if target data
type is string) or null (if the target data is not a string). No matter which case it
is, all the validators will be skipped automatically. This design is to allow the
case when some input is optional, but if the user does provide some input, then
it must be validated. Here in this case, if you enter an empty string as the
weight:
98 Chapter 3 Validating Input

The application will display an error because the UI Input can't store a null into
an int property (It could if it was an Integer property):

In this case, you can simply solve the problem by marking the weight as
required:
<f:view>
<h:messages></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:inputText label="weight" value="#{req.weight}" required="true">
<f:validateLength></f:validateLength>
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Then the user will see:
Chapter 3 Validating Input 99

Validating the patron code


Now the weight field is working fine. How to validate the patron code? There is
no built-in validator suitable. In that case, you can specify a validator method:
<f:view>
<h:messages></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:inputText label="weight" value="#{req.weight}" required="true">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"
validator="#{req.validatePatron}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
This EL expression will evaluate to a Method object
</h:form>
representing this method. The UI Command will then
</f:view> use this method as a validator.
public class Request {
...
public void validatePatron(...) {
...
}
}

So, create that validatePatron() method:


100 Chapter 3 Validating Input

The converted value (here the patron code). It


must be non-empty, otherwise this code would
not have been called.
public class Request { The component being
private int weight; It represents the JSF validated (the UI Input
private String patronCode; engine here)
private PostageService postageService;
...
public void validatePatron(FacesContext context, UIComponent component,
Object convertedValue) {
String patronCode = (String) convertedValue; If it doesn't exist, throw
if (!postageService.patronExists(patronCode)) { a ValidatorException.
throw new ValidatorException(new FacesMessage( A message contains three
FacesMessage.SEVERITY_ERROR, pieces of information: the
"Patron code is invalid", severity (INFO, WARN,
"Patron code:" + patronCode + " is invalid")); ERROR, ...), a summary
} message and a detail
} message. By default, the UI
You'll create this Messages will display the
}
method next summary messages.

Create the patronExists() method in the PostageService class:


public class PostageService {
private Map<String, Integer> patronCodeToDiscount;

public PostageService() {
patronCodeToDiscount = new HashMap<String, Integer>();
patronCodeToDiscount.put("p1", 90);
patronCodeToDiscount.put("p2", 95);
}
public int getPostage(Request r) {
Integer discount = (Integer) patronCodeToDiscount
.get(r.getPatronCode());
int postagePerKg = 10;
int postage = r.getWeight() * postagePerKg;
if (discount != null) {
postage = postage * discount.intValue() / 100;
}
return postage;
}
public boolean patronExists(String patronCode) {
return patronCodeToDiscount.containsKey(patronCode);
}
}
Now run it and it should work:

However, the Request class is a domain class and thus should not know about
JSF stuff such as FacesContext, UIComponent and ValidatorException. To
Chapter 3 Validating Input 101

solve this problem, you can create a new bean to do this work. For example,
let's create a PatronCodeValidatingBean class in the same package and move
the validatePatron() method into there:
public class PatronCodeValidatingBean {
private PostageService postageService;

public void setPostageService(PostageService postageService) {


this.postageService = postageService;
}
public void validatePatron(FacesContext context, UIComponent component,
Object convertedValue) {
String patronCode = (String) convertedValue;
if (!postageService.patronExists(patronCode)) {
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Patron code is invalid",
"Patron code:" + patronCode + " is invalid"));
}
}
}
Define the bean:
<faces-config ...>
...
<managed-bean>
<managed-bean-name>postageService</managed-bean-name>
<managed-bean-class>postage.PostageService</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>req</managed-bean-name>
<managed-bean-class>postage.Request</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>postageService</property-name>
<property-class>postage.PostageService</property-class>
<value>#{postageService}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>patronCodeValidatingBean</managed-bean-name>
<managed-bean-class>
postage.PatronCodeValidatingBean
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<managed-property>
<property-name>postageService</property-name>
<property-class>postage.PostageService</property-class>
<value>#{postageService}</value>
</managed-property>
</managed-bean>
...
</faces-config>
Note how it refers to the "postageService" bean. This is OK as they are both
application scoped beans and thus have the same life span. Next, modify
getrequest.jsp:
<f:view>
<h:messages></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:inputText label="weight" value="#{req.weight}" required="true">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"
102 Chapter 3 Validating Input

validator="#{PatronCodeValidatingBean.validatePatron}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Run it and it should continue to work.

Displaying the error messages in red


Suppose that you'd like the error messages to be in red. To do that, modify
getrequest.jsp:
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html> Define some styles
<head> These styles are called
<meta http-equiv="Content-Type" content="text/html; "CSS styles". CSS
stands for cascading
charset=ISO-8859-1">
style sheet.
<title>Insert title here</title>
<style type="text/css">
li.err { color: red }
</style> The browser will look up the styles and find line
</head> matching the element (<li>) and the class ("err").
<body> Therefore it will set the color of the <li> element
<f:view> to red.
<h:messages errorClass="err"></h:messages>
...
</f:view>
</body>
</html>
<ul>
<li class="err">...</li>
<li>...</li>
</ul>

Now run it and it will work:


Chapter 3 Validating Input 103

Displaying the error message along with the field


You may wonder what is the purpose of the detail message in a FacesMessage.
It is intended to be displayed along with the field like:

To do that, open getrequest.jsp, choose the <message> tag (NOT the plural
<messages> tag!) in the JSF HTML tag lib:
104 Chapter 3 Validating Input

Drop it after the first <inputText> element. However, the page will look like:

This is due to the way the UI Panel lays out its child components. To solve this
problem, you can put the UI Input component and the UI Message component
into another UI Panel (see the diagram below). It is important that the new UI
Panel render its child components one by one without any extra markup. To do
that, just let it use a group renderer, while the original UI Panel is using a grid
renderer:
Chapter 3 Validating Input 105

<table>
<tr>
<td>[MARKUP OF CHILD1]</td>
<td>[MARKUP OF CHILD2]</td>
...

Grid
renderer

UI Panel UI Panel [MARKUP OF CHILD1] [MARKUP


OF CHILD2] ...

... ...
Group
renderer

UI Input UI Panel

UI
Message
UI Input

UI
Message

To implement this idea, choose the <panelGroup> tag in the JSF HTML tag lib:

Then move the <inputText> element and the <message> element into its body:
<f:view>
<h:messages errorClass="err"></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:panelGroup>
<h:inputText label="weight" value="#{req.weight}"
required="true">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:message></h:message>
</h:panelGroup>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"
106 Chapter 3 Validating Input

validator="#{PatronCodeValidatingBean.validatePatron}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Then the page will look fine again:

Finally, set up the <message> tag:


<f:view>
<h:messages errorClass="err"></h:messages>
<h:form id="f">
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:panelGroup>
<h:inputText id="w" label="weight" value="#{req.weight}"
required="true">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:message for="w"></h:message>
</h:panelGroup>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"
validator="#{PatronCodeValidatingBean.validatePatron}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view> 1: "w" is a relative client id. It uses
the client id of the form to get the 2: It will display the detail
client id (f:w) and use it to look up message
the message.

Clie nt id Leve l Sum m ary Detail


f :w ERROR ... ...
... ... ... ...

It is not really required to set the id of the <form>. It is set just to show you what
the client id looks it. Run it and it should work fine:
Chapter 3 Validating Input 107

Why is the detail message the same as the summary message? This is
because you are not providing the detail message in the message bundle. To
provide it, modify Postage.properties:
javax.faces.validator.LongRangeValidator.MINIMUM={1} must be at least {0}!
javax.faces.validator.LongRangeValidator.MINIMUM_detail={1} is invalid. It must \
be at least {0}!

Just add the string


"_detail" to the key

Run it again and it should work:

To make the message appear in red, just set its "errorClass" attribute:
108 Chapter 3 Validating Input

...
<style type="text/css">
li.err {
color: red
}
span.err { The UI Message
color: red component will
} output a <span>
</style>
...
<f:view>
<h:messages errorClass="err"></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:panelGroup>
<h:inputText id="w" label="weight" value="#{req.weight}"
required="true">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:message for="w" errorClass="err"></h:message>
</h:panelGroup>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"
validator="#{PatronCodeValidatingBean.validatePatron}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>

Run it and it should work:

Validating a combination of multiple input values


Suppose that for a particular patron p1, you will never ship a package that is
weighted more than 50kg. As this involves both the weight and the patron code
(two components), you can't make a validator and assign it to a single
component. In this case, you can register a so-called action listener to perform
the validation. The JSF engine will call all action listeners in the Invoke
Application phase.
To implement this idea, open getrequest.jsp, choose the <actionListener> tag in
Chapter 3 Validating Input 109

the JSF Core tag lib:

Drop it into the body of the <commandButton> element and then modify it:
<f:view>
<h:messages errorClass="err"></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:panelGroup>
<h:inputText id="w" label="weight" value="#{req.weight}"
required="true">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:message for="w" errorClass="err"></h:message>
</h:panelGroup>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"
validator="#{PatronCodeValidatingBean.validatePatron}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok">
<f:actionListener type="postage.RequestValidatingListener" />
</h:commandButton>
</h:panelGrid>
</h:form> This is the class of the
</f:view> action listener. You'll
create this class next.

Create this RequestValidatingListener class in the postage package:


110 Chapter 3 Validating Input

This event means the


click on the button
The application contains various Get access to the JSF
helpers that can be customized. engine from the view of
Here, you'll use it to evaluate an this request
EL expression.
public class RequestValidatingListener implements ActionListener {
public void processAction(ActionEvent event) Evaluate this EL
throws AbortProcessingException { expression to get access
FacesContext context = FacesContext.getCurrentInstance(); to the "req" bean
Application app = context.getApplication();
Request req = (Request) app.evaluateExpressionGet(context, "#{req}",
Request.class); Validate it. You'll create
if (!req.isValid()) { this method next
context.addMessage("f:w", new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"weight too heavy for the patron",
null));
throw new AbortProcessingException();
}
} If you specify null as the
} detail message, it will be
treated as equal to the Record the error for this
summary. component
Tell the JSF engine to stop
processing this event. As the
outcome is set as the last step,
it won't be set.

Make sure the <form> has the id set:


<f:view>
<h:messages errorClass="err"></h:messages>
<h:form id="f">
<h:panelGrid border="1" columns="2">
<h:outputText value="Weight:"></h:outputText>
<h:panelGroup>
<h:inputText id="w" label="weight" value="#{req.weight}"
required="true">
<f:validateLongRange minimum="0"></f:validateLongRange>
</h:inputText>
<h:message for="w" errorClass="err"></h:message>
</h:panelGroup>
<h:outputText value="Patron code:"></h:outputText>
<h:inputText value="#{req.patronCode}"
validator="#{PatronCodeValidatingBean.validatePatron}"></h:inputText>
<h:outputText></h:outputText>
<h:commandButton value="Submit" action="ok">
<f:actionListener type="postage.RequestValidatingListener" />
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Create the isValid() method in the Request class:
public class Request {
private int weight;
private String patronCode;
private PostageService postageService;
...
public boolean isValid() {
if (patronCode.equals("p1") && weight > 50) {
return false;
}
return true;
}
}
Chapter 3 Validating Input 111

Note that its properties will have been updated in the Update Domain Values
phase while the action listener is executed in the Invoke Application phase.
Now, run it and it should work:

Summary
A UI Panel component lays out its child components according to its renderer. It
can lay them out in a table (<panelGrid>) or just arrange them one by one
(<panelGroup>).
To validate the user input in a single UI Input component, you can add one or
more validator to it. They will be invoked one by one in the Process Validations
phase to check the converted value. If any one fails, it will log an error for that
component (or rather, for its client id) and tell the JSF engine to jump to the
Render Response phase directly.
There are a few pre-defined validators coming with JSF for checking the range
of an int, a double or the length of a string. To customize their error messages,
use a message bundle.
To perform custom validation, you can do it in a method of a bean and use the
"validate" attribute of the tag to refer to it. It will be treated like a normal
validator. If the validation involves two or more components, you can add an
action listener to a UI Command. It will be executed in the Invoke Application
phase. As such the beans will have been updated so you can check their
properties.
A JSF message contains a severity level, a summary and a detail. Usually you
will display the summary using a UI Messages component, while display the
detail using a UI Message component along with each UI Input component.
To customize the appearance of the HTML output, you can define CSS style
classes and let the components to refer to them.
113

Chapter 4
Chapter 4 Creating an e-Shop
114 Chapter 4 Creating an e-Shop

What's in this chapter?


In this chapter you'll learn how to create an e-shop. This involves implementing
a global product catalog, a shopping cart for each user, user login and logout
and requiring authenticated access for the checkout page.

Creating an e-shop
Suppose that you'd like to create an e-shop. The front page lists all the
products:

As you can see, a product has an id, a name and a price. For example, the first
product's id is "p01", its name is "Pencil" and its price is $1.2. If the user clicks
on a product say "Eraser", he will see a detailed description of Eraser:

For simplicity, you will be using strings like "a", "b" and "c" as the detailed
descriptions for the products.
Chapter 4 Creating an e-Shop 115

Listing the products


OK, let's do it. Create a new Dynamic Web Project named Shop and enable
JSF. Then create a showcatalog.jsp page. Enter the heading:

Right click the heading and choose "Style | Paragraph Format | Heading 1":

Then it will become:


116 Chapter 4 Creating an e-Shop

To list the products in a table, you can't use the <panelGrid> as it requires you
to add the child components at design time. Instead, now you need to query the
database at runtime to determine how many rows to have (one for each
product). For this purpose, use the <dataTable> tag in the JSF HTML tag lib:

Drop it into the page after the <h1> element in the source pane:
Chapter 4 Creating an e-Shop 117

Here, the UI
Column will
render its header
facet if it is the
first row only
UI Column
(header row)

header
UI Output
3: Add the component to the
parent as a special kind of
It will create a UI Column which child called a facet. How a
represents the whole column facet is used is entirely up to
the parent to decide.
2: Look, a facet name 1: Create
("header") is set
It will create a
UI Data
component
which will
render as the
table
You can put other components here.
The UI Column will render them if it is
a subsequent row (data row).

Another
column

Modify the text for the two headers:


118 Chapter 4 Creating an e-Shop

Add components for the data rows:


Whether it is put before
the <facet> or after it
doesn't matter at all
<h:dataTable border="1">
<h:column id="column1">
<h:outputText></h:outputText>
<f:facet name="header">
<h:outputText value="Id"></h:outputText>
</f:facet>
</h:column>
<h:column id="column2">
<h:outputText></h:outputText>
<f:facet name="header">
<h:outputText value="Name"></h:outputText>
</f:facet>
</h:column>
</h:dataTable>

To add the price column, choose the <column> tag in the JSF HTML tag lib:
Chapter 4 Creating an e-Shop 119

Drop it into the <dataTable> after the two existing columns:

It has no facet. To add the facet, choose the <facet> tag in the JSF Core tag lib
(non-HTML markup may also use facets):

Drop it into the column (works only in the source pane):


120 Chapter 4 Creating an e-Shop

It is in error because it needs a facet name. Modify the code:


<h:dataTable border="1">
<h:column id="column1">
<h:outputText></h:outputText>
<f:facet name="header">
<h:outputText value="Id"></h:outputText>
</f:facet>
</h:column>
<h:column id="column2">
<h:outputText></h:outputText>
<f:facet name="header">
<h:outputText value="Name"></h:outputText> Set the facet name to "header".
</f:facet> Must use this name as the UI
</h:column> For the data rows Column only recognizes this
<h:column> name.
<h:outputText></h:outputText>
<f:facet name="header">
<h:outputText value="Price"></h:outputText>
</f:facet>
</h:column>
</h:dataTable> Output the header text. You
must use an <outputText>. You
can't do it this way:
<h:column>
This won't work because you <h:outputText></h:outputText>
must have one and only one
<f:facet name="header">
component inside <facet>. It
also lacks the intelligence to Price
turn plain HTML code into UI </f:facet>
Output components. </h:column>

To provide the data items, create a Catalog class in the shop package:
Chapter 4 Creating an e-Shop 121

package shop;

public class Catalog {


private List<Product> products;

public Catalog() {
products = new ArrayList<Product>();
products.add(new Product("p01", "Pencil", "a", 1.20));
products.add(new Product("p02", "Eraser", "b", 2.00));
products.add(new Product("p03", "Ball pen", "c", 3.50));
}
public List<Product> getProducts() {
return products;
}
}
Define the Product class in the same package:
package shop;

public class Product {


private String id;
private String name;
private String desc;
private double price;

public Product(String id, String name, String desc, double price) {


this.id = id;
this.name = name;
this.desc = desc;
this.price = price;
}
public String getDesc() {
return desc;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
Define a managed bean for the catalog. As it is a global thing, use the
application scope:
<faces-config ...>
<managed-bean>
<managed-bean-name>catalog</managed-bean-name>
<managed-bean-class>shop.Catalog</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
</faces-config>
Now, tell the UI Data to get the items from the bean (see the diagram below).
When the UI Data needs to render itself, it will loop through the list of Product
objects. For each Product, it will ask its children to render:
122 Chapter 4 Creating an e-Shop

p01 p02 p03


<h:dataTable border="1" value="#{catalog.products}">
<h:column id="column1">
<h:outputText></h:outputText> Render them once for p01,
<f:facet name="header"> UI Data once for p02 and once for
<h:outputText value="Id"></h:outputText> p03
</f:facet>
</h:column>
<h:column id="column2"> Column 1
<h:outputText></h:outputText>
<f:facet name="header">
<h:outputText value="Name"></h:outputText> UI Output
</f:facet>
</h:column>
<h:column> Column 2
<h:outputText></h:outputText>
<f:facet name="header">
<h:outputText value="Price"></h:outputText>
</f:facet> Column 3
</h:column>
</h:dataTable>

How can the columns access the current Product? You can do it this way:
Chapter 4 Creating an e-Shop 123

p01 p02 p03

UI Data

2: In each turn, add or


set an attribute in the
table to point to the Attribute table for request 1
current item
Object name Object instance
p
... ...
... ...

Request 1
1: "p" is the name of an "attribute". There is a table for
each request like this. It looks like the table for request-
3: An EL expression can refer to scoped managed beans but they aren't the same thing.
attributes ("p" here). The JSF This table can function without a bean definition table. It
engine will try to find a bean named only associates an object with a name but won't create
"p" in all three scopes first. If not objects. Each entry is called an attribute.
found, it will try to find an attribute.

<h:dataTable border="1" value="#{catalog.products}" var="p">


<h:column id="column1">
<h:outputText value="#{p.id}"></h:outputText>
<f:facet name="header">
<h:outputText value="Id"></h:outputText>
</f:facet>
</h:column>
<h:column id="column2">
<h:outputText value="#{p.name}"></h:outputText>
<f:facet name="header">
<h:outputText value="Name"></h:outputText>
</f:facet>
</h:column>
<h:column>
<h:outputText value="#{p.price}"></h:outputText>
<f:facet name="header">
<h:outputText value="Price"></h:outputText>
</f:facet>
</h:column>
</h:dataTable>

Now run it and it should work:


124 Chapter 4 Creating an e-Shop

Showing the product details


Now, let's create the links to show the product details. Choose the
<commandLink> tag from JSF HTML tag lib and drop it into the product name
column:

Drag the <outputText> for the product name and drop it into the body of the
<commandLink>:
Chapter 4 Creating an e-Shop 125

Delete the default <outputText> created when you dropped the


<commandLink>.
How to respond to a click on the link? A <commandLink> is very similar to a
<commandButton>. They will both create a UI Command component. One is
rendered as a link while the other is rendered as a button:
Render Output markup <a href="...">...</a>
me Link
renderer

UI Command
<input type="button" ...>
Button
renderer Output markup
Render
me

As it is just like a <commandButton>, it will submit a form and thus it must be


inside a <form> element. That's why a <form> element was created when you
dropped the <commandLink>:
<h:column id="column2">
<h:form>
<h:commandLink>
<h:outputText value="#{p.name}"></h:outputText>
</h:commandLink>
</h:form>
<f:facet name="header">
<h:outputText value="Name"></h:outputText>
</f:facet>
</h:column>
As it is just like a <commandButton>, you can set its "action" attribute to specify
126 Chapter 4 Creating an e-Shop

the outcome:
<h:column id="column2">
<h:form>
<h:commandLink action="detail">
<h:outputText value="#{p.name}"></h:outputText>
</h:commandLink>
</h:form>
<f:facet name="header">
<h:outputText value="Name"></h:outputText>
</f:facet>
</h:column>
For it to work, create a productdetails.jsp page:

Create a "product" bean for it:


<faces-config ...>
<managed-bean>
<managed-bean-name>catalog</managed-bean-name>
<managed-bean-class>shop.Catalog</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>product</managed-bean-name>
<managed-bean-class>shop.Product</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
Make sure the Product class has a no-argument constructor:
public class Product {
private String id;
private String name;
private String desc;
private double price;

public Product() {
Chapter 4 Creating an e-Shop 127

}
public Product(String id, String name, String desc, double price) {
this.id = id;
this.name = name;
this.desc = desc;
this.price = price;
}
public String getDesc() {
return desc;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
Create a navigation case for it:

However, there is still a question: Who will set the "product" bean to the
selected Product object? You can do it this way (see the diagram below). The
<setPropertyActionListener> tag will create a "Set Property" action listener and
add it to the component created by its parent tag (here, the UI Command). For
some unknown reason, this tag is unavailable on the palette, so you have to
type it in manually. When the form is submitted, the UI Data will be asked to
apply the request values (decode). It will loop through the list of Product objects.
For each Product object, it will ask its child components to decode. For the UI
Command, it will check if it was clicked. If no, nothing happens. If yes, it usually
will schedule the Set Property action listener to be executed in the Invoke
Application phase. However, here the "immediate" attribute of the UI Command
is set to true. In that case it will call all its action listeners immediately in the
Apply Request Values phase. Here, the Set Property action listener will evaluate
#{p.id} and get the current Product id and then pass it to the setId() method of
the "product" bean:
128 Chapter 4 Creating an e-Shop

<h:column id="column2">
<h:form>
<h:commandLink action="detail" immediate="true">
<h:outputText value="#{p.name}"></h:outputText>
<f:setPropertyActionListener
value="#{p.id}" target="#{product.id}" />
</h:commandLink> Create
</h:form>
<f:facet name="header">
<h:outputText value="Name"></h:outputText>
</f:facet>
</h:column> Set Property
Action Listener
2: Apply request
UI Data values

... UI Command
3: Was I clicked? If yes, call the special action
listener. Usually, it is called in the Invoke
Application phase. But here, "immediate" is
p01 p02 p03 true, so call it in the Apply Request Values
phase (now).
1: Point to it
4: The listener evaluates
Attribute table Bean table #{p.id} and stores the
Object name Object instance Object name Object instance value into #{product.id}

p product
... ... ... ...
... ... ... ...

Product

void setId(String pid) {


...
}

What would happen if "immediate" is not set to true? Then the "p" attribute will
change in each turn of the loop. At the end of the loop it will be deleted
altogether. These will happen in the Apply Request Values phase. When it
reaches the Invoke Application phase, the Set Property action listener is called,
but then the "p" attribute no longer exists and #{p} will evaluate to null (Yes, it
will return null if it can't find a managed bean or attribute with that name).
Now, define the setId() method to update the other fields:
public class Product {
private String id;
private String name;
private String desc;
private double price;
private Catalog catalog;

public Product() {

}
public Product(String id, String name, String desc, double price) {
this.id = id;
this.name = name;
this.desc = desc;
this.price = price;
}
public String getDesc() {
Chapter 4 Creating an e-Shop 129

return desc;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public void setId(String pid) {
Product p = catalog.getProduct(pid);
id = p.id;
name = p.name;
desc = p.desc;
price = p.price;
}
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
}
}
Create the getProduct() method in the Catalog class:
public class Catalog {
private List<Product> products;

public Catalog() {
products = new ArrayList<Product>();
products.add(new Product("p01", "Pencil", "a", 1.20));
products.add(new Product("p02", "Eraser", "b", 2.00));
products.add(new Product("p03", "Ball pen", "c", 3.50));
}
public List<Product> getProducts() {
return products;
}
public Product getProduct(String pid) {
for (Product p : products) {
if (p.getId().equals(pid)) {
return p;
}
}
return null;
}
}
Inject the "catalog" bean into the "product" bean:
<faces-config ...>
<managed-bean>
<managed-bean-name>catalog</managed-bean-name>
<managed-bean-class>shop.Catalog</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>product</managed-bean-name>
<managed-bean-class>shop.Product</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>catalog</property-name>
<property-class>shop.Catalog</property-class>
<value>#{catalog}</value>
</managed-property>
</managed-bean>
<navigation-rule>
<display-name>showcatalog</display-name>
<from-view-id>/showcatalog.jsp</from-view-id>
<navigation-case>
<from-outcome>detail</from-outcome>
<to-view-id>/productdetails.jsp</to-view-id>
130 Chapter 4 Creating an e-Shop

</navigation-case>
</navigation-rule>
</faces-config>
Note that the UI Command was actually rendered three times and generated
three HTML <input> elements. How can it tell which one was clicked? It works
like this (see the diagram below): When the UI Data renders each row, the UI
Command will try to get a client id for itself. It will ask the UI Data for its client id.
Assuming that the id of the UI Data is "d". It will append the current row index (0
here) and return "d:0" as its client id. Assuming that the id of the UI Command is
"c", then its client id will be "d:0:c" and will use it to identify the <a> element. For
the second row, the client id will be "d:1:c" and etc.:
2: What's your client id?

3: It is d:0 in which 0 is the row


index
UI Data
4: Use its client id to
"d"
identify the link
... UI Command
"c"
<a id="d:0:c">...</a>
<a id="d:1:c">...</a>
1: Current item <a id="d:2:c">...</a>

p01 p02 p03

Suppose that the second product was clicked (so the HTML id is "d:1:c") and
the form is submitted. The UI Data will again loop through each Product object.
For each Product object, it will ask the UI Command to decode. The UI
Command will determine its client id first and then check if it was the link that
was clicked:

4: Equal to
3: Decode my client id?
clicked=d:1:c
... UI Data
"d"
... UI Command
"c"
1: The second Product
was clicked. The
request arrives. 2: Current item

p01 p02 p03

Now, run the application and it should work.

Implementing a shopping cart


Now, let's allow the user to add products to his shopping cart. Your purpose is
that the product details page should be like:
Chapter 4 Creating an e-Shop 131

If the user clicks "Continue shopping", the catalog page will be displayed:

If the user clicks "Add to cart", a single piece of the product is added to his
shopping cart, then the contents of his shopping cart will be displayed:

From there the user should be able to continue shopping or checkout. Now, let's
do it. First, add the two buttons to productdetails.jsp:
132 Chapter 4 Creating an e-Shop

<f:view>
<h1><h:outputText value="#{product.name}"></h:outputText></h1>
<h:outputText value="#{product.desc}"></h:outputText>
<h:form>
<h:commandButton value="Add to cart" action="#{product.addToCart}">
</h:commandButton>
<h:commandButton value="Continue shopping" action="catalog">
</h:commandButton>
</h:form>
Usually it is just the outcome, but here
</f:view> you specify a method. It should take no
argument and return a string which is
the outcome.

public class Product {


...
public String addToCart() {
}
}

Define this addToCart() method in the Product class:


public class Product {
private String id;
private String name; You'll inject the shopping cart into this
private String desc; property. So you need a setter.
private double price;
private Catalog catalog;
private Cart cart;
...
public void setCart(Cart cart) {
this.cart = cart;
Add the product id to
} the shopping cart
public String addToCart() {
cart.add(id);
return "added";
} Outcome indicating it was added
successfully
}

You need to have the shopping cart as a managed bean. What scope it should
be in? For each user to have his own shopping cart, it should be in the session
scope. So, let's create the Cart class:
Chapter 4 Creating an e-Shop 133

As it will be stored into the session and Tomcat


may save objects in the session to the hard disk
or send to other nodes in the cluster, all objects
in the session must implement Serializable.

public class Cart implements Serializable {


private List<String> productIds;

public Cart() {
productIds = new ArrayList<String>();
}
public void add(String pid) {
productIds.add(pid);
}
public List<String> getProductIds() {
return productIds;
}
}

Define a bean for the shopping cart and inject it into the "product" bean:
<faces-config ...>
<managed-bean>
<managed-bean-name>catalog</managed-bean-name>
<managed-bean-class>shop.Catalog</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>product</managed-bean-name>
<managed-bean-class>shop.Product</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>catalog</property-name>
<property-class>shop.Catalog</property-class>
<value>#{catalog}</value>
</managed-property>
<managed-property> Inject the "cart" bean into the
<property-name>cart</property-name> "product" bean
<property-class>shop.Cart</property-class>
<value>#{cart}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>cart</managed-bean-name>
<managed-bean-class>shop.Cart</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean> in the session scope
<navigation-rule>
<display-name>showcatalog</display-name>
<from-view-id>/showcatalog.jsp</from-view-id>
<navigation-case>
<from-outcome>detail</from-outcome>
<to-view-id>/productdetails.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>

Next, create a showcart.jsp page to display the shopping cart as below. Note
that it doesn't specify any "header" facet so the columns will not have any
header:
134 Chapter 4 Creating an e-Shop

Loop through the product ids Store each product id as an


in the shopping cart attribute named "pid"
<f:view>
<h1>Shopping cart</h1>
<h:dataTable border="1" value="#{cart.productIds}" var="pid">
<h:column id="column1">
<h:outputText value="#{pid}"></h:outputText>
</h:column>
<h:column id="column2"> Output the product id
<h:outputText value="#{???}"></h:outputText>
</h:column> How to output the product
<h:column id="column3"> name?
<h:outputText value="#{???}"></h:outputText>
</h:column>
</h:dataTable>
<h:form>
<h:commandButton value="Checkout" action="checkout"></h:commandButton>
<h:commandButton value="Continue shopping" action="catalog"></h:commandButton>
</h:form>
</f:view>

To solve this problem, you can't just provide a list of product ids to the UI Data.
You need to provide a list of Product objects. To do that, you may try to modify
the Cart class:
When a shopping cart is created, the "catalog" will be
injected into it. Therefore there is no need to drag in
the catalog during serialization, not to mention that it is
also not serializable.
public class Cart implements Serializable {
private List<String> productIds;
private transient Catalog catalog; You'll inject the catalog into
here
public Cart() {
productIds = new ArrayList<String>();
}
public void add(String pid) {
productIds.add(pid);
}
public List<String> getProductIds() {
return productIds;
}
public void setCatalog(Catalog catalog) {
this.catalog = catalog; Return a list of Product
} objects
public List<Product> getProducts() {
List<Product> products = new ArrayList<Product>();
for (String pid : productIds) {
products.add(catalog.getProduct(pid));
}
return products;
}
}

However, do NOT do that. When the "cart" bean is serialized and saved into the
hard disk (see the diagram below), the "catalog" bean is not included due to the
effect of the transient keyword. Later, when it is loaded from the hard disk and
deserialized, its "catalog" field will be null. The JSF engine is not involved and
won't inject the "catalog" bean into it at all:
Chapter 4 Creating an e-Shop 135

transient null
cart catalog cart

1: When the "cart" bean is saved


into the hard disk, the "catalog" 2: When it is loaded
bean is not included. from the hard disk,
its "catalog" field will
be null.

The take home message is that you should never let a session scoped bean
refer to any other managed bean. But how to solve the problem? You can
create an extra request scoped bean to retrieve product ids from the "cart" bean
and then translate ids into Product objects using the "catalog" bean:
showCart
Helper 2: Give me the Product
object for p01 and etc.

1: Give me the
product ids

cart catalog

To implement this idea, create a ShowCartHelper class:


public class ShowCartHelper {
private Cart cart;
private Catalog catalog;

public void setCart(Cart cart) {


this.cart = cart;
}
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
}
public List<Product> getProducts() {
List<Product> products = new ArrayList<Product>();
for (String pid : cart.getProductIds()) {
products.add(catalog.getProduct(pid));
}
return products;
}
}
Define the bean and inject the "cart" bean and the "catalog" bean into it:
<faces-config ...>
...
<managed-bean>
<managed-bean-name>showCartHelper</managed-bean-name>
<managed-bean-class>shop.ShowCartHelper</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>cart</property-name>
<property-class>shop.Cart</property-class>
<value>#{cart}</value>
136 Chapter 4 Creating an e-Shop

</managed-property>
<managed-property>
<property-name>catalog</property-name>
<property-class>shop.Catalog</property-class>
<value>#{catalog}</value>
</managed-property>
</managed-bean>
...
</faces-config>
Now, you can easily display the product name and price in showcart.jsp:
<f:view>
<h1>Shopping cart</h1>
<h:dataTable border="1" value="#{showCartHelper.products}" var="p">
<h:column id="column1">
<h:outputText value="#{p.id}"></h:outputText>
</h:column>
<h:column id="column2">
<h:outputText value="#{p.name}"></h:outputText>
</h:column>
<h:column id="column3">
<h:outputText value="#{p.price}"></h:outputText>
</h:column>
</h:dataTable>
<h:form>
<h:commandButton value="Checkout" action="checkout"></h:commandButton>
<h:commandButton value="Continue shopping" action="catalog"></h:commandButton>
</h:form>
</f:view>
Next, define the navigation cases:

Now run it. Unfortunately, if you try to add a product to the shopping cart, the
product information will still not be displayed:
Chapter 4 Creating an e-Shop 137

This is because when the click "Add to cart", a new "product" bean will be
created by the UI Command component:
<f:view>
<h1><h:outputText value="#{product.name}"></h:outputText></h1>
<h:outputText value="#{product.desc}"></h:outputText>
<h:form>
<h:commandButton value="Add to cart" action="#{product.addToCart}">
</h:commandButton>
<h:commandButton value="Continue shopping" action="catalog">
</h:commandButton>
</h:form>
</f:view>
Obviously the id in that new Product object is not set at all. In order to
remember the product id, choose the <inputHidden> tag from the JSF HTML
tag lib and drop it into the <form>:

It will create a UI Input component just like <inputText>. The difference is that
138 Chapter 4 Creating an e-Shop

that it will use an HTML Hidden renderer. When it's rendering (see the diagram
below), it will get the id of the "product" bean and output it into an HTML hidden
field through the HTML Hidden renderer:

"product"

1: Get its id (e.g., p02)


UI Input

value: p02
...
3: Generate an HTML hidden
Hidden field <input type="hidden"
renderer
2: Read the value name="client-id"
value="p02">
...

When the form is submitted (see the diagram below), the value of the HTML
hidden field (p02) is included in the request. The UI Input component will
retrieve the value (Apply Request Values) and store it into the id of the "product"
bean (Update Domain Values). Then your setId() method will update the other
fields:

client-id: p02 "product"


...

2: Set the id (p02)


1: Request arrives UI Hidden

Alternatively, you can think of an <inputHidden> as an <inputText> except that


the value is invisible in the browser and thus the user won't be able to see or
change it. Now run it and it should work.

Of course, if you have added more products you will see all of them listed here.
This is because the session is accumulating the product ids, and even after the
web application is reloaded, the session is not affected at all. In fact, even if you
restart Tomcat, the session is still there because Tomcat will save it to disk and
load it back later. To get rid of the old session and get a new one, you may wait
say 30 minutes, but an easier way is to close the browser and open a new one.
But how does it work? To understand it, you need to understand how Tomcat
Chapter 4 Creating an e-Shop 139

and the browser co-operate to maintain the session.

How Tomcat and the browser maintain the session


Before further explanation, let's add a product to the shopping cart and then
check the "cookies" stored in your browser. For example, for FireFox, choose
"Tools | Options":

Click "Privacy" on the top, click "Show Cookies". Locate the site "localhost" and
you'll find a cookie whose name is "JSESSIONID". This is how Tomcat and the
browser maintain the session:
140 Chapter 4 Creating an e-Shop

When a user first accesses a web application (see the diagram below), Tomcat
will generate a random number called "session id" and use it to identify the
session. Then it sends this session id back to the browser and tell it to save the
session id in a cookie named "JSESSIONID". In addition, Tomcat tells the
browser to associate the cookie with the host "localhost" and with the
path /Shop. Later when the browser accesses any page of the application (e.g.,
http://localhost/Shop/faces/foo.jsp), the browser finds that there is a cookie
associated with this host ("localhost") and that the path /Shop/faces/foo.jsp
being accessed is somewhere under the path associated with the cookie
(/Shop), so it will send the content of the cookie (the session id) to the server.
When Tomcat receives the session id, it can find out which session to use with
the id:
Chapter 4 Creating an e-Shop 141

5: Access
http://localhost/Shop/faces/ 1: Access http://localhost/Shop
foo.jsp 2: Create it
Browser 1 Tomcat

3: 111333 is the 8: You have


session id. Please save this id. Use
it into a JSESSIONID. it.

7: Send JSESSIONID=111333
to Tomcat

Session 111333
4: Save it along 6: Look, there is a cookie
with the host and for localhost and /Shop
path

Hos t Path Nam e Value


localhost /Shop JSESSIONID 111333

Session 123456

It means that if you delete this cookie and then access the application again,
Tomcat will treat you as a new user. But why restarting the browser also works?
The cookies are stored on disk and so they are persistent. So, usually restarting
the browser will not delete them. However, each cookie has a maximum age (in
seconds). For example, see the "Expires" field of another cookie shown below:

If that age is set to -1, it means the browser should delete the cookie when the
browser is closed. As shown in the screen shot below, this is exactly the case
with your JSESSIONID cookie ("at end of session"):
142 Chapter 4 Creating an e-Shop

The checkout function


You have implemented the shopping cart. Now, let's add the checkout function.
Suppose that for a user to checkout, he must first create an account with you (to
enter his credit card # and etc.) and login. For simplicity, let's assume that there
are already some existing user accounts:

Email Password Credit card #


paul@yahoo.com aaa 1111 2222 3333 4444
john@hotmail.com bbb 2222 3333 4444 5555
Suppose that a user can click a login link on the catalog page:

After logging in, the catalog page is displayed again. When a user tries to
Chapter 4 Creating an e-Shop 143

checkout, if he already logged in, he only needs to confirm the checkout:

But if he hasn't logged in when trying to checkout, he will be asked to login first,
then he will see confirm page:
144 Chapter 4 Creating an e-Shop

Implementing the login function


Now, let's add the login link in showcatalog.jsp:
<f:view>
<h1>Product listing</h1>
<h:dataTable border="1" value="#{catalog.products}" var="p">
...
</h:dataTable>
<h:form>
<h:commandLink action="login">Login</h:commandLink>
</h:form>
</f:view>
Create a login.jsp file:
<f:view>
<h1>Login</h1>
<h:panelGrid border="1" columns="2">
<h:outputText value="Email:"></h:outputText>
<h:outputText value="item2"></h:outputText>
<h:outputText value="Password:"></h:outputText>
<h:outputText value="item4"></h:outputText>
</h:panelGrid>
</f:view>
Delete the item2 and item4. Drop an <inputText> in place of item2. Drop an
<inputSecret> in place of item4. Make sure they are in the same <form>
element:
Chapter 4 Creating an e-Shop 145

<f:view>
<h1>Login</h1>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Email:"></h:outputText>
<h:inputText></h:inputText>
<h:outputText value="Password:"></h:outputText>
<h:inputSecret></h:inputSecret>
</h:panelGrid>
</h:form>
</f:view> It is just like <inputText> in that it will
create a UI Input. The only difference is
that the user input will appear as stars.
Again, this is done using a different
renderer.

Add a dummy <outputText> and a <commandButton>:


<f:view>
<h1>Login</h1>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Email:"></h:outputText>
<h:inputText></h:inputText>
<h:outputText value="Password:"></h:outputText>
<h:inputSecret></h:inputSecret>
<h:outputText></h:outputText>
<h:commandButton value="Login"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Create a LoginRequest class:
public class LoginRequest {
private String email;
private String password;

public String getEmail() {


return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Create a managed bean from it:
<faces-config ...>
...
<managed-bean>
<managed-bean-name>loginRequest</managed-bean-name>
<managed-bean-class>shop.LoginRequest</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
...
</faces-config>
Bind it to the UI Input components:
<f:view>
146 Chapter 4 Creating an e-Shop

<h1>Login</h1>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Email:"></h:outputText>
<h:inputText value="#{loginRequest.email}"></h:inputText>
<h:outputText value="Password:"></h:outputText>
<h:inputSecret value="#{loginRequest.password}"></h:inputSecret>
<h:outputText></h:outputText>
<h:commandButton value="Login"></h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
To handle the form submission, you need a business action method:
<f:view>
<h1>Login</h1>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Email:"></h:outputText>
<h:inputText value="#{loginRequest.email}"></h:inputText>
<h:outputText value="Password:"></h:outputText>
<h:inputSecret value="#{loginRequest.password}"></h:inputSecret>
<h:outputText></h:outputText>
<h:commandButton value="Login" action="#{loginRequest.login}">
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Define the login() method:
Chapter 4 Creating an e-Shop 147

public class LoginRequest {


private String email;
private String password;
private UserService userService;
private LoginSession loginSession; You'll create this session scoped
bean to remember the logged in
public String getEmail() { user
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
public void setLoginSession(LoginSession loginSession) {
this.loginSession = loginSession;
}
public String login() { Try to find a matching user
try {
User user = userService.findMatchingUser(this);
loginSession.setUser(user);
return "loggedIn"; Log an error message
} catch (UserNotFoundException e) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Login failed", null));
return null;
}
} Return null so that the existing page is
redisplayed
}

To display the error messages, add a <messages> element:


<f:view>
<h1>Login</h1>
<h:messages></h:messages>
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Email:"></h:outputText>
<h:inputText value="#{loginRequest.email}"></h:inputText>
<h:outputText value="Password:"></h:outputText>
<h:inputSecret value="#{loginRequest.password}"></h:inputSecret>
<h:outputText></h:outputText>
<h:commandButton value="Login" action="#{loginRequest.login}">
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
Next, create the UserService class:
public class UserService {
private List<User> users;

public UserService() {
148 Chapter 4 Creating an e-Shop

users = new ArrayList<User>();


users.add(new User("paul@yahoo.com", "aaa", "1111 2222 3333 4444"));
users.add(new User("john@hotmail.com", "bbb", "2222 3333 4444 5555"));
}
public User findMatchingUser(LoginRequest loginRequest) {
for (User user : users) {
if (user.matches(loginRequest)) {
return user;
}
}
throw new UserNotFoundException();
}
}
Create the User class:
public class User {
private String email;
private String password;
private String creditCardNo;

public User(String email, String password, String creditCardNo) {


this.email = email;
this.password = password;
this.creditCardNo = creditCardNo;
}
public boolean matches(LoginRequest loginRequest) {
return email.equals(loginRequest.getEmail())
&& password.equals(loginRequest.getPassword());
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public String getCreditCardNo() {
return creditCardNo;
}
}
Create the UserNotFoundException class:
public class UserNotFoundException extends RuntimeException {
}
Create the LoginSession class:
public class LoginSession {
private User user;

public void setUser(User user) {


this.user = user;
}
public User getUser() {
return user;
}
}
Create the various beans and hook them up:
<faces-config ...>
...
<managed-bean>
<managed-bean-name>loginRequest</managed-bean-name>
<managed-bean-class>shop.LoginRequest</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>userService</property-name>
<property-class>shop.UserService</property-class>
<value>#{userService}</value>
</managed-property>
Chapter 4 Creating an e-Shop 149

<managed-property>
<property-name>loginSession</property-name>
<property-class>shop.LoginSession</property-class>
<value>#{loginSession}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>loginSession</managed-bean-name>
<managed-bean-class>shop.LoginSession</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>userService</managed-bean-name>
<managed-bean-class>shop.UserService</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
...
</faces-config>
As the "loginSession" bean is in the session scope, all its fields must implement
Serializable:
public class LoginSession {
private User user;

public void setUser(User user) {


this.user = user;
}
public User getUser() {
return user;
}
}

So, modify the User class:


public class User implements Serializable {
private String email;
private String password;
private String creditCardNo;

public User(String email, String password, String creditCardNo) {


this.email = email;
this.password = password;
this.creditCardNo = creditCardNo;
}
public boolean matches(LoginRequest loginRequest) {
return email.equals(loginRequest.getEmail())
&& password.equals(loginRequest.getPassword());
}
}
Setup the navigation:
150 Chapter 4 Creating an e-Shop

Now, run the application and try to login using a valid account or an invalid
account. It should work.
There is a minor issue though: As shown below, the code to log an error
message is using JSF specific classes such as FacesContext and is thus
polluting this class:
public class LoginRequest {
private String email;
private String password;
private UserService userService;
private LoginSession loginSession;
...
public String login() {
try {
User user = userService.findMatchingUser(this);
loginSession.setUser(user);
return "loggedIn";
} catch (UserNotFoundException e) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Login failed", null));
return null;
}
}
}
You may put this code into an action listener like this:
public class LoginValidatingListener implements ActionListener {
private UserService userService;
private LoginRequest loginRequest;
...
public void processAction(ActionEvent event)
throws AbortProcessingException {
try {
userService.findMatchingUser(loginRequest);
} catch (UserNotFoundException e) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Login failed", null));
throw new AbortProcessingException();
}
}
}
<f:view>
<h1>Login</h1>
Chapter 4 Creating an e-Shop 151

<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Email:"></h:outputText>
<h:inputText value="#{loginRequest.email}"></h:inputText>
<h:outputText value="Password:"></h:outputText>
<h:inputSecret value="#{loginRequest.password}"></h:inputSecret>
<h:outputText></h:outputText>
<h:commandButton value="Login" action="#{loginRequest.login}">
<f:actionListener type="shop.LoginValidatingListener"/>
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
However, on one hand this is quite some extra work. On the other hand,
sometimes errors will occur only when you try to perform a business action. For
example, when withdrawing from an account, checking if the balance is enough
beforehand is useless due to concurrent accesses. Anyway, in this case you'll
simply leave it as is without using a separate action listener.

Implementing the checkout function


Now, let's work on the checkout function. Create confirm.jsp:
You'll create this "checkoutService"
bean
<f:view>
<h1>Confirm your order</h1>
<p>You're going to pay <h:outputText
value="#{checkoutService.total}"></h:outputText> with your credit card
<h:outputText value="#{loginSession.user.creditCardNo}"></h:outputText>.</p>
<h:form>
<h:commandButton value="Confirm" action="#{checkoutService.checkout}">
</h:commandButton>
<h:commandButton value="Continue shopping" action="catalog">
</h:commandButton>
</h:form>
Get the user from the login session
</f:view> and then get the credit card # from
the user

Create CheckoutService.java:
public class CheckoutService {
private Cart cart;
private Catalog catalog;

public void setCart(Cart cart) {


this.cart = cart;
}
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
}
public double getTotal() {
double total = 0;
for (String pid : cart.getProductIds()) {
total += catalog.getProduct(pid).getPrice();
}
return total;
}
public String checkout() {
// charge the user, schedule the delivery and etc.
return "charged";
}
152 Chapter 4 Creating an e-Shop

}
Define the "checkoutService" bean and hook up the beans:
<faces-config ...>
...
<managed-bean>
<managed-bean-name>checkoutService</managed-bean-name>
<managed-bean-class>shop.CheckoutService</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>cart</property-name>
<property-class>shop.Cart</property-class>
<value>#{cart}</value>
</managed-property>
<managed-property>
<property-name>catalog</property-name>
<property-class>shop.Catalog</property-class>
<value>#{catalog}</value>
</managed-property>
</managed-bean>
...
</faces-config>
Create the thankyou.jsp file:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
Thank you for your order!
</body>
</html>
Define the navigation:
Chapter 4 Creating an e-Shop 153

Now run it. Login, add some products, checkout and confirm. It should work
fine:
154 Chapter 4 Creating an e-Shop

What if the user hasn't logged in yet? In that case, you should send him to the
login page. After logging in, he should be returned to the confirm page
automatically. To do that, create a ForceLoginPhaseListener class:
Chapter 4 Creating an e-Shop 155

It will be called before and after a


certain phase. Which phase? It tells
the JSF Engine it is only interested in
the Render Response phase: About to render the
confirm page?
public class ForceLoginPhaseListener implements PhaseListener {
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE; The JSP engine is
} the view handler
public void beforePhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance(); Access the logged
String viewId = context.getViewRoot().getViewId(); in user
if (viewId.equals("/confirm.jsp")) {
Application app = context.getApplication();
LoginSession loginSession = (LoginSession) app
.evaluateExpressionGet(context, "#{loginSession}",
LoginSession.class);
if (loginSession.getUser() == null) {
ViewHandler viewHandler = app.getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, "/login.jsp");
context.setViewRoot(viewRoot);
Ask the view handler to View id
} build the JSF
} Tell the JSF engine to component tree
} render this view
public void afterPhase(PhaseEvent event) {
}
}

You need to register this phase listener with the JSF engine. To do that, open
faces-config.xml, choose the "Others" tab and click "Phase Listener":

Click here
156 Chapter 4 Creating an e-Shop

Click "Add" and specify your ForceLoginPhaseListener. If you're curious, you


can check the code in the "Source" tab:
<faces-config ...>
<managed-bean>
...
</managed-bean>
...
<navigation-rule>
...
</navigation-rule>
<lifecycle>
<phase-listener>shop.ForceLoginPhaseListener</phase-listener>
</lifecycle>
</faces-config>
That's it. Now try to go to the confirm page without logging in. It will send you to
the login page. But after logging in, how to return to the original page? In the
current case, there is only one protected page (the confirm page) so after
logging in, the login page should definitely return the user to the confirm page.
But in principle, there could be multiple protected pages such as a page
allowing the user to edit his profile (see the diagram below). Then after logging
in, the user should be returned to the original page he requested:

1: Try to render a
protected page
edit ...
confirm
profile

2: Redirected to the 3: Return to the


login page original requested
page

login

If you use the navigation system in JSF, you'll have to add one navigation case
for each protected page:
edit
confirm ...
profile

editProfile
checkout ...

login

It means whenever you have one more protected page, you'll have to add one
more navigation case, meaning that the login page will never become stable. To
solve this problem, you can remember the originally requested page in the
phase listener:
public class ForceLoginPhaseListener implements PhaseListener {
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
public void beforePhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
String viewId = context.getViewRoot().getViewId();
Chapter 4 Creating an e-Shop 157

if (viewId.equals("/confirm.jsp")) {
Application app = context.getApplication();
LoginSession loginSession = (LoginSession) app
.evaluateExpressionGet(context, "#{loginSession}", LoginSession.class);
if (loginSession.getUser() == null) {
loginSession.setOriginalViewId(viewId);
ViewHandler viewHandler = app.getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, "/login.jsp");
context.setViewRoot(viewRoot);
}
}
}
public void afterPhase(PhaseEvent event) {
}
}
Add this field in the LoginSession class:
public class LoginSession {
private User user;
private String originalViewId;

public void setUser(User user) {


this.user = user;
}
public User getUser() {
return user;
} Once it is read, clear it.
public String getOriginalViewId() {
String temp = originalViewId;
originalViewId = null;
return temp;
}
public void setOriginalViewId(String originalViewId) {
this.originalViewId = originalViewId;
}
}

Retrieve it after logging in:


It is possible that the user
public class LoginRequest { explicitly clicked the login link
... Retrieve the originally to go to the login page
public String login() { requested view id instead of being redirected to
try { here. In that case there is no
User user = userService.findMatchingUser(this); original view id (null).
loginSession.setUser(user);
String originalViewId = loginSession.getOriginalViewId();
if (originalViewId != null) {
FacesContext context = FacesContext.getCurrentInstance();
ViewHandler viewHandler = context.getApplication().getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, originalViewId);
context.setViewRoot(viewRoot); Create the component
return null; tree for the original
Tell the JSF
} else { page
engine to render it
return "loggedIn";
}
} catch (UserNotFoundException e) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Login failed", null));
return null;
} Tell the navigation system to
} NOT change the view root
}
158 Chapter 4 Creating an e-Shop

Now restart the browser to get rid of the session. Then run the application
again. Try to checkout without logging in. It should ask you to login and then
return you to the confirm page:

Implementing logout
Suppose that you'd like to allow the user to logout:

Download at WoweBook.com
Chapter 4 Creating an e-Shop 159

The minimum that you need to do is to remove the User object from the
"loginSession" bean. However, a better way is to delete the session altogether
(including the shopping cart, for example). To do that, modify showcatalog.jsp:
<f:view>
<h1>Product listing</h1>
<h:dataTable border="1" value="#{catalog.products}" var="p">
... It stands for non-breaking space. That is, a space
</h:dataTable> between the two links.
<h:form>
<h:commandLink action="login">Login</h:commandLink>
&nbsp;
<h:commandLink>
<h:outputText value="Logout"></h:outputText>
<f:actionListener type="shop.LogoutActionListener"/>
</h:commandLink>
</h:form> Remove the session in this action listener. Why
</f:view> not specify a method in the "action" attribute? You
could do that but removing the session is a UI
Do not set the outcome ("action")
specific task, not a business task. So an action
so that it remains on the catalog
listener is better.
page

Create the LogoutActionListener class:


160 Chapter 4 Creating an e-Shop

The external context means the


platform JSF engine is running on.
In this case, it's Tomcat.

public class LogoutActionListener implements ActionListener {


public void processAction(ActionEvent event)
throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
Object session = externalContext.getSession(false);
HttpSession httpSession = (HttpSession) session;
httpSession.invalidate();
} Get access to the session. The session
} Remove the session is maintained by the platform (Tomcat).

The session is an Object, not an HttpSession.


This is because JSF could potentially run on a
platform that doesn't use HTTP. Here you're
sure it is using HTTP, so you can typecast it.

Run it. Login and then logout. Then try to checkout and it should ask you to
login again.

Summary
A facet is a child component that is subjected to special processing by its
parent.
A request has a table of attributes. Each attribute has a name and a value
(Object). It allows you to give a name to an object. It is like request-scoped
managed bean except that it doesn't create the object; it only associates it with
a name.
To loop some items in a table, if the number of items can only be determined at
runtime, use the UI Data component. You provide a List of items to it and it will
loop through it for each item. Its children must be UI Column components. Each
UI Column represents a column in the table. For each item, the UI Data will
store the item into an attribute and ask each UI Column to render for it. Each UI
Column will render its own child components (excluding the "header" facet). A
UI Column can optionally have a facet named "header". In that case, the UI
Data will ask the UI Columns to render an extra header row before it starts to
process the data rows.
On form submission, the UI Data will loop through the items again, giving each
component inside an opportunity to apply request values, process validations,
update domain values and invoke application in each respective phase.
As one component inside the UI Data will render multiple times and generate
multiple HTML elements, the UI Data will pretend to have a different client id for
each item so that the component will have a unique client id for each item.
Because the current item is stored into an attribute, not a bean, if you need to
Chapter 4 Creating an e-Shop 161

pass it onto the next page, most likely you'll want to use the Set Property action
listener.
To create a link, use the UI Command component with a link renderer. In terms
of behavior, it is just like a UI Command component with a button renderer.
For a UI Command, in addition to setting an outcome in its "action" attribute,
you can also specify a method. This is useful when you need to perform a
business action. That method should return a string indicating the outcome. If
you need to perform a UI specific action, it's better to add an action listener to
the UI Command.
A UI Input can be rendered such that user input is echoed as stars. This is good
for password input.
If you display the properties of a request-scoped bean using a page, you must
be careful when the form is submitted because a new request-scoped bean will
be created and its id will not have been set. Usually you will use a UI Input
component along with an HTML Hidden renderer to store the id in the browser
as a hidden field. You need to load the other fields when the id is set.
The JSF engine uses a view handler to create the JSF component tree from a
specified view id. By default the JSP engine is the view handler. You need to
use a view handler when you want to by-pass the JSF navigation system: You
need to load a view and set it as the one to be render.
A session is identified by the session stored in a cookie in the browser. To start
a new session, either restart the browser or delete that cookie. To remove the
session on the server, call invalidate() on the session. This is commonly done
when logging out. All session scoped managed beans must implement
Serializable. Injected fields should be marked as transient.
You should never inject any managed bean into a session scoped bean, as
after deseralization the field will become null.
A phase listener will be notified before entering a phase or after exiting from a
phase. You can use it to make sure the user has logged in before rendering a
certain pages.
The external context means the platform on which the JSF engine is running. In
your case it is Tomcat. JSF assumes this platform is responsible for maintaining
the session.
163

Chapter 5
Chapter 5 Building Interactive Pages
with AJAX
164 Chapter 5 Building Interactive Pages with AJAX

What's in this chapter?


In this chapter you'll learn how to build pages that are more interactive than
normal HTML forms using a technique called AJAX.

A sample AJAX application


Suppose that you'd like to develop an application that allows the user to view a
list of FAQ like this:

If the user clicks on a question, its answer will be shown instantly, while the rest
of page is not refreshed:

If he clicks that question again, the answer will be hidden again. As a first step,
you'll show a single question only. To do that, create a Dynamic Web Project
named FAQ with JSF enabled. Create a listfaq.jsp page:
Chapter 5 Building Interactive Pages with AJAX 165

...
<f:view> You'll trigger the display of the answer
<div> in this method
<h:form>
<h:commandLink action="#{faqService.trigger}">
<h:outputText value="#{faqService.questionText}">
</h:outputText>
</h:commandLink>
</h:form>
</div>
<div>
<h:outputText
value="#{faqService.answerText}"
rendered="#{faqService.expanded}">
</h:outputText>
</div> You'll create an isExpanded() method. If it returns
</f:view> true, the component will render itself, otherwise it
won't. If the "rendered" attribute is not set, it is
... assumed to be true.

Create the FAQService class in the faq package:


public class FAQService {
private String questionText = "How to run Eclipse?";
private String answerText = "Double click its icon.";
private boolean isExpanded = false;

public String getQuestionText() { At the beginning, don't show the


return questionText; answer.
}
public String getAnswerText() {
return answerText;
} Return null so that the current page is
public String trigger() { redisplayed
isExpanded = !isExpanded;
return null;
}
public boolean isExpanded() {
return isExpanded;
}
}

Create a bean for it. As it must remember the expanded status, you need to use
the session scope:
<faces-config ...>
<managed-bean>
<managed-bean-name>faqService</managed-bean-name>
<managed-bean-class>faq.FAQService</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>
Therefore, the class must implement Serializable:
public class FAQService implements Serializable {
...
}
Add the project as a module to the Tomcat instance and then run it. It should
work:
166 Chapter 5 Building Interactive Pages with AJAX

Refreshing the question only


Next, you'd like to refresh that question only, not the whole page. To do that
(see the diagram below), you need to generate an HTML element enclosing the
question and answer. Suppose that it's generated by a component whose id is
"qa" so its client id is also "qa" as it is not in a form. Make sure some Javascript
is generated for the onclick event of the link (the <a> element). When the link is
clicked, that Javascript will send a request to your application, which will reverse
the visibility of the answer and then render the "qa" component only (not the
whole page). It will generate an HTML element whose HTML id is its client id
("qa"). Your application will then send this element back to the Javascript in the
browser. The Javascript will look up an element in the existing HTML page
whose HTML id is "qa" and replace it:
Chapter 5 Building Interactive Pages with AJAX 167

1: Make sure there is an HTML element


enclosing the whole question and
answer

2: The link is clicked. The Javascript


sends a request to your application.
Your app
<html>
<span id="qa">
<div>
<a onclick="some Javascript">How to...</a>
</div>
</span> 3: Only render the
</html> component whose id
is "qa". This time,
the answer is
displayed. The client
4: Return this HTML element to the id "qa" is used as
Javascript. It will look up the the HTML id.
element with "qa" as the ID and
replace it.
<span id="qa">
<div>
<a onclick="...">How to...</a>
</div>
<div>Double click...</div>
</span>

To implement this idea, you need to use the JBoss RichFaces components. So,
modify web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
<display-name>FAQ</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>org.richfaces.SKIN</param-name>
<param-value>blueSky</param-value>
</context-param>
<filter>
<display-name>RichFaces Filter</display-name>
<filter-name>richfaces</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>richfaces</filter-name>
<servlet-name>Faces Servlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
168 Chapter 5 Building Interactive Pages with AJAX

<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
</web-app>
Copy the required jars files into WEB-INF/lib:

Modify listfaq.jsp:
Use the <commandLink> <a onclick="some Javascript">How to...</a>
in the Ajax4Jsf tag lib 3: Render this "qa"
component only

<%@taglib prefix="a4j" uri="http://richfaces.org/a4j"%>


... 1: It will set the
<f:view> "onclick" event
<h:panelGroup id="qa"> handler to 2: If clicked, it will still call this
<div> some trigger() method in your
<h:form> Javascript application
<a4j:commandLink
action="#{faqService.trigger}"
reRender="qa">
<h:outputText value="#{faqService.questionText}"></h:outputText>
</a4j:commandLink>
</h:form>
</div>
<div>
<h:outputText
value="#{faqService.answerText}"
rendered="#{faqService.expanded}">
</h:outputText>
</div>
</h:panelGroup>
</f:view>

Run it and it should work.


Chapter 5 Building Interactive Pages with AJAX 169

Refreshing the answer itself


At the moment you're refreshing the question and the answer. However, is it
possible to refresh just the answer? At present, if the answer component is not
rendered, it will not output any HTML element. Then there will be no HTML
element to replace! To solve this problem, you can put it inside a <panelGroup>
and re-render that <panelGroup> instead:
<f:view>
<h:panelGroup id="qa">
<div>
<h:form>
<a4j:commandLink
action="#{faqService.trigger}"
reRender="qa answerPanel">
<h:outputText value="#{faqService.questionText}"></h:outputText>
</a4j:commandLink>
</h:form>
</div>
<div>
<h:panelGroup id="answerPanel">
<h:outputText
value="#{faqService.answerText}"
rendered="#{faqService.expanded}">
</h:outputText>
</h:panelGroup>
</div>
</h:panelGroup>
</f:view>
Now run it and it should continue to work.

Giving rating to a question


Suppose that you'd like to allow the user to rate the helpfulness of the question
(and its answer). The average rating so far is displayed at the end of the
question:

Again, you don't want to refresh the whole page, but just the relevant parts. This
is just like the <a4j:commandLink>, but now you need a button. This is done by
using an <a4j:commandButton>:
170 Chapter 5 Building Interactive Pages with AJAX

<f:view>
<div> Put the form to the right
<h:form>
<a4j:commandLink action="#{faqService.trigger}" reRender="answerPanel">
<h:outputText
value="#{faqService.questionText} (#{faqService.averageRating})"
id="qt">
</h:outputText> This part is just a literal string Literal string again
</a4j:commandLink> which will be output as is
</h:form>
<h:form style="float:right">
<h:inputText size="2" value="#{faqService.rating}"></h:inputText>
<a4j:commandButton
value="Rate" Refresh this component
action="#{faqService.rate}"
reRender="qt">
</a4j:commandButton>
</h:form> <commandButton> from the Ajax4Jsf tag lib. It will
</div> put Javascript into the onclick handler of the HTML
<div> submit button.
<h:panelGroup id="answerPanel">
<h:outputText
value="#{faqService.answerText}"
rendered="#{faqService.expanded}">
</h:outputText>
</h:panelGroup>
</div>
</f:view>

Modify the FAQService class:


public class FAQService implements Serializable {
private String questionText = "How to run Eclipse?";
private String answerText = "Double click its icon.";
private boolean isExpanded = false;
private int rating;
private int noRatings = 0;
private int totalRating = 0;

public String getQuestionText() {


return questionText;
}
public String getAnswerText() {
return answerText;
}
public String trigger() {
isExpanded = !isExpanded;
return null;
}
public boolean isExpanded() {
return isExpanded;
}
public String rate() {
totalRating += rating;
noRatings++;
return null;
}
public int getAverageRating() {
return noRatings == 0 ? 0 : totalRating / noRatings;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
Chapter 5 Building Interactive Pages with AJAX 171

}
Now run it and it should work:

What if the user enters something invalid such as "abc" as the rating? Then it
should display an error. To do that, modify listfaq.jsp:
<messages> will render nothing if there is no error.
Then later you'll be unable to refresh the HTML
element. So, put it inside a <panelGroup>.
<f:view>
<h:panelGroup id="msgs">
<h:messages></h:messages>
</h:panelGroup>
<div>
<h:form>
<a4j:commandLink action="#{faqService.trigger}" reRender="answerPanel">
<h:outputText
value="#{faqService.questionText} (#{faqService.averageRating})"
id="qt">
</h:outputText>
</a4j:commandLink>
</h:form> Make the error message look better
<h:form style="float:right">
<h:inputText
label="rating" size="2" value="#{faqService.rating}"></h:inputText>
<a4j:commandButton
value="Rate"
action="#{faqService.rate}"
reRender="qt,msgs">
</a4j:commandButton>
Refresh both the question text and
</h:form>
the error messages
</div>
<div>
<h:panelGroup id="answerPanel">
<h:outputText
value="#{faqService.answerText}"
rendered="#{faqService.expanded}">
</h:outputText>
</h:panelGroup>
</div>
</f:view>

Now run it and it should work:


172 Chapter 5 Building Interactive Pages with AJAX

What if you'd like to submit the form when the user moves the mouse over the
Rate button (even without clicking it)? You can do it this way:
<f:view>
<h:panelGroup id="msgs">
<h:messages></h:messages>
</h:panelGroup>
<div>
<h:form>
<a4j:commandLink action="#{faqService.trigger}" reRender="answerPanel">
<h:outputText
value="#{faqService.questionText} (#{faqService.averageRating})"
id="qt">
</h:outputText>
</a4j:commandLink>
</h:form> No need for any
<h:form style="float:right"> behavior
<h:inputText
label="rating" size="2" value="#{faqService.rating}"></h:inputText>
<h:commandButton value="Rate" action="..." reRender="...">
<a4j:support
Use a normal JSF event="onmouseover" 1: Generate the <input> element
commandButton action="#{faqService.rate}"
reRender="qt,msgs"> <input
</a4j:support> type="submit"
</h:commandButton> onmouseover="some Javascript">
</h:form> 4: Refresh the components
</div>
2: Set the onmouseover event
<div>
handler
<h:panelGroup id="answerPanel">
<h:outputText
value="#{faqService.answerText}" 3: When the mouse is over the
rendered="#{faqService.expanded}"> submit button, send a request to
</h:outputText> your application calling this
</h:panelGroup> method.
</div>
</f:view>

Now run it and it should work.

Using a modal panel to get the rating


The form is making the screen too crowded. Therefore, you'd like to change it
Chapter 5 Building Interactive Pages with AJAX 173

like this (see the screen shot below): The user can click a "Rate" link. Then it
will open a modal panel to show the form to allow him to enter the rating:

After closing the form, the average rating for the question will be refreshed.
Now, let's do it. Modify listfaq.jsp:
174 Chapter 5 Building Interactive Pages with AJAX

<%@taglib prefix="rich" uri="http://richfaces.org/rich"%>


...
<f:view>
<h:panelGroup id="msgs">
<h:messages></h:messages> When the user clicks on this link, this
</h:panelGroup> Javascript will execute and will show
<div> the modal panel
<h:form>
<a4j:commandLink action="#{faqService.trigger}" reRender="answerPanel">
<h:outputText
value="#{faqService.questionText} (#{faqService.averageRating})"
id="qt">
</h:outputText> Component id
</a4j:commandLink>
<a href="javascript:Richfaces.showModalPanel('mp')">Rate</a> This is the modal
</h:form> panel. Initially it
<rich:modalPanel id="mp"> is hidden.
<f:facet name="header">
<h:outputText value="Enter a rating"></h:outputText> The header of the modal
</f:facet> panel. It is optional.
<h:form style="float:right">
<h:inputText
label="rating" size="2" value="#{faqService.rating}"></h:inputText>
<h:commandButton value="Rate">
<a4j:support This Javascript will be executed after
event="onmouseover" the HTML elements have been
refreshed. Here, you will hide the
action="#{faqService.rate}"
modal panel.
reRender="qt,msgs"
oncomplete="Richfaces.hideModalPanel('mp')">
</a4j:support>
</h:commandButton>
</h:form>
</rich:modalPanel>
</div>
<div> Everything here will appear
<h:panelGroup id="answerPanel"> in the modal panel
<h:outputText
value="#{faqService.answerText}"
rendered="#{faqService.expanded}">
</h:outputText>
</h:panelGroup>
</div>
</f:view>

Now run it and it should work. For the moment, if the user enters some
garbage, it will close the modal panel and display the error in the main page.
What if you'd like to display the error in the modal panel and not close it as
shown below?
Chapter 5 Building Interactive Pages with AJAX 175

To do that, modify the code:


<f:view>
<h:panelGroup id="msgs">
<h:messages></h:messages>
</h:panelGroup>
<div>
<h:form>
<a4j:commandLink action="#{faqService.trigger}" reRender="answerPanel">
<h:outputText
value="#{faqService.questionText} (#{faqService.averageRating})"
id="qt">
</h:outputText>
</a4j:commandLink>
<a href="javascript:Richfaces.showModalPanel('mp')">Rate</a>
</h:form>
<rich:modalPanel id="mp">
<f:facet name="header">
Move the <h:outputText value="Enter a rating"></h:outputText>
messages </f:facet>
to there <h:panelGroup id="msgs"> Check if there is any actual
<h:messages id="m"></h:messages> error message, if no, hide the
</h:panelGroup> modal panel. If yes, do
<h:form style="float:right"> nothing so it remains visible.
<h:inputText
label="rating" size="2" value="#{faqService.rating}"></h:inputText>
<h:commandButton value="Rate">
<a4j:support
event="onmouseover"
action="#{faqService.rate}"
reRender="qt,msgs"
oncomplete="if (document.getElementById('m')==null)
Richfaces.hideModalPanel('mp')">
</a4j:support>
</h:commandButton>
</h:form>
</rich:modalPanel>
</div>
...
</f:view>

Run it and it should work.


176 Chapter 5 Building Interactive Pages with AJAX

Finally, the modal panel may be a bit too large. You can set its initial size to say
200 pixels x 100 pixels:
<rich:modalPanel id="mp" width="200" height="140">
...
</rich:modalPanel>
Run it and the panel should be smaller.

Setting the look and feel with skins


All RichFaces components supports so-called skins. For example, you can
define a skin as:
Skin name: s1
text font: Arial
text color: blue
text size: 12pt
background color: yellow
...

RichFaces comes with several pre-defined skins. They're named: blueSky,


classic, deepMarine and etc. To choose which one to use, do it in web.xml:
<web-app ...>
...
<context-param>
<param-name>org.richfaces.SKIN</param-name>
<param-value>blueSky</param-value>
</context-param>
...
</web-app>
Of course, the normal JSF components won't use the skin. To solve the
problem, put everything inside a <rich:panel>:
<f:view>
<rich:panel>
<div>
...THE QUESTION AND THE MODAL PANEL...
</div>
<div>
...THE ANSWER...
</div>
</rich:panel>
</f:view>
The <rich:panel> will generate a <div> like:
Chapter 5 Building Interactive Pages with AJAX 177

foo {
font-family: Arial;
color: ...;
}

foo is a CSS class whose definition The value comes


is generated at runtime: the skin

<div class="foo">
<span...>
<form...>
<input...>
</div>

These elements are generated by the


standard JSF components. They don't
have CSS class applied so they will
inherit the look & feel from the <div>.
Skin name: s1
text font: Arial
text color: blue
text size: 12pt
background color: yellow
...

Now run it again and you should notice that font has changed:

Summary
AJAX means that when a certain event occurs in the browser, a request is sent
to the application so that it can perform some action and then only parts of a
page are refreshed. You can use an <a4j:commandLink> for a link, an
<a4j:commandButton> for a button or an <a4j:support> for any other events. In
these tags, you also specify an action method to execute in the application and
a list of component ids which are to be refreshed.
A component can be excluded from rendering. In that case, normally it will
generate nothing. If you need to show it using AJAX, you can put it inside a
panel. Similarly, some components such as the UI Messages may output
nothing in normal use. To update them using AJAX, put them inside a panel.
178 Chapter 5 Building Interactive Pages with AJAX

You can show or hide a modal panel using Javascript.


A skin defines a look and feel including font family, font size, color and etc. All
RichFaces components supports skins. It works by creating and using CSS
classes at runtime.
179

Chapter 6
Chapter 6 Using Facelets
180 Chapter 6 Using Facelets

What's in this chapter?


In this chapter you'll learn how to use a very useful JSF technology called
Facelets.

Setting up IDE support for Facelets


Eclipse has no built-in support for Facelets. Fortunately, there is a 3rd party
plugin that does, which is called the JBoss IDE tools. To install it, in Eclipse,
choose "Help | Software Updates | Find and Install", choose "Search for new
features to install". Click "New Remote Site" and enter the information as shown
below:

Later, choose to install the JBoss IDE:

Continue to finish the installation.


Chapter 6 Using Facelets 181

Creating a Facelets project


Now, let's implement the FAQ page using Facelets. In Eclipse, choose "File |
New | Others", then choose "JBoss Tools Web | JSF | JSF Project":

Click Next. Then enter the information as shown below:


182 Chapter 6 Using Facelets

Any project name will do

Choose this to enable Facelets support in


this project

It won't put the JSF jars into your WEB-INF/lib.


It means no sample It's OK, you'll copy the files there yourself.
code

Click Next. It will suggest to add this project as a module to the Tomcat server
instance:
Chapter 6 Using Facelets 183

This is what you want. Click Finish. It will suggest switching to the Web
Development perspective (provided by the JBoss IDE Tools). Say Yes. Now the
project is created. Copy the JSF RI jar files and those of RichFaces into WEB-
INF/lib:

Next, right click the WebContent folder and choose "New | XHTML File". Enter
listfaq as the name:
184 Chapter 6 Using Facelets

Click Next. It will then asks you what tag libs you'd like to use in this page.
Choose as shown below:

Click Finish. Then you'll see a visual page editor very much like the one you've
been using:
Chapter 6 Using Facelets 185

This is the source code

The palette

This is the visual editing area

In addition, the file is a strict XML file (a JSP file is not XML) and a tag lib is
used as an XML namespace:
186 Chapter 6 Using Facelets

listfaq.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j">
</html>

It is a prefix for an XML


namespace
Those are not allowed in
XML!
listfaq.jsp
<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
...
<f:view>
...

"f" is not a prefix for an


XML namespace

Why is being strict XML significant? It means you can use the many XML
editors to edit the file.
Now modify the file either by entering the code or using drag and drop. Content
assist will work as usual:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j">
<body>
<div>
<h:form>
<h:commandLink
value="#{faqService.questionText}" action="#{faqService.trigger}">
</h:commandLink>
</h:form>
</div>
<div>
<h:outputText
value="#{faqService.answerText}" rendered="#{faqService.expanded}">
</h:outputText>
</div>
</body>
</html>
Copy the FAQService class from the previous chapter. Create the bean
definition for it. Double click on the faces-config.xml file, the JBoss XML Editor
will open:
Chapter 6 Using Facelets 187

Choose "Managed Beans" on the left and then click "Add" to create a session
scoped bean:

Go ahead to finish it. Start the Tomcat instance and try to access it. Here is the
URL:
188 Chapter 6 Using Facelets

2: You handle it
Tomcat JSF engine

4: Replace the
1: Look, jsf extension. extension with
Context path, as usual. xhtml so it
gets /listfaq.xhtml
as the view id. Ask
http://localhost:8080/FAQFacelets/listfaq.jsf the view handler to
load the view.

3: What's the path? Oh,


it's /listfaq.jsf.
Facelet
engine

WebContent

listfaq.xhtml
5: Use the view id as a
... relative path to read the
file and create a
component tree

Run it and it should work. However, you may ask, so what? It is not that
different from using JSP. Read on.

Creating your own tag


Before listing multiple questions, it would be great if you had a tag such as <qa>
that will display a question and its answer. Then you would use it like:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:foo="http://foo.com">
<body> This namespace represents your tag lib
<h:dataTable value="#{faqService.questions}" var="q">
<h:column>
<foo:qa question="#{q}"/>
</h:column>
</h:dataTable>
</body> Assume that each question object
</html> contains the question text, the
answer text and the rating
information.

To create such a Facelet tag (and the tag lib), create a META-INF folder in your
Java source folder and then create a file foo.taglib.xml in it (The file name is
unimportant as long as it ends with .taglib.xml). The content is like:
Chapter 6 Using Facelets 189

A tag lib is identified by a URL.


Here you use this URL for your
tag lib.

Define a Facelet tag lib

<!DOCTYPE facelet-taglib PUBLIC


"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib xmlns="http://java.sun.com/JSF/Facelet" >
<namespace>http://foo.com</namespace>
<tag> The XML elements used here to
<tag-name>qa</tag-name> define your own tag (e.g.,
<source>qa.xhtml</source> <facelet-taglib> and <tag>) are all
</tag> in this facelet namespace
</facelet-taglib>
The tag is <qa>
Define one tag here. You could
define many tags in a tag lib.
What does the <qa> tag mean? You'll
define its meaning using the file
qa.xhtml. This is a relative path to this
taglib.xml file.

Create the qa.xhtml in the same META-INF folder:


This is the Facelet tag lib. It provides
tags such as <component> to let you
define your own tags.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"


"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core" This is the meaning of the <qa> tag.
xmlns:h="http://java.sun.com/jsf/html" That is, whenever the Facelet engine
xmlns:ui="http://java.sun.com/jsf/facelets" sees a <qa> element, it will trace into
xmlns:a4j="http://richfaces.org/a4j"> here and create a JSF subtree:
<body>
<ui:component>
<div> Assume that there is an attribute "q" <f:panelGroup>
<h:form> containing the Question object <foo:qa/>
<h:commandLink </f:panelGroup>
value="#{q.questionText}" action="#{q.trigger}">
</h:commandLink>
</h:form>
</div> UI Panel
<div>
<h:outputText
value="#{q.answerText}" rendered="#{q.expanded}"> Dummy
</h:outputText>
</div> UI Output
</ui:component> <div>
</body>
</html> UI Form
Everything outside will not go into the JSF component
tree and thus will have no effect on the output. The
code is there so that it looks good in a visual editor. ...

Create the Question class. It is just a copy of the FAQService class:


package faq;
190 Chapter 6 Using Facelets

public class Question implements Serializable {


private String questionText = "How to run Eclipse?";
private String answerText = "Double click its icon.";
private boolean isExpanded = false;
private int rating;
private int noRatings = 0;
private int totalRating = 0;

public String getQuestionText() {


return questionText;
}
public String getAnswerText() {
return answerText;
}
public String trigger() {
isExpanded = !isExpanded;
return null;
}
public boolean isExpanded() {
return isExpanded;
}
public String rate() {
totalRating += rating;
noRatings++;
return null;
}
public int getAverageRating() {
return noRatings == 0 ? 0 : totalRating / noRatings;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
}
Modify the FAQService class:
package faq;

public class FAQService implements Serializable {


private Question q1 = new Question();

public Question getQ1() {


return q1;
}
}
Modify listfaq.xhtml:
Chapter 6 Using Facelets 191

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"


"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core" 4: When the tree is
xmlns:h="http://java.sun.com/jsf/html" Dummy rendered
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:foo="http://foo.com"
UI Form
xmlns:a4j="http://richfaces.org/a4j"> qa.xhtml
<body> ...
<foo:qa q="#{faqService.q1}" /> UI
</body> Command
</html>
value: #{q.questionText}
1: Need to trace into its definition to build
the tree
action: ...

Facelet 3: Trace into the definition to build the tree


engine
2: Build a table for variables for each 5: It will look up this variable
attribute in the tag ("q" here). Evaluate the table first before trying the
expression to get the value. beans and etc.

Nam e Value
q q1
...
...

Now run it and it should continue to work.


How is this better than JSP? First, with JSP you can also create your own tag,
but to define the meaning of the tag, you have to create a Java class and can't
reuse the existing tags. In contrast, with Facelets, you can use existing tags to
define the new tag. This is a huge difference.
Second, a JSP tag can access its attributes such as "q" above at runtime, but
they will NOT be available when the JSF component tree is rendered. To create
such a variable, it may have to use a request attribute or a bean, but then it will
become a global entity that may clash with those from other tags.
Due to these reasons, Facelets is generally considered a better technology than
JSP to be used with JSF.
However, it does have some drawbacks. First, Facelets is not in the JSF
standard (even though it seems becoming a de-facto industry standard).
Second, for example, even though you're using the familiar tags such as
<h:inputText> in your Facelets application, it is not the same <h:inputText> you
used in your JSP applications. It was rewritten from scratch while making sure
they look the same. It means if you buy/download a component library, you
have to check if it supports Facelets. For example, RichFaces does.

Hiding JSF tags into HTML tags


Currently the qa.xhtml file contains many JSF tags such as <h:form>. It means
it is impossible to edit the file using say Dreamweaver. To solve this problem,
Facelets allows you to hide a JSF tag into an HTML tag like:
192 Chapter 6 Using Facelets

Now it is a HTML <a> tag

The other attributes are


jsfc stands for JSF component unchanged
<html xmlns="http://www.w3.org/1999/xhtml" ...>
<body>
<ui:component> <a jsfc="h:commandLink" value="..." action="...">
<div>
<h:form> The real tag name is here
<h:commandLink
value="#{q.questionText}" action="#{q.trigger}">
</h:commandLink>
</h:form>
</div> </a>
<div>
<h:outputText
value="#{q.answerText}" rendered="#{q.expanded}">
</h:outputText>
</div>
</ui:component>
</body>
</html>

Therefore, you can modify the qa.xhtml file like:


<html xmlns="http://www.w3.org/1999/xhtml" ...>
<body>
<ui:component>
<div>
<form jsfc="h:form">
<a jsfc="h:commandLink"
value="#{q.questionText}" action="#{q.trigger}">
</a>
</form>
</div>
<div>
<span jsfc="h:outputText"
value="#{q.answerText}" rendered="#{q.expanded}">
</span>
</div>
</ui:component>
</body>
</html>
You can do the same thing to Facelets tags such as <ui:component> too:
<html xmlns="http://www.w3.org/1999/xhtml" ...>
<body>
<span jsfc="ui:component">
<div>
<form jsfc="h:form">
<a jsfc="h:commandLink"
value="#{q.questionText}" action="#{q.trigger}">
</a>
</form>
</div>
<div>
<span jsfc="h:outputText"
value="#{q.answerText}" rendered="#{q.expanded}">
</span>
</div>
</span>
</body>
</html>
You can do the same thing for your <qa> too. For example, in listfaq.xhtml:
Chapter 6 Using Facelets 193

<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:foo="http://foo.com">
<body>
<span jsfc="foo:qa" q="#{faqService.q1}" />
</body>
</html>
Run it and it will continue to work.

Forbidding direct access to xhtml files


Let's do an experiment: Try to access http://localhost:
8080/FAQFacelets/listfaq.xhtml (NOT listfaq.jsf). You'll get a blank page like:

If you view the HTML source code in the browser, you'll see:

It means the source code of your web pages is being exposed! This is a serious
security problem. To fix it, modify web.xml:
194 Chapter 6 Using Facelets

<web-app ...>
...
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
It represents some "protected"
<servlet-name>Faces Servlet</servlet-name> files. That is, those files can be
<url-pattern>*.jsf</url-pattern> accessed by a certain users
</servlet-mapping> only.
<security-constraint>
<display-name>Block browser access to xhtml</display-name>
<web-resource-collection>
<web-resource-name>xhtml files</web-resource-name>
<url-pattern>*.xhtml</url-pattern>
</web-resource-collection>
<auth-constraint> Here, all *.xhtml are
</auth-constraint> protected.
</security-constraint>
...
</web-app> Usually here you would list the user
groups, but in this you don't want
anyone to have access, so leave it
empty.

Restart the application and try to access the file again. This time you will get an
error:

This is good. Of course, you can still access it as listfaq.jsf.


Chapter 6 Using Facelets 195

Summary
Facelets is a view handler that can replace the JSP engine in a JSF application.
It allows you to easily create new tags using existing tags. In addition, it allows
you to hide JSF and Facelets tags into normal HTML tags so that you can work
on the pages using web authoring tools such as Dreamweaver.
By default users can retrieve your xhtml files in the browser. This is a security
problem. You should block such direct accesses using web.xml.
Facelets is a recommended technology by many JSF developers.
As Facelets is not in the JSF standard, JSF component libraries may not
support it.
197

Chapter 7
Chapter 7 Providing a Common Layout
with Facelets
198 Chapter 7 Providing a Common Layout with Facelets

What's in this chapter?


In this chapter you'll learn how to create pages that share a common layout
using Facelets.

Providing a common layout


Suppose that you'd like to develop an application shown below. It is unimportant
what it does. What is important is that on each page there is a menu on the left:

To do that, create a new Facelets project named "Layout". Copy the two JSF jar
files into WEB-INF/lib. Create three pages: home.xhtml, products.xhtml and
contact.xhtml. home.xhtml and products.xhtml may be like:
Chapter 7 Providing a Common Layout with Facelets 199

home.xhtml products.xhtml
<html ...> <html ...>
<span jsfc="h:form"> <span jsfc="h:form">
<table> Must not use <br> <table>
<tr> as XML requires a <tr>
closing tag <td width="40%">
<td width="40%">
<a jsfc="h:commandLink" <a jsfc="h:commandLink"
action="home">Home</a> action="home">Home</a>
<br/> <br/>
<a jsfc="h:commandLink" <a jsfc="h:commandLink"
action="products">Products</a> action="products">Products</a>
<br/> <br/>
<a jsfc="h:commandLink" <a jsfc="h:commandLink"
action="contact">Contact</a> action="contact">Contact</a>
</td> </td>
<td>This is the Home page.</td> <td>This is the Products page.</td>
</tr> </tr>
</table> </table>
</span></html> </span></html>

Unique content

Or graphically, the situation is like what is shown below, the HTML page
structures are the same, the only difference is the cell content:
Html Html

Form Form
Table Table

Row Row
Cell Cell Cell Cell

Content unique Content unique


Menu to the Home Menu to the Products
page page

In that case, you can create a page to contain the common structure (see the
diagram below). The varying parts are left as abstract. Then let each page
extend this page and provide its unique content, just like Java class inheritance:

Base
Abstract

Home Products
Unique content Unique content

To do that, create base.xhtml (also in the WebContent folder):


200 Chapter 7 Providing a Common Layout with Facelets

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<span jsfc="h:form">
<table> The <insert> tag is a Facelets tag
<tr>
<td width="40%">
<a jsfc="h:commandLink" action="home">Home</a> <br/>
<a jsfc="h:commandLink" action="products">Products</a> <br/>
<a jsfc="h:commandLink" action="contact">Contact</a>
</td>
<td>
<span jsfc="ui:insert">unique content</span>
</td>
</tr>
It indicates that this is an abstract part
</table>
and the concrete part will be inserted.
</span>
</html>

Modify home.xhtml to "inherit" base.xhtml:


<html xmlns="http://www.w3.org/1999/xhtml" Everything outside the
xmlns:f="http://java.sun.com/jsf/core" <composition> element
xmlns:h="http://java.sun.com/jsf/html" will NOT be output
xmlns:ui="http://java.sun.com/jsf/facelets">
<span jsfc="ui:composition" template="/base.xhtml">
This is the home page.
</span>
</html>

Include a certain page This path starts from the


Everything outside the
(here /base.xhtml) context root folder
<composition> element
(WebContent)
will NOT be output
The tag body is the
concrete part

products.xhtml is:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<span jsfc="ui:composition" template="/base.xhtml">
This is the Products page.
</span>
</html>
Run it and it should work (except for the links). To create the links, you may
define the navigation rules like:
Chapter 7 Providing a Common Layout with Facelets 201

/home.xhtml

/home.xhtml
home

/products.xhtml
products

/contact.xhtml
contact

/products.xhtml

/home.xhtml
home

/products.xhtml
products

/contact.xhtml
contact

However, it is a lot of duplication. A better way is:


A star will match
any view id

/home.xhtml
home

/products.xhtml
products

/contact.xhtml
contact

To do that, modify faces-config.xml:


202 Chapter 7 Providing a Common Layout with Facelets

The source is:


<?xml version="1.0" encoding="UTF-8"?>
<faces-config ...>
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>products</from-outcome>
<to-view-id>/products.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>home</from-outcome>
<to-view-id>/home.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
...
</faces-config>
Restart the application and the links should work fine.

Having two abstract parts


Suppose that each page may need to have a particular header that may contain
any HTML elements or even JSF components:
Chapter 7 Providing a Common Layout with Facelets 203

This header may contain


markup such as <h1> or
even components

Now, the situation is like:


base.xhtml

Base Abstract2

Abstract1

home.html Home Products products.xhtml

Unique content 2 Unique content 2

Unique content 1 Unique content 1

This is like a base class having two abstract methods. For this to work, you
need to give a name to each abstract part:
204 Chapter 7 Providing a Common Layout with Facelets

Let's name it "p1".


You can use any
name you'd like.
<html ...>
<span jsfc="ui:insert" name="p1">foo</span>
<span jsfc="h:form">
<table>
<tr>
<td width="40%">
<a jsfc="h:commandLink" action="home">Home</a> <br/>
<a jsfc="h:commandLink" action="products">Products</a> <br/>
<a jsfc="h:commandLink" action="contact">Contact</a>
</td>
<td>
<span jsfc="ui:insert" name="p2">unique content</span>
</td>
</tr> Let's name it "p2"
</table>
</span>
</html>

Modify home.xhtml to provide the two concrete parts:


The <define> tag is
used to provide a
named concrete part
<html ...>
<span jsfc="ui:composition" template="/base.xhtml">
<span jsfc="ui:define" name="p1">
<h1>Home</h1> p1
</span>
<span jsfc="ui:define" name="p2">
This is the home page. p2
</span>
</span>
</html>

Similarly, modify products.xhtml:


<html ...>
<span jsfc="ui:composition" template="/base.xhtml">
<span jsfc="ui:define" name="p1">
<h1><i>Products</i></h1>
</span>
<span jsfc="ui:define" name="p2">
This is the Products page.
</span>
</span>
</html>
Run it and it should work.

Having page specific navigation cases


Suppose that you'd like to have a link on the Products page to display hot deals:
Chapter 7 Providing a Common Layout with Facelets 205

To do that, modify products.xhtml:


<html ...>
<span jsfc="ui:composition" template="/base.xhtml">
<span jsfc="ui:define" name="p1">
<h1><i>Products</i></h1>
</span>
<span jsfc="ui:define" name="p2">
This is the Products page. Here are some <a jsfc="h:commandLink"
action="hotDeals">hot deals</a>.
</span>
</span>
</html>
There is nothing special. Create a simple hotdeals.xhtml page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
Hot deals here!
</html>
Then, create a normal navigation rule for the Products page:

The source is:


206 Chapter 7 Providing a Common Layout with Facelets

<faces-config ...>
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>products</from-outcome> 2: It will be considered if
<to-view-id>/products.xhtml</to-view-id> no matching navigation
</navigation-case> case was found in the
<navigation-case> specific rule
<from-outcome>home</from-outcome>
<to-view-id>/home.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/products.xhtml</from-view-id>
<navigation-case> 1: It will be considered
<from-outcome>hotDeals</from-outcome> first as it specifies a
<to-view-id>/hotdeals.xhtml</to-view-id> specific view id
</navigation-case>
</navigation-rule>
...
</faces-config>

Run it and it will work.

Summary
If you have pages with a common layout, you can extract the common stuff into
a base page and mark the abstract parts using <ui:define>. Then in each child
page, suck in the base page using <ui:composition> and provide each concrete
part using <ui:define>. Each part should have a unique name. If there is only
one abstract part, you can omit the name and provide the concrete part as the
body of the <ui:composition> element.
You can use a star as the view id in a navigation rule. In that case it will match
any view id. This is useful if multiple pages share the same navigation cases. If
a page needs some additional navigation cases, it can have its own normal
navigation rule which will take be checked first.
207

Chapter 8
Chapter 8 Using JBoss Seam
208 Chapter 8 Using JBoss Seam

What's in this chapter?


In this chapter you'll learn how to use the JBoss Seam framework. It is like a
much enhanced version of the JSF bean management facility.

Recreating the e-shop with Seam


Let's recreate the e-shop with Seam. First, go to
http://labs.jboss.com/projects/download to download a binary package of Seam.
Suppose that it is jboss-seam-2.0.1.GA.zip. Unzip it into say c:\jboss-seam.
Next, in Eclipse, choose "File | New | Others". Then choose "Seam | Seam Web
Project":

If you don't see this option, it means you didn't install the JBoss IDE Tools as
told in the previous chapter. Click Next. Then enter a project name and choose
to use Seam 2.0:
Chapter 8 Using JBoss Seam 209

Keep clicking Next until you see the dialog below:


210 Chapter 8 Using JBoss Seam

Click "Add" to define a Seam runtime:

Then click "New" to define a so-called connection profile. Usually a Seam


application needs to connect to a database. In a connection profile you specify
Chapter 8 Using JBoss Seam 211

things like database name, user name and password. Here, choose the
HSQLDB type:

Enter a name for the profile:

Click Next and you'll see:


212 Chapter 8 Using JBoss Seam

Click "..." to choose a database driver. You'll see:


Chapter 8 Using JBoss Seam 213

Click "Add":

Choose it

Choose the hsqldb.jar file and click "Edit Jar/Zip" to specify its correct location:
214 Chapter 8 Using JBoss Seam

If you don't have Hypersonic DB, you can download it from


http://www.hsqldb.org. Unzip it into say c:\hsqldb. You can find the hsqldb.jar file
in c:\hsqldb\lib. Then go back to choose the newly created driver:
Chapter 8 Using JBoss Seam 215

Click OK. Finally, enter the information as shown below:

The name of the DB as


used in Eclipse

The URL to be used in the


JDBC URL

The default user in Hypersonic DB.


It has an empty password.

Finish the part about the DB. Then specify the Java packages to be used:
216 Chapter 8 Using JBoss Seam

The packages for things like "session beans" or "entity beans".


As you won't be using these EJB3 stuff, the packages don't
really matter.

Finish the creation of the project. It will asks if you'd like to switch to the Seam
perspective. Say Yes. Then copy commons-collections.jar, commons-
logging.jar, dom4j.jar, hibernate-validator.jar, javassist.jar, jta.jar and
persistence-api.jar in c:\jboss-seam\lib into WEB-INF/lib. Refresh the project.
By default, the Seam plugin assumes that your application will connect to the
database at startup. As you don't have a database, modify WEB-
INF/components.xml (see the diagram below). This components.xml file is used
to configured the Seam components, which are like the JSF managed beans or
Spring beans:
Chapter 8 Using JBoss Seam 217

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


<components ...>

<core:init debug="true" jndi-pattern="@jndiPattern@" />

<core:manager concurrent-request-timeout="500"
conversation-timeout="120000" conversation-id-parameter="cid" />
Comment out these two elements. They'll try
<!-- to connect to a database.
<persistence:managed-persistence-context name="entityManager"
auto-create="true"
entity-manager-factory="#{ShopSeamEntityManagerFactory}" />

<persistence:entity-manager-factory
name="ShopSeamEntityManagerFactory" persistence-unit-name="ShopSeam" />

-->
...
</components>

In addition, by default Seam will start and commit a transaction for you
automatically at suitable times (for example, when updating domain values and
invoking application):

Update Domain Values Invoke Application

1: Start a transaction 2: Commit the transaction

This in turn requires a database connection (or an EJB3 server). As you have
neither, you need to tell Seam not to manage the transactions for you. This is
again done by configuring components in components.xml:
218 Chapter 8 Using JBoss Seam

<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:persistence="http://jboss.com/products/seam/persistence"
xmlns:drools="http://jboss.com/products/seam/drools"
xmlns:bpm="http://jboss.com/products/seam/bpm"
xmlns:security="http://jboss.com/products/seam/security"
xmlns:mail="http://jboss.com/products/seam/mail"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:transaction="http://jboss.com/products/seam/transaction"
xsi:schemaLocation=" What elements are included in
http://jboss.com/products/seam/core this namespace? This xsd file
http://jboss.com/products/seam/core-2.0.xsd includes all the definitions. Having
... this information Eclipse can
http://jboss.com/products/seam/transaction perform content assist.
http://jboss.com/products/seam/transaction-2.0.xsd">

<core:init debug="true" jndi-pattern="@jndiPattern@"


transaction-management-enabled="false"/>

<transaction:no-transaction />
... It tells the component named
</components> "init" not to start and commit
Seam assumes that there is always transactions automatically
a transaction component that can
start or commit a transaction. Here
you install a no-op transaction
component that does nothing for
those operations:
transaction
1: start a
transaction 2: Do nothing

Now start the Tomcat instance and observe the output in the console. Seam
should start and print out a lot of information saying various components are
initialized:
INFO: Initializing Sun's JavaServer Faces implementation (1.2_07-b03-FCS) for
context '/ShopSeam'
INFO: Welcome to Seam 2.0.1.GA
...
INFO: Installing components...
Feb 17, 2008 1:16:03 PM org.jboss.seam.Component <init>
INFO: Component: authenticator, scope: EVENT, type: JAVA_BEAN, class:
shop.session.Authenticator
Feb 17, 2008 1:16:03 PM org.jboss.seam.Component <init>
INFO: Component: org.jboss.seam.async.dispatcher, scope: APPLICATION, type:
JAVA_BEAN, class: org.jboss.seam.async.ThreadPoolDispatcher
Feb 17, 2008 1:16:03 PM org.jboss.seam.Component <init>
INFO: Component: org.jboss.seam.captcha.captcha, scope: SESSION, type: JAVA_BEAN,
class: org.jboss.seam.captcha.Captcha
...
Most importantly, there shouldn't be any exception.

Creating the catalog page


To create the catalog page, you can use JSP or Facelets. Seam supports both
and recommends Facelets. You will use Facelets here. Create
showcatalog.xhtml as usual:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
Chapter 8 Using JBoss Seam 219

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<h:form>
<h:dataTable border="1" value="#{showCatalog.products}" var="p">
<h:column>
<h:outputText value="#{p.id}"></h:outputText>
</h:column>
<h:column>
<h:commandLink value="#{p.name}" action="#{showCatalog.showDetails}">
</h:commandLink>
</h:column>
<h:column>
<h:outputText value="#{p.price}"></h:outputText>
</h:column>
</h:dataTable>
</h:form>
</body>
</html>
There is nothing special here. The interesting thing is the "showCatalog" bean.
Create a class ShowCatalog in the shop.helper package. Note that there are
two existing source folders in the project:

Which one to use doesn't really affect the functionality. However, as the helper
belongs to the UI, not the domain, so the src/action folder seems a better fit. So
create the ShowCatalog class there:
220 Chapter 8 Using JBoss Seam

package shop.helper;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In; Define a bean named "showCatalog"
from this class. It is actually not a JSF
import org.jboss.seam.annotations.Name; bean, but a Seam component that
import org.jboss.seam.annotations.Scope; can be used like a JSF bean.

@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In The scope for the bean (i.e.,
private CatalogService catalogService; component). Event scope the
same thing as the request
public List<Product> getProducts() { scope.
return catalogService.getProducts();
}
public String showDetails() {
return "details";
}
}

Inject a Seam component into Note that you don't need a


this field. The name of the setter. Seam can inject it
component is the field name directly into the field.
("catalogService" here).

The injection performed by Seam is a bit different from JSF: For example, in
this case, if someone calls any method on "showCatalog" component (see the
diagram below), Seam will intercept the call and set the "catalogService" field to
point to the "catalogServie" component before the body of the method is
executed:
@Name("showCatalog") 2: Seam will set the field to the
@Scope(ScopeType.EVENT) component
public class ShowCatalog {
@In
1: Someone calls any private CatalogService catalogService;
method (e.g., the
getProducts method)
public List<Product> getProducts() {
return catalogService.getProducts();
}
3: The body of the public String showDetails() {
method starts to return "details"; ...
execute }
}
Seam components for the app
Name Value
catalogService
... ...
... ...

The advantage of this is that even if the "showCatalog" component were in the
session scope, after deserialization it would still gain access to the injected
components.
Next, create the "catalogService" component and the CatalogService class. As
it is a service, let's put it into the shop.service package. Strictly speaking, a
Chapter 8 Using JBoss Seam 221

service doesn't belong to the UI or the business domain, but for simplicity, let's
put it into the src/model folder:
package shop.service;
Component name
@Name("catalogService")
@Scope(ScopeType.APPLICATION) Application scope
public class CatalogService {
private List<Product> products;

public CatalogService() {
products = new ArrayList<Product>();
products.add(new Product("p01", "Pencil", "a", 1.20));
products.add(new Product("p02", "Eraser", "b", 2.00));
products.add(new Product("p03", "Ball pen", "c", 3.50));
}
public List<Product> getProducts() {
return products;
}
public Product getProduct(String pid) {
for (Product p : products) {
if (p.getId().equals(pid)) {
return p;
}
}
return null;
}
}

Create the Product class in the shop.domain package. Put it into the src/model
folder:
package shop.domain;

public class Product {


private String id;
private String name;
private String desc;
private double price;

public Product() {

}
public Product(String id, String name, String desc, double price) {
this.id = id;
this.name = name;
this.desc = desc;
this.price = price;
}
public String getDesc() {
return desc;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
It is not a Seam component. It is just a normal Java class. Now, run it by going
222 Chapter 8 Using JBoss Seam

to the URL:
Context path, as usual.

http://localhost:8080/ShopSeam/showcatalog.seam

By default it uses .seam


as the extension instead
of .jsf

Unfortunately, it will fail with an exception:

When it tried to inject the


"catalogService" component, the
component didn't exist so it got a
null.
public class ShowCatalog {
@In
private CatalogService catalogService;
...
}

This is because unlike JSF managed beans, by default Seam will NOT create
Seam components automatically, unless it is accessed directly by an EL
expression in your page. In your case the "showCatalog" component will be
created automatically but not the "catalogService" component:
<html ...>
<body>
<h:form>
<h:dataTable border="1" value="#{showCatalog.products}" var="p">
<h:column>
<h:outputText value="#{p.id}"></h:outputText>
</h:column>
<h:column>
<h:commandLink value="#{p.name}" action="#{showCatalog.showDetails}">
</h:commandLink>
Chapter 8 Using JBoss Seam 223

</h:column>
<h:column>
<h:outputText value="#{p.price}"></h:outputText>
</h:column>
</h:dataTable>
</h:form>
</body>
</html>
To tell Seam to automatically create the "showCatalog" component, do it this
way:
@Name("catalogService")
@Scope(ScopeType.APPLICATION)
@AutoCreate
public class CatalogService {
...
}
Run it again and it should work (except for the links).

Displaying product details


To make the links work, create the productdetails.xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<h1><h:outputText value="#{product.name}"></h:outputText></h1>
<p>
<h:outputText value="#{product.desc}"></h:outputText>
</p>
<h:form>
<h:commandButton value="Add to cart" action="#{productDetails.addToCart}">
</h:commandButton>
<h:commandButton value="Continue shopping" action="catalog">
</h:commandButton>
</h:form>
</body>
</html>
For it to work, you need a component named "product". To do that, modify the
Product class:
@Name("product")
@Scope(ScopeType.EVENT)
public class Product {
private String id;
private String name;
private String desc;
private double price;
...
}
Next, implement the link in the ShowCatalog class (see the diagram below).
Note the @Out annotation. What does it mean? It means for example, if
someone calls any method of the "showCatalog" component (e.g., the
showDetails method), the method body will execute as usual. But before it
returns to the caller, Seam will intercept it and set the "product" component
point to the "product" field:
224 Chapter 8 Using JBoss Seam

Outject this field

@Name("showCatalog")
@Scope(ScopeType.EVENT) By default, if the product field is null, Seam will
public class ShowCatalog { treat it as an error. Setting required to false will
@In suppress the error.
private CatalogService catalogService;
@Out(required=false)
private Product product;

public List<Product> getProducts() {


return catalogService.getProducts();
}
public String showDetails() { 1: Someone calls any method on
product = ???; the component
return "details";
} 2: The method body starts to execute
} Need to find out the
selected product and Seam components for request 1
store it into the field Name Value
product
???
... ...
... ...
showCatalog

product:
3: Point to some 4: Just before the method returns to
Product object the caller, Seam intercepts it and sets
the "product" component to the
"product" field.

As a test, simply set the product field to a hard coded Product:


@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out
private Product product;

public List<Product> getProducts() {


return catalogService.getProducts();
}
public String showDetails() {
product = new Product("p04", "a", "b", 3.6);
return "details";
}
}
For it to work, you need to define the navigation case. In a Seam application,
you don't specify it in faces-config.xml. You do it in WEB-INF/pages.xml:
Chapter 8 Using JBoss Seam 225

Information about
the /showcatalog.xhtml
page

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


<pages ...
no-conversation-view-id="/home.xhtml" login-view-id="/login.xhtml">

<page view-id="/showcatalog.xhtml">
<navigation> If outcome is "details"
<rule if-outcome="details">
<render view-id="/productdetails.xhtml" />
</rule>
</navigation> Then render /productdetails.xhtml
</page>

<page view-id="*">
<navigation>
<rule if-outcome="home">
<redirect view-id="/home.xhtml" />
</rule>
</navigation>
</page>
...
</pages>

Run it and it should work. Now, the next question is how to find out the selected
product. In the past you use the <setPropertyActionListener> to do that but it is
quite a low level thing and it requires setting "immediate" attribute of the
command link to true and thus is not really a good solution. The idea is, instead
of providing a List to the UI Data, you provide a DataModel (see the diagram
below). The UI Data will ask it to see how many rows there are, loop through
each row and get the object on the each row:

UI Data
How many rows?
DataModel

Set the current row to 0

Tell me the object on the


current row

The key is, when a link is clicked, in the Invoke Application phase, the UI Data
will set the current row to the selected row:
1: A link is clicked
2: Set the current
UI Data row to the selected
row
DataModel

As long as you can get access to the DataModel, you can find out the selected
row index and get the right Product object. To implement this idea, modify
ShowCatalog:
226 Chapter 8 Using JBoss Seam

...
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;

@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out(required=false)
private Product product;
private DataModel dataModel;
Create a DataModel wrapping
around this List of Product
public DataModel getDataModel() { objects
if (dataModel == null) {
dataModel = new ListDataModel(getProducts());
}
return dataModel;
}
public List<Product> getProducts() {
return catalogService.getProducts();
}
public String showDetails() {
product = new Product("p04", "a", "b", 3.6);
product = (Product) dataModel.getRowData();
return "details";
} 1: Get the object on
} the current row List Current row: 0
DataModel

List
2: Call get(0) on the
list, assuming that
the current row is 0.

Modify showcatalog.xhtml to use the DataModel instead of the List:


<html ...>
<body>
<h:form>
<h:dataTable border="1" value="#{showCatalog.dataModel}" var="p">
<h:column>
<h:outputText value="#{p.id}"></h:outputText>
</h:column>
<h:column>
<h:commandLink value="#{p.name}" action="#{showCatalog.showDetails}">
</h:commandLink>
</h:column>
<h:column>
<h:outputText value="#{p.price}"></h:outputText>
</h:column>
</h:dataTable>
</h:form>
</body>
</html>
As this usage of DataModel is so common, Seam provides some support for it.
To see how to make use of it, first, let's create the DataModel as a component:
Chapter 8 Using JBoss Seam 227

5: Outjects the field value (the DataModel) as a


component

Name Value
... ... 1: Give me the component
named "productList"
... ...
... ...

@Name("showCatalog") Seam
@Scope(ScopeType.EVENT) 2: Check if there is such a
public class ShowCatalog { component there. At the
@In beginning there is not.
private CatalogService catalogService;
@Out(required = false)
private Product product;
@Out
private DataModel productList;
4: The field is set
@Factory("productList")
public void loadProducts() { 3: Look, there is a factory
productList = new ListDataModel(getProducts()); method for the component.
} Call it.
public List<Product> getProducts() {
return catalogService.getProducts();
}
public String showDetails() {
product = (Product) productList.getRowData();
return "details";
}
}

Modify showcatalog.xhtml to use the "productList" component:


<html ...>
<body>
<h:form>
<h:dataTable border="1" value="#{productList}" var="p">
...
</h:dataTable>
</h:form>
</body>
</html>
Run it and it should continue to work. Next, Seam can create a DataModel for
you automatically as long as you give it a List:
228 Chapter 8 Using JBoss Seam

5: @DataModel is like @Out in that it will outject a component.


However, it doesn't outject the field value directly. Instead, it
creates a DataModel wrapping the field value and then outjects
that DataModel.

import org.jboss.seam.annotations.datamodel.DataModel;

@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out(required = false)
private Product product;
@Out You only need to provide a List
@DataModel
private DataModel List<Product> productList;
@DataModel will create the DataModel. You
@Factory("productList")
only need to provide a List.
public void loadProducts() {
productList = new ListDataModel(getProducts());
}
public List<Product> getProducts() {
return catalogService.getProducts();
}
@In(value = "productList", required = false) As the DataModel is no
private javax.faces.model.DataModel dataModel; longer a field, to access
it, you need to inject it.
public String showDetails() {
product = (Product) dataModel.getRowData();
return "details"; This is the name of the component to be injected.
} As it is different from the field name ("dataModel"),
} you have to specify it explicitly.

Run it and it will continue to work. In addition, Seam can help you inject the row
data of a DataModel component you outjected:
Chapter 8 Using JBoss Seam 229

@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out(required = false)
private Product product;
@DataModel
private List<Product> productList;
DataModel
@Factory("productList") 1: Find a @DataModel and get the
component name ("productList")
public void loadProducts() { 3: Call getRowData() on
productList = getProducts(); it and inject the result
} into the field
public List<Product> getProducts() {
return catalogService.getProducts();
} Name Value
@DataModelSelection ... ...
private Product selected; 2: Look up the "productList"
DataModel ... ...
@In(value = "productList", required = false) ... ...
private javax.faces.model.DataModel dataModel;

public String showDetails() {


product = (Product) dataModel.getRowData();
product = selected;
return "details";
}
}

Run it and it will continue to work. Finally, the code can be further improved a
little bit. Currently the flow is:
@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out(required = false)
private Product product; 3: Outject
@DataModel
private List<Product> productList;

@Factory("productList")
public void loadProducts() {
productList = getProducts();
}
public List<Product> getProducts() {
return catalogService.getProducts();
}
@DataModelSelection 2: Assign
private Product selected; 1: Inject

public String showDetails() {


product = selected;
return "details";
}
}

Then why not combine the two fields into one?


230 Chapter 8 Using JBoss Seam

@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out(required = false)
@DataModelSelection
private Product product; Now it is both injected and outjected. It is
@DataModel said to be bijected.
private List<Product> productList;

@Factory("productList")
public void loadProducts() {
productList = getProducts();
}
@DataModelSelection
private Product selected;

public String showDetails() {


product = selected;
return "details";
}
}

Run it and it will continue to work.

Adding a product to the shopping cart


Now, let's implement the "Add to cart" button. Note the action method in
productdetails.xhtml:
<html ...>
<body>
<h1><h:outputText value="#{product.name}"></h:outputText></h1>
<p>
<h:outputText value="#{product.desc}"></h:outputText>
</p>
<h:form>
<h:commandButton value="Add to cart" action="#{productDetails.addToCart}">
</h:commandButton>
<h:commandButton value="Continue shopping" action="catalog">
</h:commandButton>
</h:form>
</body>
</html>
Therefore, you need to create a ProductDetails class. Put it into the shop.helper
package:
package shop.helper;

@Name("productDetails")
@Scope(ScopeType.EVENT)
public class ProductDetails {
@In
private Product product;

public String addToCart() {


//add the product to the cart
return "addedToCart";
}
}
However, as the "product" component is in the event scope, when the button is
Chapter 8 Using JBoss Seam 231

clicked, a new one will be created which is not the one displayed. In the past
you solved this problem by storing the product id in a hidden field and loading all
its properties when the id is set. This is a lot of work. Seam provides a very
powerful solution. It provides a new scope: conversation scope. For example,
suppose at a certain time you start a new conversation (see the diagram below).
Seam will assign a unique id (say 123) to it. Then a request comes in and a
response is rendered. Assume in the process a Seam component (say "foo") is
created and it is marked to be in the conversation scope, then it will be stored
into a component table for conversation 123. That table is stored in the session.
Suppose that another request comes in. The "foo" component will still be there
for you to access. If later you decide to end the conversation, Seam will then
destroy the component table for conversation 123:

2: Handle a submission and 1: Start a conversation. Seam


render the response. In the gives it an id (say 123)
process, a conversation-
scoped component may be
created.
Session for the client
components for conversation 123
Name Value
foo
3: The component
is put there
... ...
... ...
4: Handle a submission and
render the response. The
component is still there!

5: End the conversation. The


components in conversation
123 will be destroyed.

Time

How is it better than using the session scope? For example, if the user views
product 1 in a tab in the browser (see the diagram below), if you're using the
session scope, product 1 will be stored in the session scope. Suppose that the
user then opens a new tab and views product 2. Then product 2 will replace
product 1 in the session. Finally, suppose that the user returns to the original
tab and clicks the "Add" button. Even though he is looking at product 1 in the
tab, your application will add product 2 to the shopping cart as product 2 is in
the session! This is extremely confusing to the user:
232 Chapter 8 Using JBoss Seam

Session components for the client


Name Value
product 2: Put product 1 into there
... ...
... ...
4: Put product 2 into there,
replacing product 1.

Product 1 Product 2 Product 1


Add Add Add
1: View product 1 3: Open a new tab 5: Return to the original
in a tab in a and view product 2 tab and click "Add".
browser in that tab Ouch! Product 2 is
added to the cart!

What if you use the conversation scope instead? Then, you'll have a separate
conversation for each tab and the two tabs will work independently:
Conversation 1 Conversation 2
Name Value Name Value
product product
... ... ... ...
... ... ... ...

2: Put product 1 into there 4: Put product 2 into there

Product 1 Product 2 Product 1


Add Add Add
1: View product 1 3: Open a new tab 5: Return to the original tab and
in a tab in a and view product 2 click "Add". This will correctly
browser in that tab add product 1 to the cart.

Actually, whenever a request arrives, even if you don't start a conversation,


Seam will always create a temporary conversation for you. Such a conversation
will be ended automatically after the response is rendered:
Chapter 8 Using JBoss Seam 233

1: Seam starts a temporary


conversation

2: Handle a submission and


render the response

3: Seam ends the conversation


automatically

Time

Why is it important? When handling the request, you may create a conversation
scope component first (see the diagram below) and then decide to start a
conversation. In that case, Seam will NOT create a new conversation. Instead,
it will use that temporary conversation and mark it as a normal (non-temporary)
conversation. The result is, the conversation scoped component will remain
there even though when it was added, the conversation was still a temporary
one:

1: Seam starts a temporary


conversation

2: Handle a submission and


render the response
components for conversation 123
Name Value
foo
3: A component is
... ...
created
... ...
4: You decide to start a
conversation. The
temporary conversation is
used and marked as non-
temporary.

Time

To make use of this feature, change the scope of the "product" component and
let it implement Serializable:
@Name("product")
@Scope(ScopeType.EVENT)
@Scope(ScopeType.CONVERSATION)
public class Product implements Serializable {
private String id;
private String name;
private String desc;
private double price;
...
}
234 Chapter 8 Using JBoss Seam

In addition, you need start and end the conversation:


start conversation
showcatalog productdetails
end conversation

end conversation

showcart

To do that, modify pages.xml:


<pages ...> Start a conversation before
displaying the productdetails
<page view-id="/showcatalog.xhtml">
page
<navigation>
<rule if-outcome="details">
<begin-conversation/>
<render view-id="/productdetails.xhtml" />
</rule>
</navigation>
</page> End the conversation before
<page view-id="/productdetails.xhtml"> leaving the productdetails
page
<navigation>
<rule if-outcome="catalog">
<end-conversation/>
<render view-id="/showcatalog.xhtml" />
</rule>
<rule if-outcome="addedToCart">
<end-conversation/>
<render view-id="/showcart.xhtml" />
</rule>
</navigation>
</page>
...
</pages>

However, depending on the caller to begin the conversation is no good. The


productdetails page should start the conversation itself:
Chapter 8 Using JBoss Seam 235

<pages ...>
<page view-id="/showcatalog.xhtml">
<navigation>
<rule if-outcome="details">
<begin-conversation/>
<render view-id="/productdetails.xhtml" />
</rule>
</navigation> Start a conversation before
</page> rendering this page
<page view-id="/productdetails.xhtml">
<begin-conversation/>
<navigation>
<rule if-outcome="catalog">
<end-conversation/>
<render view-id="/showcatalog.xhtml" />
</rule>
<rule if-outcome="addedToCart">
<end-conversation/>
<render view-id="/showcart.xhtml" />
</rule>
</navigation>
</page>
...
</pages>

To test it, modify the ProductDetails class:


@Name("productDetails")
@Scope(ScopeType.EVENT)
public class ProductDetails {
@In
private Product product;

public String addToCart() {


System.out.println(product.getName());
return "addedToCart";
}
}
Then create a dummy showcart page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<body>
cart
</body>
</html>
Run it and try to add a product to the cart. You should see the product name
displayed in the console:
236 Chapter 8 Using JBoss Seam

To really add the product to the shopping cart, you need to create a session
scoped component named "cart". To do that, create a Cart class in the
shop.domain package:
No page is going to refer to it
package shop.domain; directly, so need to auto-
create it.
@Name("cart") Everything in the session
@Scope(ScopeType.SESSION) must implement
@AutoCreate Serializable
public class Cart implements Serializable {
private List<String> productIds;

public Cart() {
productIds = new ArrayList<String>();
}
public void add(String pid) {
productIds.add(pid);
}
public List<String> getProductIds() {
return productIds;
}
}

Modify the productdetails page:


@Name("productDetails")
@Scope(ScopeType.EVENT)
public class ProductDetails {
@In
private Product product;
@In
private Cart cart;

public String addToCart() {


System.out.println(product.getName());
cart.add(product.getId());
return "addedToCart";
}
}
Next, modify the showcart page to display the cart content:
<html ...>
<body>
<h:dataTable border="1" value="#{showCart.products}" var="p">
<h:column>
<h:outputText value="#{p.id}"></h:outputText>
</h:column>
<h:column>
<h:outputText value="#{p.name}"></h:outputText>
Chapter 8 Using JBoss Seam 237

</h:column>
<h:column>
<h:outputText value="#{p.price}"></h:outputText>
</h:column>
</h:dataTable>
<h:form>
<h:commandButton value="Checkout" action="checkout"></h:commandButton>
<h:commandButton value="Continue shopping" action="catalog"></h:commandButton>
</h:form>
</body>
</html>
Create the ShowCart class in the shop.helper package:
package shop.helper;

@Name("showCart")
@Scope(ScopeType.EVENT)
public class ShowCart { Inject it to get the product ids
@In Inject it to get the product names
private Cart cart; and prices from the ids
@In
private CatalogService catalogService;

public List<Product> getProducts() {


List<Product> products = new ArrayList<Product>();
for (String pid : cart.getProductIds()) {
products.add(catalogService.getProduct(pid));
}
return products;
}
}

Define the navigation cases in pages.xml:


<pages ...>
...
<page view-id="/showcart.xhtml">
<navigation >
<rule if-outcome="checkout">
<render view-id="/confirm.xhtml" />
</rule>
<rule if-outcome="catalog">
<render view-id="/showcatalog.xhtml" />
</rule>
</navigation>
</page>
</pages>
Now run it and it should work.

Confirming the checkout


Next, create the confirm.xhtml file:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<p>
You're going to pay <h:outputText value="#{confirm.total}"></h:outputText> with
credit card <h:outputText value="#{confirm.creditCardNo}"></h:outputText>.
</p>
238 Chapter 8 Using JBoss Seam

<h:form>
<h:commandButton value="Confirm" action="#{confirm.confirm}">
</h:commandButton>
<h:commandButton value="Continue shopping" action="catalog">
</h:commandButton>
</h:form>
</body>
</html>
Create the Confirm class in the shop.helper package:
package shop.helper;

@Name("confirm")
@Scope(ScopeType.EVENT)
public class Confirm {
@In
private Cart cart;
@In
private CatalogService catalogService;

public double getTotal() {


double total = 0;
for (String pid : cart.getProductIds()) {
total += catalogService.getProduct(pid).getPrice();
} You need to get the card # from
return total; the account of the logged in user.
} For now, just return a hard code
public String getCreditCardNo() { value.
return "123";
}
public String confirm() {
//charge his card, schedule the delivery and etc.
return "charged";
}
}

Define the navigation cases:


<pages ...>
...
<page view-id="/confirm.xhtml">
<navigation >
<rule if-outcome="charged">
<render view-id="/thankyou.xhtml" />
</rule>
<rule if-outcome="catalog">
<render view-id="/showcatalog.xhtml" />
</rule>
</navigation>
</page>
</pages>
Create a simple thankyou.xhtml page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<body>
Thank you!
</body>
</html>
Run it and it should work.
Chapter 8 Using JBoss Seam 239

Logging in
In order to retrieve the credit card number from the user account, you need to
allow the user to login first. To do that, Seam already provides a session scoped
component that can hold the user name and password:

Identity

username:
password:

The plugin even creates a login.xhtml file for you. But here, let's modify it as:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<body>
It will output an HTML <label>
<h:messages/>
element:
<h:form id="f">
<h:panelGrid columns="2">
<h:outputLabel for="email">Email</h:outputLabel>
<h:inputText id="email" value="#{identity.username}" />
<h:outputLabel for="password">Password</h:outputLabel>
<h:inputSecret id="password" value="#{identity.password}" />
</h:panelGrid>
<h:commandButton value="Login" action="#{identity.login}" />
</h:form>
</body>
</html>
The "identity" component also
<label for="f:email">Email</label>
has a login() method to
authenticate the user <input id="f:email">....</input>

How can its login() method know if the user name and password are valid or
not? It doesn't know. It will assume that you will provide an "authenticator"
component which has an authenticate() method. Furthermore, the plugin
actually created one for you in the shop.session package:
package shop.session;

import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.log.Log;
import org.jboss.seam.security.Identity;

@Name("authenticator")
public class Authenticator
{
@Logger Log log;

@In Identity identity;

public boolean authenticate()


{
log.info("authenticating #0", identity.getUsername());
//write your authentication logic here,
//return true if the authentication was
//successful, false otherwise
identity.addRole("admin");
240 Chapter 8 Using JBoss Seam

return true;
}
}
To authenticate the user, create the UserService class in the shop.service
package:
package shop.service;

@Name("userService")
@Scope(ScopeType.APPLICATION)
@AutoCreate
public class UserService {
private List<User> users;

public UserService() {
users = new ArrayList<User>();
users.add(new User("paul@yahoo.com", "aaa", "1111 2222 3333 4444"));
users.add(new User("john@hotmail.com", "bbb", "2222 3333 4444 5555"));
}
public User findMatchingUser(String email, String password) {
for (User user : users) {
if (user.matches(email, password)) {
return user;
}
}
throw new UserNotFoundException();
}
public User getUser(String email) {
for (User user : users) {
if (user.getEmail().equals(email)) {
return user;
}
}
throw new UserNotFoundException();
}
}
Create the User class in the shop.domain package:
package shop.domain;

public class User {


private String email;
private String password;
private String creditCardNo;

public User(String email, String password, String creditCardNo) {


this.email = email;
this.password = password;
this.creditCardNo = creditCardNo;
}
public boolean matches(String email, String password) {
return this.email.equals(email) && this.password.equals(password);
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public String getCreditCardNo() {
return creditCardNo;
}
}
Create the UserNotFoundException in the same package:
package shop.domain;

public class UserNotFoundException extends RuntimeException {


Chapter 8 Using JBoss Seam 241

}
Now, fill in the code in the Authenticator class:
package shop.session;

@Name("authenticator") No scope is specified. Default is


public class Authenticator EVENT.
{
@Logger Log log;
@In Identity identity;
@In
private UserService userService;

public boolean authenticate()


{
log.info("authenticating #0", identity.getUsername());
//write your authentication logic here,
//return true if the authentication was
//successful, false otherwise
try { This is the email
userService.findMatchingUser(
identity.getUsername(), identity.getPassword());
return true;
} catch (UserNotFoundException e) {
return false;
}
identity.addRole("admin");
return true;
}
}

Create the login and logout links on the showcatalog page:


<html ...>
<body>
<h:form>
<h:dataTable border="1" value="#{productList}" var="p">
<h:column>
<h:outputText value="#{p.id}"></h:outputText>
</h:column>
<h:column>
<h:commandLink value="#{p.name}" action="#{showCatalog.showDetails}">
</h:commandLink>
</h:column>
<h:column>
<h:outputText value="#{p.price}"></h:outputText>
</h:column>
</h:dataTable>
<h:commandLink value="Login" action="login"></h:commandLink>
<h:commandLink value="Logout" action="#{showCatalog.logout}"></h:commandLink>
</h:form>
</body>
</html>
Define the navigation case in pages.xml:
<pages ...>
...
<page view-id="/showcatalog.xhtml">
<navigation>
<rule if-outcome="details">
<render view-id="/productdetails.xhtml" />
</rule>
<rule if-outcome="login">
<render view-id="/login.xhtml" />
</rule>
</navigation>
242 Chapter 8 Using JBoss Seam

</page>
...
</pages>
Where it will go after logging in? The plugin already created a login.page.xml file
to define the navigation case:
This navigation case will apply only if the
Instead of checking the outcome, it
action expression is #{identity.login}, which
evaluates an EL expression to check
is indeed the case here.
if the user has logged in.

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


<page ...>
<navigation from-action="#{identity.login}">
<rule if="#{identity.loggedIn}">
<redirect view-id="/home.xhtml"/>
</rule>
</navigation> Using <redirect> instead of <render> will generate a
response to tell the browser to go to http://localhost:
</page>
8080/ShopSeam/home.seam.

<h:form id="login">
<h:panelGrid columns="2">
<h:outputLabel for="email">Email</h:outputLabel>
<h:inputText id="email" value="#{identity.username}" />
<h:outputLabel for="password">Password</h:outputLabel>
<h:inputSecret id="password" value="#{identity.password}" />
</h:panelGrid>
<h:commandButton value="Login" action="#{identity.login}" />
</h:form>

The home.xhtml page is a dummy page created by the plugin. Modify it to tell
the browser to go to your showcatalog page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Refresh" content="0; URL=showcatalog.seam"/>
</head>
</html>
Tell the browser to refresh Wait 0 second before Relative path to the
refreshing. It means desired page
do not wait.

Modify the confirm page to get the credit card number from the logged in user:
Chapter 8 Using JBoss Seam 243

import org.jboss.seam.security.Identity;

@Name("confirm")
@Scope(ScopeType.EVENT)
public class Confirm {
@In
private Cart cart;
@In
private CatalogService catalogService;
@In
private UserService userService;

public double getTotal() {


double total = 0;
for (String pid : cart.getProductIds()) {
total += catalogService.getProduct(pid).getPrice();
}
return total;
}
public String getCreditCardNo() {
Identity identity = Identity.instance(); You could inject it. This is an
String email = identity.getUsername(); alternative way to access it.
User user = userService.getUser(email);
return user.getCreditCardNo();
return "123"; All you get is the email. Need
} to find the User object to get
public String confirm() { his credit card number.
//charge his card, schedule the delivery and etc.
return "charged";
}
}

Finally, by default the "identity" component is configured (in components.xml) to


use "security rules":
<components ...>
...
<drools:rule-base name="securityRules">
<drools:rule-files>
<value>/security.drl</value>
</drools:rule-files>
</drools:rule-base>
<security:identity
authenticate-method="#{authenticator.authenticate}"
security-rules="#{securityRules}" remember-me="true" />
</components>
As you don't have any security rule, get rid of it:
<components ...>
...
<!--
<drools:rule-base name="securityRules">
<drools:rule-files>
<value>/security.drl</value>
</drools:rule-files>
</drools:rule-base>
-->
<security:identity
authenticate-method="#{authenticator.authenticate}"
security-rules="#{securityRules}" remember-me="true" />
</components>
Restart the application. Login and then go to the confirm page. It should display
the correct credit card number.
244 Chapter 8 Using JBoss Seam

Protecting the confirm page


To make sure a user has logged in before rendering the confirm page, all you
need to do is modify pages.xml:
<pages ...
no-conversation-view-id="/home.xhtml" login-view-id="/login.xhtml">

...
<page view-id="/confirm.xhtml" login-required="true">
<navigation >
<rule if-outcome="charged">
<render view-id="/thankyou.xhtml" />
</rule> Check if a user has logged in
<rule if-outcome="catalog"> (by asking the "identity"
component). If not display the
<render view-id="/showcatalog.xhtml" />
login page. Which page is the
</rule>
login page? It is specified
</navigation> here:
</page>
</pages>

Try to access it without logging. It will tell you to login:

After logging in, it will return you to the originally requested page (the confirm
page). How does it do that? If you check the components.xml file, you'll see:
Chapter 8 Using JBoss Seam 245

1: Try to access the confirm page


without logging in
Security Listener 2
Listener 1
check

2: Create an event of the type


org.jboss.seam.security.notLoggedIn. Event 4: Yes,
The type is is a string. 3: Want to execute this
handle me? action method.

<components ...>
...
<event type="org.jboss.seam.security.notLoggedIn"> Redirect
<action execute="#{redirect.captureCurrentView}" />
</event>
<event type="org.jboss.seam.security.loginSuccessful">
<action execute="#{redirect.returnToCapturedView}" /> 5: Capture the
</event> view id for
</components> later restore
This will return
to the original
view id

Implementing logout
The logout link is already defined:
<html ...>
<body>
<h:form>
<h:dataTable border="1" value="#{productList}" var="p">
...
</h:dataTable>
<h:commandLink value="Login" action="login"></h:commandLink>
<h:commandLink value="Logout" action="#{showCatalog.logout}"></h:commandLink>
</h:form>
</body>
</html>
For it to work, define the logout method in the ShowCatalog class:
246 Chapter 8 Using JBoss Seam

@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out(required = false)
@DataModelSelection
private Product product;
@DataModel
private List<Product> productList;

@Factory("productList")
public void loadProducts() {
productList = getProducts();
}
public List<Product> getProducts() {
return catalogService.getProducts();
}
public String showDetails() {
return "details";
} This will invalidate the session
public String logout() {
Identity.instance().logout();
return null;
} Do not change the view id
}

Run it. Try to logout and then click a product link. It will trigger an error:

To fix this problem, you can force a browser redirect. To do that modify the
ShowCatalog class to return an outcome:
@Name("showCatalog")
@Scope(ScopeType.EVENT)
public class ShowCatalog {
@In
private CatalogService catalogService;
@Out(required = false)
@DataModelSelection
private Product product;
Chapter 8 Using JBoss Seam 247

@DataModel
private List<Product> productList;

@Factory("productList")
public void loadProducts() {
productList = getProducts();
}
public List<Product> getProducts() {
return catalogService.getProducts();
}
public String showDetails() {
return "details";
}
public String logout() {
Identity.instance().logout();
return "loggedOut";
}
}
Define the navigation case:
<pages ...>
...
<page view-id="/showcatalog.xhtml">
<navigation>
<rule if-outcome="details">
<render view-id="/productdetails.xhtml" />
</rule>
<rule if-outcome="login">
<render view-id="/login.xhtml" />
</rule>
<rule if-outcome="loggedOut">
<redirect view-id="/showcatalog.xhtml" />
</rule>
</navigation>
</page>
</pages>
Run it again and it will work.

Redirect vs render
Let's take an experiment: Click a product link from the catalog page. Note that
the URL is not changed:

The URL is still


showcatalog.seam

This is because the navigation case is using <render> instead of <redirect>:


<page view-id="/showcatalog.xhtml">
<navigation>
<rule if-outcome="details">
248 Chapter 8 Using JBoss Seam

<render view-id="/productdetails.xhtml" />


</rule>
...
</navigation>
</page>
Having the old URL is not only confusing to the user, in addition, if you click the
Refresh or Reload button in the browser in an attempt to refresh the product
details, it will ask if you'd like to submit the form again:

This is no good. To solve these problems, you're advised to always use


<redirect> instead of <render>. However, for this to work, if one page creates a
component to be used by the next page (see the diagram below). When the
browser tries to access the next page, the component may not still exist if it is in
the event scope:

...

2: Redirect to page 2 1: Put a component there


Browser Page 1 Name Value
foo
... ...
Page 2 ... ...
3: Render page 2 4: Is foo still there?

Therefore, to use <redirect>, make sure the component is in the conversation


scope.
In your application, you can safely change all navigation cases to <redirect>:
<pages ...>
<page view-id="/showcatalog.xhtml">
<navigation>
<rule if-outcome="details">
<redirect view-id="/productdetails.xhtml" />
</rule>
<rule if-outcome="login">
<redirect view-id="/login.xhtml" />
</rule>
<rule if-outcome="loggedOut">
<redirect view-id="/showcatalog.xhtml" />
</rule>
</navigation>
</page>
<page view-id="/productdetails.xhtml">
<begin-conversation join="true"/>
<navigation >
<rule if-outcome="catalog">
<end-conversation/>
<redirect view-id="/showcatalog.xhtml" />
Chapter 8 Using JBoss Seam 249

</rule>
<rule if-outcome="addedToCart">
<end-conversation/>
<redirect view-id="/showcart.xhtml" />
</rule>
</navigation>
</page>
<page view-id="/showcart.xhtml">
<navigation >
<rule if-outcome="checkout">
<redirect view-id="/confirm.xhtml" />
</rule>
<rule if-outcome="catalog">
<redirect view-id="/showcatalog.xhtml" />
</rule>
</navigation>
</page>
<page view-id="/confirm.xhtml" login-required="true">
<navigation >
<rule if-outcome="charged">
<redirect view-id="/thankyou.xhtml" />
</rule>
<rule if-outcome="catalog">
<redirect view-id="/showcatalog.xhtml" />
</rule>
</navigation>
</page>
...
</pages>
Run it and it should continue to work. Besides, the URL will change with each
click.

Summary
A Seam component is like a JSF managed bean except that it is more powerful.
The injection does it work before each method call and thus prevents the
problem of deserialization. It also supports outjection so that you can store
components. If a property is both injected and outjected, it is said to be bijected.
Seam components are defined using annotations so that you don't have to edit
configuration files. If you need to change their settings, you can do it in the
components.xml file.
Seam provides a very powerful scope: conversation scope. You define when to
start a conversation and when to end it. During this time period, all conversation
scoped components will remain available, across different requests.
You can use the @DataModel annotation to create a DataModel wrapping a List
and outject that DataModel as a component. Most likely you'll use a @Factory
annotation to mark a method as a factory method for the DataModel component
and load the List in the method. You can inject the selected object in the
DataModel using @DataModelSelection.
You define the navigation cases in the pages.xml file. In that file you can define
additional properties of the pages: for example, if a page requires a logged in
user or requires to start a new conversation.
Seam provides an "identity" component to keep track of the currently logged in
250 Chapter 8 Using JBoss Seam

user (if any). All you need is to provide a page to store the user input into that
"identity" component and a method in the "authenticator" component. By default,
event listeners have been set up to capture the original view id and to return to it
on a successful login.
To render the next page, you can choose between a plain render or a redirect.
Redirect will show the new URL in the browser and will work fine with the
Reload/Refresh button. To allow redirect, if one page needs to pass a
component to the next page, it should be in the conversation scope.

Download at WoweBook.com
251

Chapter 9
Chapter 9 Supporting Other Languages
252 Chapter 9 Supporting Other Languages

What's in this chapter


In this chapter you'll learn how to develop an application that can appear in two
or more different languages to suit users in different countries.

A sample application
Suppose that you have an application that displays the current date:

This is easy. Create a JBoss JSF project named MultiLang and enable
Facelets. Copy the JSF RI jar files into WEB-INF/lib. Create a showdate.xhtml
file in the WebContent folder:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<head>
<title>Current date</title>
</head>
<body>
Today is: <h:outputText value="#{showDate.today}"/>.
</body>
</html>
Create a ShowDate class in the multilang package:
package multilang;

import java.util.Date;

public class ShowDate {


public Date getToday() {
return new Date();
}
}
Define the "showdate" bean:
<faces-config ...>
<managed-bean>
<managed-bean-name>showDate</managed-bean-name>
<managed-bean-class>multilang.ShowDate</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Chapter 9 Supporting Other Languages 253

<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
</faces-config>
Start the Tomcat instance and try to access http://localhost:
8080/MultiLang/showdate.jsf in the browser. It should work.

Supporting Chinese
Suppose that some of your users are Chinese. They would like to see the
application in Chinese when they run the application. To do that, create a file
msgs.properties (the filename is not really important) in the multilang package:
currentDate=Current date
todayIs=Today is:
To support Chinese, create another file msgs_zh.properties. "zh" represents
Chinese. Usually, people use the Big5 encoding to encode Chinese. However,
Java requires this file be in a special encoding called "escaped Unicode
encoding". For example, the Chinese for "Current date" consists of four Unicode
characters (see the diagram below). Their Unicode values (hexadecimal) are
also shown. The properties file should be written as:

當前日期

0x7576 0x65E5

0x524D 0x671F

currentDate=\u7576\u524d\u65e5\u671f
todayIs=...

Obviously, this is very difficult to do. Fortunately, the JBoss IDE tools includes a
properties file editor that allows you to enter any Unicode characters directly
including Chinese:
254 Chapter 9 Supporting Other Languages

You can enter Chinese or any Unicode


character directly

It works only in the "Properties" tab. If you


work in the "Source" tab, you'll have to
enter the Unicode code values directly!

As you probably don't know how to input Chinese, you can simply type some
random text pretending to be Chinese. To make use of the properties files,
modify showdate.xhtml:
<html ...>
<head>
<f:loadBundle basename="multilang.msgs" var="b" />
<title>Current date</title>
</head>
<body>
Today is: <h:outputText value="#{showDate.today}"/>.
</body>
</html> 1: Create this UI Load
Bundle component Attribute table for the request
Object name Object instance
b
UI View Root
... ...
... ...
2: Render
UI Load 4: Put the resource
Bundle bundle into an attribute in WEB-INF
the request scope. The
attribute name is "b" as classes
specified.
multilang
ShowDate.class
msgs.properties
3: Load multilang.msgs from the class path msgs_zh.properties
and assume an extension of .properties.
Create a table from the content. Such a table
is called a "resource bundle":

Key String
currentDate Current date
todayIs Today is:

To read messages from the resource bundle, do it this way:


Chapter 9 Supporting Other Languages 255

b (resource bundle)
Key String
1: Evaluate "b" and get currentDate Current date
access to the resource
bundle
todayIs Today is:
2: JSF will call getCurrentDate() on "b". It will fail. Then
it will check if it is a resource bundle. If so, it will look up
<html ...> the key "currentDate" and return the string ("Current
<head> date").
<title><h:outputText value="#{b.currentDate}"/></title>
</head>
<body>
<h:outputText value="#{b.todayIs}"/>
<h:outputText value="#{showDate.today}"/>.
</body>
</html>
Look up the "todayIs"
key

Run it and it should continue to work. How to make the page use the Chinese
version of the resource bundle (msgs_zh.properties)? For example, in FireFox,
choose "Tools | Options | Advanced":

Click "Choose" and make sure that Chinese is listed as the first entry (most
preferred):
256 Chapter 9 Supporting Other Languages

When the browser sends a request, it will include this list in the request (see the
diagram below). After Tomcat receives it, it will let the view handler handle it
(here it's the Facelets engine but could have been the JSP engine). The view
handler will create the component tree as usual. Then it will get the most
preferred language ("zh" here) from the list and store it into the view root. Later,
the UI Load Bundle component will find that the language of the view root is
"zh", so it will load msgs_zh.properties instead:
...
languages: zh, en, ...

1: Send a request
Browser Tomcat

2: You handle it

5: Store zh into it
View handler as its language
4: Look, zh is the most
preferred language.
View
root 6: What's your language?
3: Create the
Oh it's zh. Load
component tree Load msgs_zh.properties.
bundle

There is still a minor twist: Actually, the view handler will not blindly store the
most preferred language into the view root. It will check if that language is
supported by your application (see the diagram below). For example, if the most
preferred language is en or zh, it is supported and will be stored into the view
root. However, if it is, say, de (German), then it is not supported and the default
language will be used (en here):
Chapter 9 Supporting Other Languages 257

1: Do you support en? Yes.


Use it.
View handler

default: en
supported: zh
2: Do you support zh? supported: fr
Yes. Use it.

3: Do you support de?


No. Use the default
(en here).

To specify the default language and supported languages, modify faces-


config.xml:

2: Choose it

A language is also called a


locale

3: Enter the default language

4: Click "Add" to enter "zh"

1: Choose this tab

The corresponding XML code is:


258 Chapter 9 Supporting Other Languages

<faces-config ...>
<managed-bean>
<managed-bean-name>showDate</managed-bean-name>
<managed-bean-class>multilang.ShowDate</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>zh</supported-locale>
</locale-config>
</application>
</faces-config> If you also supported,
say, fr, you would add
one more <supported-
locale> element
there:

Save the file, restart the Tomcat instance and then reload the page. You should
see the Chinese version:

If you can't see Chinese on your computer, make sure it has a font that
supports Chinese. For example, login as the Administrator, open the Control
Panel and choose "Regional Settings" and ensure that traditional Chinese
support is enabled.
You may be wondering why the UI Load Bundle component doesn't load the
msgs_en.properties file when the most preferred locale is en. To understand
how it works, first consider the case when the most preferred locale is zh. In
that case, it will load msgs_zh.properties and use msgs.properties as the parent
(see the diagram below). If a key is not found in the child, the child will look for it
in the parent:
Chapter 9 Supporting Other Languages 259

msgs.properties
Key String
currentDate Current date
todayIs Today is:
If a key is not found
(e.g., "foo"), look for it
in the parent.

msgs_zh.properties
Key String
currentDate 當前日期
todayIs 今日是:

That's why it is called a bundle. Now, consider the case when the most
preferred locale is en. In that case, it try to load msgs_en.properties but it is not
found (see the diagram below). Then you can consider it will use a non-existing
or empty msgs_en.properties as the child. Effectively only the parent will be
used:
msgs.properties
Key String
currentDate Current date
todayIs Today is:

parent

msgs_en.properties

NON-EXISTING

Anyway, now you are done. It is said that you have "internationalized" this page
(let it use a resource bundle) and "localized" it to Chinese (provide
msgs_zh.properties). If in the future you need to add support for say French,
you will not need to internationalize it again but just need to localize it to French
(provide msgs_fr.properties). As the word "internationalization" is very long,
sometimes people use "i18n" as its short form because there are 18 characters
between the starting "i" and the ending "n". Similarly, people use "l10n" as a
short form for "localization".

Internationalize the date display


For the moment, the date is still displayed in English. To solve the problem,
modify showdate.xhtml:
260 Chapter 9 Supporting Other Languages

<html ...>
<head>
<f:loadBundle basename="multilang.msgs" var="b" />
<title><h:outputText value="#{b.currentDate}"/></title>
</head>
<body>
<h:outputText value="#{b.todayIs}"/>
<h:outputText value="#{showDate.today}">
<f:convertDateTime dateStyle="long"/>
</h:outputText>.
</body>
</html> This setting is not really
The date time converter will
use the locale stored in the required. It is set to long so
view root to format the Date that you can see Chinese
characters in the date
display.

Run the application and it should work:


English Chinese

Letting the user change the locale


Suppose that a user is using a browser that prefers Chinese the most, but he
would like to show the application to his friend who doesn't understand Chinese
but understands English. To support this, you should enhance the application to
allow the user to explicitly choose a locale:
Chapter 9 Supporting Other Languages 261

After choosing a locale, he can click "Change":

Let's do it. Modify showdate.xhtml:


<html ...>
<head>
<f:loadBundle basename="multilang.msgs" var="b" />
<title><h:outputText value="#{b.currentDate}"/></title>
</head>
<body>
<h:outputText value="#{b.todayIs}"/>
<h:outputText value="#{showDate.today}">
<f:convertDateTime dateStyle="long"/>
</h:outputText>.
<h:form>
<h:selectOneMenu value="#{showDate.locale}">
<f:selectItems value="#{showDate.locales}"/>
</h:selectOneMenu>
<h:commandButton action="#{showDate.changeLocale}" value="Change"/>
</h:form>
</body>
</html>
Define the properties required in ShowDate.java:
262 Chapter 9 Supporting Other Languages

public class ShowDate {


private String locale;

public String getLocale() {


return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public List<SelectItem> getLocales() {
Application app = FacesContext.getCurrentInstance().getApplication();
List<Locale> locales = new ArrayList<Locale>();
locales.add(app.getDefaultLocale()); Add the supported locales
Iterator<Locale> iter = app.getSupportedLocales(); (including the default locale)
into a List
while (iter.hasNext()) {
locales.add(iter.next()); It returns an iterator for the
} supported locales (the
List<SelectItem> items = new ArrayList<SelectItem>(); default locale is NOT
included)
for (Locale locale : locales) {
items.add(new SelectItem(
Convert each locale into a
locale.toString(), SelectItem
locale.getDisplayName(locale)));
}
return items;
} This is the name for the locale displayed in that
public Date getToday() { locale (e.g., "English" or " 中文 ").
return new Date();
} For example, this may be
} "en" or "zh".

Define the action method:


public class ShowDate {
private String locale;
...
public String changeLocale() {
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
viewRoot.setLocale(new Locale(locale));
return null;
}
} Store the new Locale It needs a Locale
object into the view object, not a string
root like "en".

Run it and it will work. Finally, the Change button should also be
internationalized and localized:
Chapter 9 Supporting Other Languages 263

Should be in Chinese

It's easy. Modify showdate.xhtml:


<html ...>
<head>
<f:loadBundle basename="multilang.msgs" var="b" />
<title><h:outputText value="#{b.currentDate}"/></title>
</head>
<body>
<h:outputText value="#{b.todayIs}"/>
<h:outputText value="#{showDate.today}">
<f:convertDateTime dateStyle="long"/>
</h:outputText>.
<h:form>
<h:selectOneMenu value="#{showDate.locale}">
<f:selectItems value="#{showDate.locales}"/>
</h:selectOneMenu>
<h:commandButton action="#{showDate.changeLocale}" value="#{b.change}"/>
</h:form>
</body>
</html>
Define the entry in the resource bundles:
msgs.properties msgs_zh.properties
currentDate=Current date currentDate=當前日期
todayIs=Today is:
todayIs=今日是:
change=Change
change=變更
Run it and it should work:

Localizing the full stop


There is still a minor issue here. The full stop used at the end of the sentence
above is the English one, not the Chinese one (yes, there is a Chinese full
264 Chapter 9 Supporting Other Languages

stop). To solve this problem, you could add a new entry to your properties files
for the full stop:
msgs.properties msgs_zh.properties
currentDate=Current date currentDate=當前日期
todayIs=Today is:
todayIs=今日是:
change=Change
fullStop=. change=變更
fullStop=。
Then change showdate.xhtml to:
<h:outputText value="#{b.todayIs}"/>
<h:outputText value="#{showDate.today}">
<f:convertDateTime dateStyle="long"/>
</h:outputText>
<h:outputText value="#{b.fullStop}"/>
This would work. But you are now breaking the sentence up into three parts:

Today is: March 30, 2008 .


This is getting too complicated. As an alternative, you could put the whole
sentence into a single entry:
msgs.properties msgs_zh.properties
currentDate=Current date currentDate=當前日期
todayIs=Today is: {0}.
todayIs=今日是: {0}。
change=Change
change=變更
You will put the date display into {0} at runtime. To do that, modify
showdate.xhtml as below. The <h:outputFormat> tag will create a UI Output
component (just like the <h:outputText> tag does) but associate it with a Format
renderer. The <f:param> will create a UI Parameter component and add it as a
child of the UI Output. The UI Parameter component by itself has no meaning at
all. It is entirely up to the parent component (the UI Output here) how to make
use of it. Here, the UI Output will let the Format renderer do the work, which will
use the value of the UI Output as a format pattern. Then it will find out the value
of the 0th UI Parameter and substitute it for the {0} in the pattern:
Chapter 9 Supporting Other Languages 265

<h:outputText value="#{b.todayIs}"/>
<h:outputText value="#{showDate.today}">
<f:convertDateTime dateStyle="long"/>
</h:outputText>.

<h:outputFormat value="#{b.todayIs}">
<f:param value="#{showDate.today}"/>
</h:outputFormat>

Format
renderer

UI Output
1: Read the value of the
value: "Today is: {0}." 0th parameter

2: Put it into the


format pattern
UI
Parameter

value: Date

Now, run the application and it should be like:

The full stop is working. However, the date display is no longer in the long style.
To fix it, modify the format pattern:
msgs.properties msgs_zh.properties
currentDate=Current date currentDate=當前日期
todayIs=Today is: {0, date, long}.
todayIs=今日是: {0, date, long}。
change=Change
change=變更
Now run it and it should work:
266 Chapter 9 Supporting Other Languages

BUG ALERT: Due to a bug in the JSF reference implementation, the Format
renderer is not using the locale of the view root and therefore it is displaying the
date in English.

Displaying a logo
Suppose that you'd like to display a logo on the showdate page. You have
created a logo (a GIF image) as shown below:

In addition, note the "4" in the logo. In Chinese, "4" doesn't mean "for" at all. In
fact, it is pronounced just like the word "death" in Chinese so people tend to
avoid it in names. So, you'd like to have a Chinese version of the logo:

To display the right version of the logo, save them into the WebContent folder
as logo_en.gif and logo_zh.gif respectively:
Chapter 9 Supporting Other Languages 267

Then modify showdate.xhtml:


268 Chapter 9 Supporting Other Languages

<html ...>
<head>
<f:loadBundle basename="multilang.msgs" var="b" />
<title><h:outputText value="#{b.currentDate}"/></title>
</head>
<body>
<h:graphicImage value="logo_en.gif"></h:graphicImage><p/>
<h:outputFormat value="#{b.todayIs}">
<f:param value="#{showDate.today}"/>
</h:outputFormat>
<h:form>
<h:selectOneMenu value="#{showDate.locale}">
<f:selectItems value="#{showDate.locales}"/>
</h:selectOneMenu>
<h:commandButton action="#{showDate.changeLocale}"
value="#{b.change}"/>
</h:form>
</body>
</html>
3: Copy
UI Graphic
1: Create a UI Graphic
component ...
<img src="logo_en.gif">
2: Render an <img>
element

http://localhost:8080/MultiLang/showdate.jsf

4: To get the image data, the browser


will use the "src" attribute as a relative
path from the current URL.
http://localhost:8080/MultiLang/logo_en.gif

Run it and it should display the English logo (regardless of the most preferred
locale):

To make it depend on the locale, modify it:


Chapter 9 Supporting Other Languages 269

"view" means the view root. You can


consider that there is such a pre-
<html ...> defined application scoped bean.
<head>
<f:loadBundle basename="multilang.msgs" var="b" />
<title><h:outputText value="#{b.currentDate}"/></title>
</head>
<body>
<h:graphicImage value="logo_#{view.locale}.gif"></h:graphicImage><p/>
<h:outputFormat value="#{b.todayIs}">
<f:param value="#{showDate.today}"/> Get the Locale object in the view root.
</h:outputFormat> Then convert it to a String so that you'll
<h:form> get something like "en" or "zh".
<h:selectOneMenu value="#{showDate.locale}">
<f:selectItems value="#{showDate.locales}"/>
</h:selectOneMenu>
<h:commandButton action="#{showDate.changeLocale}"
value="#{b.change}"/>
</h:form>
</body>
</html>

Now run it and it should work:

Making the locale change persistent


Suppose that you're most preferred locale is Chinese in the browser. Let's do an
experiment: Change the locale to English using the Change button, then press
Enter in the location bar in the browser. The page will be displayed in Chinese
again. It means the locale change is for temporary (for the current view root
only).
To make the change persistent say for the current session, you can store the
chosen locale into the session:
270 Chapter 9 Supporting Other Languages

public class ShowDate {


private String locale;
... Try to access the session
public String changeLocale() {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false);
if (session != null) {
session.setAttribute("multilang.locale", locale);
}
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
viewRoot.setLocale(new Locale(locale));
return null;
} If there is indeed a session, The name of the attribute
} store the chosen locale as a
session attribute.

The next step is to ask the view handler to use it if it exists. To do that, you need
to create your own view handler. Let's call create a MyViewHandler class in the
multilang package:
The Facelets view handler has
this method to get the most Extend the view handler
preferred locale from the provided by Facelets
request

public class MyViewHandler extends FaceletViewHandler {


public MyViewHandler(ViewHandler parent) {
super(parent);
}
public Locale calculateLocale(FacesContext context) {
HttpSession session = (HttpSession) context.getExternalContext()
.getSession(false);
if (session != null) {
String locale = (String) session.getAttribute("multilang.locale");
if (locale != null) {
return new Locale(locale); Check if a locale was stored in
} the session. If yes, use it
} instead of the one in the
return super.calculateLocale(context); request.
}
} Get the locale from the
request as usual

To tell the JSF engine use your own view handler, modify faces-config.xml:
Chapter 9 Supporting Other Languages 271

1: Choose it

2: Click "Browse" to choose


your MyViewHandler class

The corresponding XML code is:


<faces-config ...>
<managed-bean>
<managed-bean-name>showDate</managed-bean-name>
<managed-bean-class>multilang.ShowDate</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
<view-handler>multilang.MyViewHandler</view-handler>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>zh</supported-locale>
</locale-config>
</application>
</faces-config>
Save the file, restart the Tomcat instance and run it again. This time the locale
change should persist until you end the session (e.g., by restarting the browser).

Localizing validation messages


Remember that you can customize the validation messages using a resource
bundle. For example, you may have a file such as MyApp.properties in the
multilang package:
javax.faces.converter.DateTimeConverter.DATE={0} is an invalid {2}!
javax.faces.component.UIInput.REQUIRED=You must input {0}!
javax.faces.validator.LongRangeValidator.MINIMUM={1} must be at least {0}!
javax.faces.validator.LongRangeValidator.MINIMUM_detail={1} is invalid!
To use it, you need to say so in faces-config.xml:
<faces-config ...>
<application>
272 Chapter 9 Supporting Other Languages

<message-bundle>multilang.MyApp</message-bundle>
</application>
...
</faces-config>
It is simply a global resource bundle used by the built-in components. To
localize it for, say, Chinese, all you need is to create MyApp_zh.properties, just
like you would any other resource bundle.

Summary
To internationalize a page, you can extract the strings into resource bundles,
one for each supported language and a default resource bundle to act as the
parent. To look up a string for a certain key in a resource bundle, use
<loadBundle> to load bundle as a request attribute and access it like using an
EL expression like #{attribute-name.key}.
To determine the locale to use, the view handler will check the most preferred
locale as specified in the HTTP request and check if it is supported by your
application. If yes, it will store it into the view root. If no, it will use the default
locale specified in your application.
If you'd like to let the user specify a particular locale to use, overriding the most
preferred locale set in the browser, you may want to store it into the session and
provide a view handler subclass to retrieve it later.
If you'd like to fill in various slots in a pattern before outputting it, you can use
<outputFormat> and specify the value for each slot using a UI Parameter
component (created by a <param>). The meaning of UI Parameter is entirely
determined by its parent component.
To display an image, use a <graphicImage> tag. Its "value" attribute is a relative
path from the current URL. To internationalize an image, just internationalize its
"value" attribute.
Essential JSF, Facelets & JBoss Seam 273

References
• Apache Software Foundation. Tomcat 6 Documentation.
http://tomcat.apache.org/tomcat-6.0-doc/index.html.
• Facelets developers. Facelets - JavaServer Faces View Definition
Framework. https://facelets.dev.java.net/nonav/docs/dev/docbook.html.
• RedHat JBoss. JBoss Seam Reference Documentation.
http://docs.jboss.com/seam/2.0.1.GA/reference/en/html/
• RedHat JBoss. RichFaces Developer Guide. http://www.jboss.org/file-
access/default/members/jbossrichfaces/freezone/docs/devguide/en/html/in
dex.html.
• Sun Microsystems, Inc. Expression Language Specification v2.1.
http://jcp.org/en/jsr/detail?id=245.
• Sun Microsystems, Inc. Java™ Servlet Specification v2.4.
http://jcp.org/en/jsr/detail?id=154.
• Sun Microsystems, Inc. JavaServer™ Faces Specification v1.2 Rev A.
http://www.jcp.org/en/jsr/detail?id=252.
• Sun Microsystems, Inc. JavaServer Pages™ Specification v2.1.
http://jcp.org/en/jsr/detail?id=245.
274 Essential JSF, Facelets & JBoss Seam

Alphabetical Index
Action listener...............................................................................................108
Execution..................................................................................................59
Immediate execution...............................................................................127
Action method..............................................................................................131
AJAX............................................................................................................164
How it works............................................................................................166
Refreshing multiple components.............................................................171
Using a panel to refresh optional elements.............................................169
Apache MyFaces............................................................................................10
Application....................................................................................................109
Attribute..............................................................................................................
In request scope......................................................................................122
In session scope.....................................................................................269
Big5..............................................................................................................253
Calendar component......................................................................................77
Cascading style sheet..................................................................................102
Client id..........................................................................................................71
Component........................................................................................................
Visibility...................................................................................................164
Component tree.............................................................................................38
Component tree.................................................................................................
Created by JSP engine..............................................................................38
Components.xml..........................................................................................216
Connection profile........................................................................................210
Context path...................................................................................................24
Conversation......................................................................................................
Beginning and ending..............................................................................234
Temporary...............................................................................................232
Conversation scope......................................................................................231
Vs session scope....................................................................................231
Conversion.........................................................................................................
Customizing error message......................................................................73
Process.....................................................................................................67
Conversion errors...........................................................................................70
Cookies........................................................................................................139
CSS..............................................................................................................102
Database connection....................................................................................217
DataModel....................................................................................................225
Date converter................................................................................................66
Debugging a JSF application..........................................................................40
Decode...........................................................................................................51
Eclipse..............................................................................................................8
EL expression................................................................................................36
Accessing a resource bundle..................................................................254
Essential JSF, Facelets & JBoss Seam 275

Concatenating literals and child EL expressions.....................................169


Representing a method.............................................................................99
Encode...........................................................................................................39
Escaped Unicode encoding..........................................................................253
ExternalContext............................................................................................159
Facelets........................................................................................................180
Creating custom tags..............................................................................188
Forbidding direct access to xhtml files.....................................................193
Hiding JSF tags into HTML tags..............................................................191
IDE support.............................................................................................180
Variables.................................................................................................190
Vs JSP....................................................................................................191
Faces-config.xml............................................................................................34
FacesContext.................................................................................................99
FacesMessage...............................................................................................99
FacesMessages.................................................................................................
Detail message.......................................................................................103
Facet............................................................................................................116
HSQLDB......................................................................................................211
Hypersonic DB.............................................................................................214
I18n..............................................................................................................259
Image.................................................................................................................
Displaying a localized version..................................................................268
Internationalization.......................................................................................259
Javascript.....................................................................................................166
JavaServer Faces..........................................................................................10
JBoss IDE tools............................................................................................180
JBoss IDE Tools.................................................................................................
Editing Unicode properties file.................................................................253
JBoss Seam.................................................................................................208
JSF.................................................................................................................10
Installing....................................................................................................10
Markup independence...............................................................................87
Reference implementation........................................................................10
JSF Core tag lib..............................................................................................25
JSF HTML tag lib............................................................................................28
JSTL...............................................................................................................11
L10n.............................................................................................................259
Label..............................................................................................................73
ListDataModel..............................................................................................225
Locale...........................................................................................................257
Default.....................................................................................................257
Set in browser...........................................................................................69
Setting.....................................................................................................260
Setting for the whole session...................................................................269
Supported................................................................................................257
Localization..................................................................................................259
Logout..........................................................................................................158
276 Essential JSF, Facelets & JBoss Seam

Managed beans..............................................................................................34
Accessing in Java code...........................................................................109
Hooking up................................................................................................80
Managed property.....................................................................................83
Scopes......................................................................................................32
Message Bundle.............................................................................................74
Modal panel..................................................................................................172
Mojarra...........................................................................................................11
Navigation..........................................................................................................
Remaining in the current view.................................................................146
Setting the next view id in Java...............................................................154
Navigation case..............................................................................................54
Redirect...................................................................................................247
Navigation rule...............................................................................................54
In Seam...................................................................................................224
More specific one is considered first.......................................................205
Using wildcard.........................................................................................201
Outcome........................................................................................................54
Setting.......................................................................................................59
Using null as the outcome.......................................................................146
Pages.xml....................................................................................................224
Phase listener...............................................................................................154
Phases...............................................................................................................
Affected by conversion errors....................................................................70
Affected by validation errors......................................................................95
Apply Request Values...............................................................................51
Invoke Application.....................................................................................54
Process Validations.............................................................................67, 94
Render Response.....................................................................................49
Restore View.............................................................................................50
Update Model Values................................................................................51
Properties file.................................................................................................73
Protected pages.................................................................................................
Enforcing login........................................................................................154
Returning to after logging in....................................................................156
Redirect..............................................................................................................
Vs render.................................................................................................247
Renderer........................................................................................................87
Request..........................................................................................................32
Resource bundle..........................................................................................254
Access in an EL expression....................................................................254
Parent.....................................................................................................258
Resource key.................................................................................................73
RichFaces......................................................................................................78
Skins.......................................................................................................176
Seam............................................................................................................208
Authenticator component........................................................................239
Event.......................................................................................................244
Essential JSF, Facelets & JBoss Seam 277

Identity component..................................................................................239
Protected pages......................................................................................244
Redirect...................................................................................................247
Removing the session.............................................................................245
Runtime...................................................................................................210
Seam component...............................................................................................
Bijecting...................................................................................................229
Conversation scope.................................................................................231
Defining...................................................................................................219
Injecting...................................................................................................219
Outjecting................................................................................................223
Seam components.......................................................................................216
SelectItem......................................................................................................64
Serializable...................................................................................................132
Session..........................................................................................................32
Attribute...................................................................................................269
Get rid of.................................................................................................138
How to maintain......................................................................................139
Id.............................................................................................................140
JSESSIONID...........................................................................................139
Removing................................................................................................159
Session scoped managed beans.......................................................................
Must implement Serializable....................................................................132
Must not refer to other managed beans..................................................135
Tag lib............................................................................................................25
Tag library......................................................................................................25
Tomcat.............................................................................................................9
Tomcat instance.................................................................................................
Creating.....................................................................................................20
Modules.....................................................................................................60
Transaction..................................................................................................217
Transient......................................................................................................134
UI Column component..................................................................................116
UI Command component...............................................................................46
Rendered as a button..............................................................................125
Rendered as a link..................................................................................125
UI Data component............................................................................................
Client id...................................................................................................130
UI Form component.......................................................................................46
UI Graphic component.................................................................................267
UI Input component........................................................................................46
Rendered as a hidden field......................................................................137
Rendered as a password field.................................................................144
UI Load Bundle component..........................................................................254
UI Messages component................................................................................71
UI Output component.....................................................................................38
Using a format renderer..........................................................................264
UI Panel.............................................................................................................
278 Essential JSF, Facelets & JBoss Seam

Grid renderer...........................................................................................104
Group renderer........................................................................................104
UI Panel component.......................................................................................86
UI Parameter component.............................................................................264
UI Select One component..............................................................................63
Validating a combination of multiple input values.........................................108
Validation...........................................................................................................
Customizing the "required" message.......................................................77
Customizing error message......................................................................96
Localizing error messages......................................................................271
Marking input as required..........................................................................76
Process.....................................................................................................94
Skipping null input.....................................................................................97
Validator method.......................................................................................99
Validator.........................................................................................................94
Verbatim HTML code.....................................................................................38
View handler.................................................................................................154
Creating your own...................................................................................270
Facelets..................................................................................................187
Setting the locale.....................................................................................256
View root component......................................................................................38
Web.xml.............................................................................................................
Security constraint...................................................................................193
XHTML File..................................................................................................183
XHTML vs JSP.............................................................................................185
XML namespace..........................................................................................185
@AutoCreate...............................................................................................223
@DataModel................................................................................................227
@DataModelSelection..................................................................................228
@Factory......................................................................................................226
@In..............................................................................................................219
Specifying the component name.............................................................227
@Name........................................................................................................219
@Out............................................................................................................223
@Scope.......................................................................................................219
<a4j:commandButton> tag...........................................................................169
<a4j:commandLink> tag...............................................................................168
<a4j:support> tag.........................................................................................172
<actionListener> tag.....................................................................................108
<calendar> tag...............................................................................................79
<column> tag...............................................................................................116
<commandButton> tag...................................................................................45
<commandLink> tag.....................................................................................124
<convertDateTime> tag..................................................................................67
<dataTable> tag...........................................................................................116
<facet> tag...................................................................................................116
<form> tag......................................................................................................44
<graphicImage> tag.....................................................................................267
Essential JSF, Facelets & JBoss Seam 279

<inputHidden> tag........................................................................................137
<inputSecret> tag.........................................................................................144
<inputText> tag..............................................................................................45
<loadBundle> tag.........................................................................................254
<message> tag............................................................................................103
Setting the CSS class..............................................................................107
<messages> tag.............................................................................................71
Setting the CSS class..............................................................................102
<outputFormat> tag......................................................................................264
<outputText> tag............................................................................................28
<panelGrid> tag..............................................................................................86
<panelGroup> tag........................................................................................105
<param> tag.................................................................................................264
<rich:panel> tag............................................................................................176
<selectItems> tag...........................................................................................63
<selectOneMenu> tag....................................................................................62
<setPropertyActionListener> tag..................................................................127
<ui:component> tag......................................................................................189
<ui:composition> tag....................................................................................200
Providing multiple concrete parts............................................................204
<ui:define> tag..............................................................................................204
<ui:insert> tag...............................................................................................199
Specifying a name...................................................................................203
<validateDouble> tag......................................................................................97
<validateLength> tag......................................................................................97
<validateLongRange> tag..............................................................................95
<view> tag......................................................................................................27

Das könnte Ihnen auch gefallen