ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


JavaServer Pages, 2nd Edition

JSTL 1.0: Standardizing JSP, Part 1

by Hans Bergsten, author of JavaServer Pages, 2nd Edition
08/14/2002

June 11, 2002 started a new phase for JSP developers. That's when the JSP Standard Tag Library (JSTL) 1.0 specification was released. The Apache Taglibs project followed up with a reference implementation a few days later.

JSTL answers developers' demand for a set of standardized JSP custom actions to handle the tasks needed in almost all JSP pages, including conditional processing, internationalization, database access, and XML processing. This will speed up JSP development by more or less eliminating the need for scripting elements and the inevitable hard-to-find syntax errors, and by freeing up time previously spent on developing and learning zillions of project-specific custom actions for these common tasks.

This article is the first in a series of articles about how JSTL can simplify your life when using JSP, in applications large and small. In this article, I give you an overview of JSTL and show you how to use the most common JSTL actions. Future installments will focus on the internationalization and database access actions, how a servlet controller can interact with these actions, and how to use the JSTL classes as a base for your own custom actions. If you have suggestions for other areas you'd like to learn more about, please let me know.

JSTL Libraries Overview

JSTL 1.0 specifies a set of custom tag libraries based on the JSP 1.2 API. There are four separate tag libraries, each containing custom actions targeting a specific functional area. This table lists each library with its recommended tag prefix and default URI:

Description Prefix Default URI
Core c http://java.sun.com/jstl/core
XML Processing x http://java.sun.com/jstl/xml
I18N & Formatting fmt http://java.sun.com/jstl/fmt
Database Access sql http://java.sun.com/jstl/sql

The Core library contains actions for everyday tasks, such as including or excluding a piece of a page depending on a runtime condition, looping over a collection of items, manipulating URLs for session tracking, and correct interpretation by the target resource, as well as actions for importing content from other resources and redirecting the response to a different URL.

In This Series

JSTL 1.0: What JSP Applications Need, Part 2 -- Part 2 of our JSTL series focuses on internationization, localization, and database access.

JSTL 1.0: What JSP Applications Need, Part 3 -- In the final installment in this series on JSTL Hans Bergsten, author of JavaServer Pages, 2nd Edition, shows you how to leverage the JSTL classes when developing your own custom actions.

The XML library contains actions for -- you guessed it -- XML processing, including parsing an XML document and transforming it using XSLT. It also provides actions for extracting pieces of a parsed XML document, looping over a set of nodes, and conditional processing based on node values.

Internationalization (i18n) and general formatting are supported by the actions in the I18N & Formatting library. You can read and modify information stored in a database with the actions provided by the Database Access library.

Over time, you can expect all Web containers to include an implementation of the JSTL libraries, so no additional code will need to be installed. Until that happens, you can download and install the JSTL reference implementation (RI) instead. It's developed within the Apache Taglibs project as a library named Standard. The link to the Standard library page is included in the Resources section at the end of this article. Installing the RI is easy: just copy all of the JAR files from the distribution's lib directory to the WEB-INF/lib directory for your Web application. Note that JSTL 1.0 requires a JSP 1.2 container, so make sure your container is JSP-1.2-compliant before you try this.

JavaServer Pages

Related Reading

JavaServer Pages
By Hans Bergsten

To use a JSTL library, whether it's the implementation included with the container or the RI, you must declare the library using a taglib directive, just as you would for a regular custom tag library:

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

Note that you should always use the default URI, even though the JSP specification allows you to override it. A container is allowed to generate optimized code for the JSTL action in the class corresponding to a JSP page. This can result in better performance than when the generated code calls out to tag handlers through the standard API. It's only when you use the default URI, however, that a container is able to use an optimized implementation.

The JSTL Expression Language

In addition to the tag libraries, JSTL 1.0 defines a so-called Expression Language (EL). The EL is a language for accessing runtime data from various sources. Its syntax is considerably more user-friendly than Java, which is the only language supported directly by the JSP 1.2 specification. All JSTL actions recognize EL expressions in their attribute values, and custom actions may be developed to do the same. It is expected that the EL will be incorporated into the next version of the JSP specification to encourage its use for data access over the Java language. If so, you will be able to use EL expressions in an action attribute value, and even in template text.

If you've used JavaScript, you should feel right at home with the EL. The EL borrows the JavaScript syntax for accessing structured data as either a property of an object (with the . operator) or as a named array element (with the ["name"] operator). JavaBeans component properties and java.util.Map entries, using the key as the property name, can be accessed this way. Here are some examples:

${myObj.myProperty}$
${myObj["myProperty"]}$
${myObj[varWithTheName]}$

As shown here, an EL expression must always be enclosed within ${ and } characters. The first two expressions access a property named myProperty in an object represented by a variable named myObj. The third expression access a property with a name that's held by a variable. Instead of a single variable, this syntax can be used with any expression that evaluates to the property name.

The array access operator is also used for data represented as a collection of indexed elements, such as a Java array or a java.util.List:

${myList[2]}$
${myList[aVar + 1]}$

In addition to the property and array element operators and the arithmetic, relational, and logical operators, a special operator for testing if an object is "empty" or not can be used in an EL expression. The following table lists all operators:

Operator Description
. Access a property
[] Access an array/list element
() Group a subexpression
+ Addition
- Subtraction or negation of a number
/ or div Division
% or mod Modulo (remainder)
== or eq Test for equality
!= or ne Test for inequality
< or lt Test for less than
> or gt Test for greater than
<= or le Test for less than or equal
>= or gt Test for greater than or equal
&& or and Test for logical AND
|| or or Test for logical OR
! or not Unary Boolean complement
empty Test for empty value (null, empty string, or an empty collection)

What you don't find in the EL are statements such as assignments, if/else, or while. Action elements are used for this type of functionality in JSP, and the EL is not intended to be a general-purpose programming language, just a data access language.

Literals and variables are, of course, also part of the language. The EL provides the following literals, similar to what you find in JavaScript, Java, and other languages:

Literal Type Description
String Enclosed with single or double quotes. A quote of the same type within the string must be escaped with backslash: (\' in a string enclosed with single quotes; \" in a string enclosed with double quotes). The backslash character must be escaped as \\ in both cases.
Integer An optional sign (+ or -) followed by digits between 0 and 9.
Floating Point The same as an integer literal, except that a dot is used as the separator for the fractional part and an exponent can be specified as e or E, followed by an integer literal.
Boolean true or false.
Null null.

Any object in one of the JSP scopes (page, request, session, or application) can be used as a variable in an EL expression. For instance, if you have an bean with a firstName property in the request scope under the name customer, this EL expression represents the value of that bean's firstName property:

${customer.firstName}

But it doesn't stop there. The EL also makes request information and general container information available as a set of implicit variables:

Variable Description
param A collection of all request parameters as a single string value for each parameter.
paramValues A collection of all request parameters as a string array value for each parameter.
header A collection of all request headers as a single string value for each header.
headerValues A collection of all request headers as a string array value for each header.
cookie A collection of all request cookies as a single javax.servlet.http.Cookie instance value for each cookie.
initParams A collection of all application init parameters as a single string value for each parameter.
pageContext An instance of the javax.servlet.jspPageContext class.
pageScope A collection of all page scope objects.
requestScope A collection of all request scope objects.
sessionScope A collection of all session scope objects.
applicationScope A collection of all application scope objects.

The first five implicit variables in the table give you access to the parameter values, headers, and cookies for the current request. Here's an example of how to access a request parameter named listType and the User-Agent header:

${param.listType}
${header['User-Agent']}

Note how you must use the array syntax for the header, because the name includes a dash; with the property syntax, it would be interpreted as the value of the variable expression header.User minus the value of the variable named Agent.

The initParameter variable provides access to init parameters that are defined for the application in the web.xml file. The pageContext variable has a number of properties that provide access to the servlet objects that represent the request, response, session, application, etc. Look at the JSP specification to learn more about these properties.

The final four variables are collections containing all objects in each specific scope. You can use these to limit the search for an object to just one scope instead of searching all scopes, which is the default if no scope is specified. In other words, if there's an object named customer in the session scope, the first two expressions here find the same object and the third comes up empty:

${customer}
${sessionScope.customer}
${requestScope.customer}

All JSTL actions accept EL expressions as attribute values, for all attributes except var and scope, because these attribute values may be used for type checking at translation time in a future version. There's one additional JSTL action attribute that does not take an EL expression value, but it's only used in the XML library, so let's ignore that for now. One or more EL expressions can be used in the same attribute value, and fixed text and EL expressions can be mixed in the same attribute value:

First name: <c:out value="${customer.firstName}" />
<c:out value="First name: ${customer.firstName}" />

Before we jump in and look at examples using the Core actions, let me qualify something I said earlier: all JSTL actions in the EL library set accept EL expressions. There's actually a parallel set of JSTL libraries, referred to as the RT library set, that only accept the old-style Java expressions:

First name: <c_rt:out value="<%= customer.getFirstName() %>" /> 

I encourage you to use the EL libraries instead, but if you're curious, you can read about the RT libraries in the JSTL spec or in my book, JavaServer Pages (O'Reilly, 2nd edition 2002).

Conditional Processing and Iterations

Let's look at some examples of how you can use the JSTL conditional and iteration actions: <c:if>; the <c:choose>, <c:when>, and <c:otherwise> triple; and <c:forEach>. Along the way, we also use the basic output and variable setting actions: <c:out> and <c:set>.

<c:if> allows you to conditionally include, or process, a piece of the page, depending on runtime information. This sample includes a personal greeting if the user is a repeat visitor, as indicated by the presence of a cookie with the user's name:

<c:if test="${!empty cookie.userName}">
    Welcome back <c:out value="${cookie.userName.value}" />
</c:if> 

The test attribute value is an EL expression that checks if the cookie is present. The empty operator combined with the "not" operator (!) means it evaluates to true if the cookie is not present, causing the element body to be processed. Within the body, the <c:out> action adds the value of the cookie to the response. It's that easy.

Looping through a collection of data is almost as simple. This snippet iterates through a collection of rows from a database with weather information for different cities:

<c:forEach items="${forecasts.rows}" var="city">
   City: <c:out value="${city.name}" />
   Tomorrow's high: <c:out value="${city.high}" />
   Tomorrow's low: <c:out value="${city.low}" />
</c:forEach>

The EL expression for the items value gets the value of the rows property from an object represented by the forecasts variable. As you will learn in future articles, the JSTL database actions represent a query result as an instance of a class named javax.servlet.jsp.jstl.sql.Result. This class can be used as a bean with a number of properties. The rows property contains an array of java.util.SortedMap instances, each one representing a row with column values. The <c:forEach> action processes its body once for each element in the collection specified by the items attribute. Besides arrays, the action works with pretty much any data type that represents a collection, such as instances of java.util.Collection and java.util.Map.

If the var attribute is specified, the current element of the collection is made available to actions in the body as a variable with the specified name. Here it's named city and, since the collection is an array of maps, this variable contain a new map with column values every time the body is processed. The column values are added to the response by the same type of <c:out> actions that you saw in the previous example.

To illustrate the use of the remaining conditional actions, let's extend the iteration example to only process a fixed set of rows for each page request, and add "Previous" and "Next" links back to the same page. The user can then scroll through the database result, looking at a few rows at a time, assuming the Result object is saved in the session scope. Here's how to only process some rows:

<c:set var="noOfRows" value="10" />

<c:forEach items="${forecasts.rows}" var="city"
    begin="${param.first}" end="${param.first + noOfRows - 1}">
    City: <c:out value="${city.name}" />
    Tomorrow's high: <c:out value="${city.high}" />
    Tomorrow's low: <c:out value="${city.low}" />
 </c:forEach>

The <c:set> action sets a variable to the value specified by the value attribute; either a static value, as in this example, or an EL expression. You can also specify the scope for the variable with the scope attribute (page, request, session or application). In this example, I set a variable named noOfRows to 10 in the page scope (the default). This is the number of rows to show for each request.

The <c:forEach> in this example takes the same values for the items and var attributes as before, but I have added two new attributes.

Next we add the "Previous" and "Next" links:

<c:choose>
  <c:when test="${param.first > 0}">
     <a href="foreach.jsp?first=<c:out value="${param.first - noOfRows}"/>">
                Previous Page</a>
  </c:when>
  <c:otherwise>
     Previous Page
  </c:otherwise>
</c:choose>
<c:choose>
  <c:when test="${param.first + noOfRows < forecasts.rowsCount}">
     <a href="foreach.jsp?first=<c:out value="${param.first + noOfRows}"/>">
                Next Page</a>
  </c:when>
  <c:otherwise>
     Next Page
  </c:otherwise>
</c:choose>

The <c:choose> groups one or more <c:when> actions, each specifying a different Boolean condition. The <c:choose> action tests each condition in order and allows only the first <c:choose> action with a condition that evaluates to true to process its body. The <c:choose> body may also contain a <c:otherwise>. Its body is processed only if none of the <c:when> actions' conditions are true.

In this example, the first <c:when> action tests if the first parameter is greater than 0, i.e. if the page displays a row subset other than the first. If that's true, the <c:when> action's body adds a link back to the same page with the first parameter set to the index of the previous subset. If it's not true, the <c:otherwise> action's body is processed, adding just the text "Previous Page" as a placeholder for the link. The second <c:choose> block provides similar logic for adding the "Next Page" link.

URL Processing

The previous example works fine as long as cookies are used for session tracking. That's not a given; cookies may be turned off, or not supported, in the browser. It's therefore a good idea to enable the container to use URL rewriting as a backup for cookies. URL rewriting, as you may know, means putting the session ID in all URLs used in links and forms in the page. A rewritten URL looks something like this:

myPage.jsp;jspsessionid=ah3bf5e317xmw5

When the user clicks on a link like this, the session ID is sent to the container as part of the URL. The JSTL Core library includes the <c:url> action, which takes care of URL rewriting for you. This is how you can use it to improve the generation of the "Previous Page" link from the previous example:

<c:url var="previous" value="foreach.jsp">
  <c:param name="first" value="${param.first - noOfRows}" />
</c:url>
<a href="<c:out value="${previous}"/>">Previous Page</a>

The <c:url> supports a var attribute, used to specify a variable to hold the encoded URL, and a value attribute for the URL to be encoded. URL query string parameters can be specified using nested <c:param> actions. Special characters in the parameters specified by nested elements are encoded (if needed) and then added to the URL as query string parameters. The final result is passed through the URL rewriting process, adding a session ID if cookie-based session tracking is disabled. In this example, the fully encoded URL is then used as the href attribute value in the HTML link element.

The <c:url> also performs another nice service. As you may be aware, relative URLs in HTML elements must either be relative to the page that contains them or to the root directory of the server (if they start with a slash). The first part of a URL path for a JSP page is called the context path, and it may vary from installation to installation. You should therefore avoid hard-coding the context path in the JSP pages. But sometimes you really want to use a server-relative URL path in HTML elements; for instance when you need to refer to an image file that's located in an /images directory shared by all JSP pages, no matter where in the document structure the page reside. The good news is that if you specify a URL starting with a slash as the <c:url> value, it converts it to a server-relative path. For instance, in an application with the context path /myApp, the <c:url> action converts the path to /myApp/images/logo.gif:

<c:url value="/images/logo.gif" />

There are a few more actions related to URLs in the Core library. The <c:import> action is a more flexible action than the standard <jsp:include> action. You can use it to include content from resources within the same Web application, from another Web application in the same container, or from another server, using protocols like HTTP and FTP. The <c:redirect> action lets you redirect to another resource in the same Web application, in another Web application, or on a different server. Both actions are straightforward to use, so I leave it as an exercise for you to try them out.

Conclusion

In this installment, I have described the JSTL basic building blocks: the set of libraries and the Expression Language. I have also provided examples of how to use most of the actions in the Core library. You can download the JSTL reference implementation and use it with any JSP-1.2-compatible Web container to experiment with these actions. The RI includes a number of examples to help you get started.

In upcoming articles, we will look at the remaining JSTL libraries, including configuration options and how to control their behavior from a controller servlet when using an MVC framework such as Struts. I will also show you how classes defined by the JSTL specification can be used to simplify developing your own custom actions.

Resources

Hans Bergsten is the founder of Gefion Software and author of O'Reilly's JavaServer Pages, 3rd Edition.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.