Getting around JSF: The role of JSP
Learn how to use JavaServer Pages with JavaServer Faces
Summary
In this excerpt from JavaServer Faces in Action (Manning, November 2004), author Kito Mann explains how JavaServer Pages fits with JavaServer Faces. (1,800 words; December 13, 2004)
By Kito D. Mann
avaServer Faces (JSF) applications require some sort of display technology, such as JavaServer Pages. One of the cool things about JSP is the ability to extend it with custom tags. A custom tag is a special XML element backed by Java code, that can be used in addition to standard JSP elements or HTML elements. A custom tag can do almost anything: display the value of variables, parse XML, conditionally display parts of a page, access a database, and so on (whether or not anyone should be doing all of these things with JSP tags is a question for another day...). Their main purpose is to keep Java code out of the pages and allow frontend developers to use simple, familiar tags instead. A group of related custom tags forms a tag library.
JSF is integrated with JSP using custom tags. All of the JSF tags we've shown so far in this book —<h:inputText>
, <h:outputText>
, <h:form>
, <f:view>
, and so on—are custom tags. JSF implementations must support JSP with custom tag libraries that provide access to all of the standard components, renderers, validators, and converters. These libraries (included in the JSF JARs) are listed in the table below.
JSF custom tag libraries
|
All of the tags in these libraries must be named and implemented in a specific manner. This way, your JSP-based applications are guaranteed to be portable across different JSF implementations. Most IDEs can be used with JSP.
For the most part, using JSF with JSP is just a matter of using the JSF custom tag libraries. There are, however, some nuances you should be aware of, like using JSP includes.
Using JSP includes
One of JSP's key features is the ability to integrate content from multiple JSPs into a single page. This is often used for fun tasks like including a header or a footer. JSP supports two types of includes: dynamic and static. Dynamic includes (performed with the <jsp:include>
tag or the JSTL <c:import>
tag) access a resource at runtime. In this case, control is forwarded to the included JSP. The response from the included JSP is merged with the response sent back from the calling page. When changes are made to a dynamically included page, they automatically show up in all calling pages.
Static includes integrate the resource at translation time—when the page is morphed into Java code and compiled. The contents of the source page are essentially copied into the calling page. Changes made to the included content generally aren't automatically noticed by calling pages because they already have their own copy of the content. They have to be "touched" so that they can be recompiled with the new content. (JSP 2.0's implicit includes, which can be configured in web.xml
, are processed like static includes.)
JSF works with both types of JSP includes. For dynamic includes, there are two requirements:
- Included pages must be enclosed in a JSF
<f:subview>
core tag. This tag can either be in the included page or around the include statement. - All template text and non-JSF tags inside included pages should be enclosed with the JSF
<f:verbatimgt;
core tag.
So, let's say we had the following snippet in a JSP page:
<f:view>
...
<jsp:include page="foo.jsp"/>
...
</f:view>
Foo.jsp
might look like this:
<f:subview>
<h:outputText value="heyah!"/>
...
<f:verbatim>
<b>Template text.</b>
<customtag:dothis/>
</f:verbatim>
</f:subview>
As you can see, the entire included page is enclosed in an <f:subview>
tag, and all non-JSF tags and template text are enclosed in an <f:verbatim>
tag. Alternatively, we could move the <f:subview>
tag into the first page, around the <jsp:include>
tag.
Using a static include is much simpler. There are no restrictions—you don't even have to use the <f:subview>
tag.
In the last example, we showed a fictitious custom tag, <customtag:dothis>
, that performs some random task. This underscores an important point: you can use JSF with other JSP custom tags.
Using JSF with JSTL and other JSP custom tags
All of this talk about JSF's custom tag libraries is nice, but what if you have your own custom tags, or third-party ones? Or what if you're using the JSP Standard Tag Library (JSTL), which is a set of standard tags that do all of those neat things we just mentioned? For the most part, you can mix and match them with JSF tags. Faces tags can be nested within other tags and vice versa. Some products, like IBM's WebSphere Application Developer, encourage this approach, while others, like Sun's Java Creator Studio, opt for a pure JSF approach. Oracle's JDeveloper on the other hand, lets you mix and match, but also encourages the pure JSF approach.
Note: Whenever you nest a JSF tag inside a non-JSF custom tag, you must assign the JSF tag a component identifier.
Because JSTL is standard and familiar to many, we'll use it to demonstrate the use of JSF with custom tags. (If you're thirsty for general information on JSTL, check out Shawn Bayern's excellent book, JSTL in Action.) Let's start with the simple example (shown in Listing 1) that mixes and matches some JSTL tags with JSF tags. This code imports both JSF tag libraries and the core JSTL tag libraries.
Listing 1. Mixing JSTL tags with JSF tags
About the author
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<html>
<head>
<title>JSF in Action: JSTL Example 1 - Mixing JSF with other custom tags
</title>
</head>
<body bgcolor="#FFFFFF">
<f:view>
<h1>
<h:outputText value="Example of using JSF tags with other custom tags"/>
</h1>
<p>
<b>
<c:out value="Here's the value of your web.xml (don't do this at home):"/>
</b>
<blockquote>
<f:verbatim>
<c:import url="WEB-INF/web.xml"/>
</f:verbatim>
</blockquote>
</p>
</f:view>
</body>
</html></code>
In this example, both JSTL and JSF tags are nested within the JSF <f:view>
tag, which defines the start of the JSF component tree. The example uses the JSF HtmlOutputText
component (<h:outputText>
) and the JSTL <c:out>
tag to display text. A JSTL <c:import>
tag includes the system's web.xml
file in the page (this isn't exactly something you want to share with others, so don't do this on a real server). Because web.xml
is an XML file, the <c:import>
tag is nested in an <f:verbatim>
tag, which is a JSF UIOutput
component whose renderer escapes the XML so it can be displayed normally in an HTML page. This example doesn't do much, but it does demonstrate the ability to use different tags on the same page together.
Note that we nested a JSTL tag inside the JSF <f:verbatim>
tag. In general, it's easier to nest JSF tags inside other tags than vice versa. As a matter of fact, any component that displays its own children, like HtmlDataTable
and HtmlPanelGrid
, requires that any template text or nested tags be within an <f:verbatim>
tag.
What's great about using JSTL tags with JSF tags is that they both use similar expression languages to reference objects (this is true for JSP 2.0's expression language as well). This allows you to easily share data between JSTL and JSF tags in an intuitive manner. To illustrate this point, let's look at another example that allows the user to input a number into an HtmlInputText
control and then uses that value to display a string repeatedly with a JSTL <c:forEach>
tag. This code is shown in Listing 2.
Listing 2. Using JSF and JSTL tags together with the same backing bean
...
<f:view>
<jsp:useBean class="org.jia.examples.TestForm" id="exampleBean" scope="session"/>
<h1>
<h:outputText value="Example of using JSF and JSTL expression languages"/>
</h1>
<h:form>
<h:outputLabel for="inputInt">
<h:outputText value="How many times do you want to repeat the Oracle's prophecy?"/>
</h:outputLabel>
<h:inputText id="inputInt" value="#{sessionScope.exampleBean.number}"/>
<h:commandButton value="Go!"/>
<p>
<c:if test="${sessionScope.exampleBean.number > 0}">
<c:forEach begin="0" end="${sessionScope.exampleBean.number - 1}" var="count">
Queen Tracey will achieve world domination.<br>
</c:forEach>
</c:if>
</p>
</h:form>
...
</f:view>
...
Warning: If you're using JSP or JSTL expressions with managed beans, you need to ensure that the beans have been created first, either by a JSF expression, Java code, or your own custom tag. This is because these older expression languages don't know about JSF's Managed Bean Creation facility.
This listing references a JavaBean, called exampleBean
that has a number
property of type int
. An HtmlInputText
component is used to update the value of the bean's property based on user input. When the user clicks the Go! button (an HtmlCommandButton
component), the number
property is updated and the page is redisplayed. When this happens, the JSTL <c:forEach>
tag repeats the text displayed by a JSTL <c:out>
tag exampleBean.number
times. The <c:forEach>
tag only executes if exampleBean.number
is greater than 0; this is controlled by a JSTL <c:if>
tag.
You cannot use JSF component tags inside tags that iterate over their body, like the JSTL <c:forEach>
tag. The recommended workaround is to use the HtmlDataTable
component or another component iterates over a dataset or collection.
In this example, there are no JSF components nested inside the JSTL <c:if>
tag. But what happens if a component is displayed once and then hidden by a conditional tag like <c:if>
when the page is redisplayed? The first time the component is displayed, it will be added to the view. The second time, if the <c:if>
tag doesn't display the component, JSF will delete it from the view. This means that any input controls will lose their local values, and that you won't be able to reference these components (via client identifiers or in code). As an example, take a look at Listing 3, which is from the same page as Listing 2.
Listing 3. Conditionally displaying JSF components with JSTL tags
...
<h:form>
<h:outputText value="If you entered a number greater than 10,
two input controls will display below."/>
<p>
<c:if test="${sessionScope.exampleBean.number > 10}">
<h:outputLabel id="inputStringLabel"for="inputString">
<h:outputText id="outputStringLabel" value="Enter in your string.
JSF will remember the value unless this control is hidden."/>
</h:outputLabel>
<h:inputText id="inputString"/>
<h:commandButton value="Go!"/>
</c:if>
</p>
</h:form>
...
The JSTL <c:if>
tag will execute its body if the value of exampleBean.number
is greater than 10. If the body is executed, then all of the nested components will be added to the view and displayed. If not, the components will be removed (if they have been added previously). This is shown graphically in Figure 1. If you control visibility of components with JSTL conditional tags (or other custom tags), the components will be removed from the view if they're not displayed. This means that the components will forget their local values as well.
Figure 1. The JSTL <c:if> tag will execute its body if the value of exampleBean.number is greater than 10. Click on thumbnail to view full-sized image.
Figure 2 shows the output of the JSP page used for Listings 2 and 3. The value of the input field at the top (an HtmlInputText
component) is wired to the exampleBean.number
backing bean property, which is used by the JSTL <c:forEach>
tag to display a string exampleBean.number
times. In the bottom portion of the page, a JSTL <c:if>
tag shows a form with JSF components if exampleBean.number
is greater than 10. Otherwise, the components will not be displayed, and they are removed from the view (and the input control will lose its value).
Figure 2. The output of the JSP page shown in Listings 2 and 3. Click on thumbnail to view full-sized image.
You can achieve the same effect as the code in Listing 3 by placing these components in an HtmlPanelGroup
and setting its rendered
property to equal the same expression. An HtmlPanelGroup
is used as a container for multiple components. Here's an example:
<h:panelGroup rendered="#{sessionScope.exampleBean.number > 10}">
<h:outputLabel id="inputStringLabel2"for="inputString">
<h:outputText id="outputStringLabel2" value="Enter in your string. JSF
will remember the value."/>
</h:outputLabel>
<h:inputText id="inputString2"/>
<h:commandButton value="Go!"/>
</h:panelGroup>
If exampleBean.number
is greater than 10, this panel becomes visible. In this case, the components won't be deleted if they're not displayed. This is a good example of the types of things you can do with pure JSF tags without JSTL.
Tip: Even though custom tags like the ones provided by the JSTL provide a lot of functionality, if you're developing from scratch (or refactoring), you should first look to see if you can implement the desired behavior with standard JSF components. Using good components and well-designed backing beans, you can usually avoid the need for many JSTL tags in your pages. You can hide or display entire panels and do all sorts of powerful things with standard JSF.
Here are a few other interoperability constraints for using JSF tags with JSTL internationalization and formatting tags:
<fmt:parseDate>
and <fmt:parseNumber>
is not recommended. You should use the HtmlInputText
component with a DateTime
or Number
converter.
<fmt:requestEncoding>
tag, which is used to determine or specify the character encoding for the page, should not be used. Usually, JSF handles this automatically, and if you need to force a particular encoding, you should use the JSP page directive: <%page contentType="[contenttype];[charset]"%>
.
<fmt:setLocale>
tag shouldn't be used either. Because it doesn't know about JSF, it may cause your JSTL tags to use one locale and your JSF components may use another, which is a recipe for disaster. Instead, you should use JSF's internationalization features. To control the locale for a particular page, use the locale
property of the UIViewRoot
component. JSF's internationalization features work for both JSF and JSTL.
Combining JSF with the JSTL can be quite powerful. Custom tags that you have developed or obtained from third parties should work with JSF as well as the JSTL tags we've shown here. In general, though, you should stick with JSF tags when possible.
Kito D. Mann is a consultant specializing in enterprise architecture, mentoring, and development. A programmer since the tender age of 12, he has written several articles on Java-related technologies, and also speaks at user groups and conferences. He has consulted with several Fortune 500 companies and has been the chief architect of an educational application service provider. Mann is also the founder of the JSF Central community Website, and a member of the JSF 1.2 and JSP 2.1 expert groups. He holds a B.A. in computer science from Johns Hopkins University and lives in Stamford, Connecticut, with his wife, four cats, and two parrots. In his spare time, he enjoys making incomplete compositions with electronic music equipment.
Resources
https://secure.manning.com/catalog/view.php?book=mann
http://www.amazon.com/exec/obidos/ASIN/1930110529/javaworld
http://www.javaworld.com/channel_content/jw-jsp-index.shtml?