O'Reilly Book Excerpts: JavaServer Pages, 3rd Edition

Editor's note: We are currently running a four part series by Hans Bergsten that highlights some of the new features available in JavaServer Pages (JSP) 2.0. JSP 2.0: The new deal, part one described the new Expression Language. In part two Bergsten describes improvements in error handling and new features in the deployment descriptor. The following is an excerpt from his newly released O'Reilly book, "JavaServer Pages, 3rd Edition". This is Chapter 11 "Developing Custom Tag Libraries as Tag Files."

Starting with the 2.0 version of the JSP specification, custom tag library actions can be implemented in two ways: as Java classes or as regular text files containing JSP elements. In prior versions, custom actions could only be implemented as Java classes, putting them out of the reach of nonprogrammers. Another problem with the Java implementation of custom actions is that you're forced to printout HTML code with println() calls to produce complex content — the very problem JSP was supposed to solve.

In this chapter I show you how to develop custom actions as plain text files and package them as tag libraries that can be used in JSP pages.

Creating and Using a Tag File

A tag file is a text file that contains JSP elements implementing the functionality of a custom action. You must use a .tag[1] filename extension to identify this type of file to the web container. All JSP elements that you can use in a JSP file can also be used in a tag file, with exception to the page directive (a tag file is not a page). There are also a few JSP directives that are only allowed in a tag file, as you will see shortly. Apart from that, creating a tag file is no different than creating a JSP page. Once created and installed, a tag file is used the same as the custom actions implemented in Java that you've seen in previous chapters.

Example 11-1 shows a very simple tag file.

Example 11-1. Simple tag file (copyright.tag)

<%@ tag body-content="empty" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<jsp:useBean id="now" scope="application" class="java.util.Date" /> 
Copyright © ${now.year + 1900} My Company

This tag file inserts a copyright statement with the current year in the calling page. The first line is a tag directive. You may use attributes of this directive to specify a description, icon, or an example that a page-authoring tool can show the designer. Other attributes let you specify whether EL expressions should be processed, as well as various information related to scripting code, i.e., the same type of information as you specify with the page directive in JSP pages. All of these attributes are described in Appendix A.

In most cases, tag file authors only care about the attribute used in Example 11-1: body-content. This attribute defines how the custom action element's body should be handled, and that it must have one of these values: empty, scriptless (the default), or tagdependent. If it's empty (as in Example 11-1), trying to use a body for the custom action element results in a syntax error. The scriptless value means that the body can contain any JSP elements except the type of scripting elements described in Chapter 16. In other words, template text, EL expressions, standard actions, and custom actions are all allowed. As you will see later, the tag file can ask the container to process the actions in a scriptless body when and how often as it wants through the use of standard action named <jsp:doBody>. If the body-content attribute is set to tagdependent, the action element body is treated as pure template text (i.e., action elements and EL expressions in the body are not processed, just handled as plain text).

The rest of the tag file in Example 11-1 looks just like an ordinary JSP page. It declares that it uses the JSTL core library, a <jsp:useBean> standard action to create an instance of the java.util.Date class representing the current time (if it isn't already available in the application scope), and finally outputs static template text mixed with a dynamic value (the current year) generated by an EL expression: ${now.year + 1900}.[2]

Tag files can be placed directly in the web application structure under the WEB-INF/ tags directory or a subdirectory. Each directory containing tag files represents a separate tag library:

WEB-INF/tags/ 
mytags/ 
copyright.tag 
forEvenAndOdd.tag 
htmlFormat.tag 
motd.tag 
myothertags/ 
foo.tag 
bar.tag 

Here we have two tag libraries: mytags and myothertags. The mytags library contains the copyright.tag file from Example 11-1 plus three other tag files. By default, the name of the custom action implemented by the tag file is the filename minus the .tag extension, so the copyright.tag file represents a custom action named copyright in the mytags library.

A JSP page must declare that it uses a tag library represented by tag files in the web application structure with a slightly different taglib directive than what we've used in earlier chapters:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<html> 
<body bgcolor="white"> 
... 
<my:copyright/> 
</body> 
</html> 

Note that the tagdir attribute is used instead of the uri attribute. The value of the tagdir attribute is the context-relative path to the directory that contains the tag files for the library. It may seem redundant to have to specify the /WEB-INF/tags part of the path, since all tag library directories must start with this path. Regardless, the JSP specification group decided to require this to be consistent with other attributes taking path values.

When the JSP container processes this JSP page, it locates the copyright.tag file in the WEB-INF/tags/mytags directory and turns it into a format that the container can invoke. The conversion details are left open by the JSP specification, allowing container vendors to compete with smart implementations. Tomcat turns the tag file into a Java class and compiles it, but other implementations are possible (e.g., converting it to a proprietary data structure).

Tag files can also be packaged in a JAR file. It requires a bit more work and is primarily of interest for tag files intended to be reused in many applications, so let's defer the details to the end of this chapter. One thing to note at this time, though, is that when the tag files are packaged in a JAR file, the taglib directive is used with the uri attribute exactly as in the previous chapters. This means that tag files packaged in a JAR file are indistinguishable from custom actions implemented as Java classes. You can therefore implement the actions as tag files initially (because it's easier) and convert them to Java classes later (maybe to gain better performance) without having to make any changes in the JSP pages that use them.

Accessing Attribute Values

The tag file in Example 11-1 is too simple to illustrate all that you can do with tag files. For instance, most real-world tag files are controlled through attribute values set by the page author. You may recall from Chapter 7 that the <ora:motd> custom action has a category attribute for selecting the message category that messages should be picked from. Example 11-2 shows how a tag file implementation of the <ora:motd> action declares, accesses, and uses this attribute value.

Example 11-2. Using attributes in a tag file (motd.tag)

<%@ tag body-content="empty" %> 
<%@ attribute name="category" required="true" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<jsp:useBean id="mmb" class="com.ora.jsp.beans.motd.MixedMessageBean" /> 
<c:set target="${mmb}" property="category" value="${category}" /> 
${mmb.message} 

Each attribute must be declared with an attribute directive in a tag file. In Example 11-2, the category attribute is declared using an attribute directive with the name attribute set to category. The required attribute is set to true, meaning that the page author must specify a value for the category attribute; the container complains if the attribute is missing. The default value for required is false, so you can leave it out for attributes that are optional.

Another attribute of the attribute directive, not used in Example 11-2, is rtexprvalue. A value of true means that the author can specify the value either as a static string or as a request-time attribute value, such as an EL expression; false means the value must be a static string. The default value is true, so you only need to use this attribute if you absolutely require a static value.[3]

The value the page author assigns to an attribute shows up as a page scope variable in the tag file, with the same name as the attribute. This makes it easy to use it in an EL expression. In Example 11-2, a <c:set> action sets the category property in a MixedMessageBean (which contains the list of messages). The EL expression used as the value gets the category page scope variable that represents the category attribute.

It's important to note, however, that the page scope seen by the tag file is not the same as the page scope seen by the page that invokes the tag file — I sometimes call the page scope seen by the tag file the tag scope to make this distinction. By giving the tag file its own local page scope, there's no chance for confusion between the calling page and the tag file if they use the same names for page scope variables.

Using Undeclared Attributes

Occasionally, declaring all attributes for a tag file can be a hassle. Say you want to develop a tag file that generates an HTML table, and you want the page author to be able to specify all standard attributes that an HTML table element supports. That's a lot of attributes and the tag file would need to test for the existence of each one. A better approach for this scenario is to use the tag directive's dynamic-attributes attribute. This attribute declares that the tag file accepts any custom action element attribute. The attribute value is the name of a local page scope variable that holds a collection (a Map) with all undeclared attribute names and values. Example 11-3 shows an example of a tag file that uses this approach to generate a table with all request header values.

Example 11-3. Using undeclared attributes in a tag file (headers.tag)

<%@ tag body-content="empty" dynamic-attributes="dynattrs" %> 
<%@ attribute name="caption" required="true" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<table 
<c:forEach items="${dynattrs}" var="a"> 
${a.key}="${a.value}" 
</c:forEach> 
> 
<caption>${caption}</caption> 
<tr> 
<th>Name</th> 
<th>Value</th> 
</tr> 
<c:forEach items="${header}" var="h"> 
<tr> 
<td>${h.key}</td> 
<td>${h.value}</td> 
</tr> 
</c:forEach> 
</table>  

The dynamic-attributes attribute declares a variable named dynattrs to hold the undeclared attributes, and a JSTL <c:forEach> action loops through the collection and adds the name and value for each to the HTML <table> element's attribute list. As shown in Example 11-3, you can declare regular attributes in the same tag file. This example declares a mandatory attribute named caption, used to add a caption text for the table.

This is how you can use the tag file, shown in Example 11-3, in a JSP page:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<html> 
<head> 
<title>Headers</title> 
</head> 
<body bgcolor="white"> 
<my:headers caption="Request Headers" 
border="1" cellspacing="0" cellpadding="5" /> 
</body> 
</html>  

The action element for the tag file defines values for the mandatory caption attribute plus three undeclared attributes: border, cellspacing, and cellpadding.

[1] If you write the tag file in XML format, as described in Chapter 17, you must instead use the .tagx extension.

[2] The year property of a java.util.Date (represented by the getYear() method) contains the current year minus 1900, so here I add 1900 to get the real year.

[3] The convention established by JSTL is that only var and scope attributes should have rtexprvalue set to false. These attributes may need to be available in the translation phase (hence, have static string values) in a future version of the JSP specification to allow for additional syntax checking and optimizations.

Processing the Action Body

So far, the tag files we've looked at ignore (or actually forbid) the body of the custom action element used to invoke them, but the body is often an important part of the equation. One example is a conditional custom action, such as a variation of the <c:if> JSTL action. It needs to process the body if the condition is true. Another example is a custom action that transforms the body in some way or simply uses it as input.

Let's develop a custom action that transforms its body content. It first converts all characters that have special meaning in HTML and XML to the corresponding character entity codes (e.g., < to &lt;), and then converts special proprietary codes into HTML elements. A custom action like this can be used to process user input in an online forum to protect it against cross-site scripting attacks while still allowing for limited formatting of the messages. Here's how you can use this custom action in a JSP page:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<%-- Create test data --%> 
<c:set var="message"> 
This is just a lot of text that the browser will format to 
fit the browser window. Attempts to <blink> add HTML elements 
are dealt with by conversion to character entities. 
[code] 
This part I want the browser to leave alone, so that 
all my indentations are left intact: 
public class Foo { 
public String getBar( ) { 
return bar; 
} 
} 
[/code] 
And then some regular text again. 
</c:set> 
<html> 
<head> 
<title>Online Forum</title> 
</head> 
<body bgcolor="white"> 
<h1>Online Forum</h1> 
Here's a formatted message: 
<p> 
<my:htmlFormat> 
${message} 
</my:htmlFormat> 
</p> 
</body> 
</html> 

This page first saves test data containing text, an HTML element, and the proprietary formatting codes in a variable named message. In a real application, the text would likely come from a database or some other external source. It then processes the text with the <my:htmlFormat> custom action.

The result is shown in Figure 11-1. Note how the <blink> HTML element is displayed instead of causing most of the text to blink, and how the formatting is preserved for all text between the proprietary [code] and [/code] tags.


Figure 11-1. Result of text processing with a custom action

Example 11-4 shows the tag file that implements the <my:htmlFormat> custom action.

Example 11-4. Processing the body (htmlFormat.tag)

<%@ tag body-content="scriptless" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%-- Capture the body evaluation result in a variable --%>
<jsp:doBody var="bodyRes" />

<%-- Convert special characters to character entities --%>
<c:set var="escapedBody" value="${fn:escapeXml(bodyRes)}" />

<%-- Replace "[code]/[/code]" with "
/
" --%> <c:set var="convBody" value="${fn:replace(escapedBody, '[code]', '
')}" />
<c:set var="convBody" 
  value="${fn:replace(convBody, '[/code]', '
')}" /> <%-- Output the result --%> ${convBody}

Note that the tag directive in Example 11-4 sets the body-content attribute to scriptless. As I mentioned earlier, this means that the page author is allowed to put template text, standard actions and custom actions, in the body but not scripting elements (i.e., Java code).

It's after the directive elements that this example gets interesting; here's a standard action that we have not discussed before: <jsp:doBody>. This action can only be used in tag files. It evaluates the body of the custom action element, meaning that all action elements (if any) in the body are called and the output they produce is mixed with the template text (if any). The result is saved in a variable, using the var attribute to name the variable. This attribute is optional, as shown in Table 11-1, and you can use the varReader attribute as an alternative. If you don't specify any of these attributes, the result is added to the page invoking the custom action.

Table 11-1. Attributes for <jsp:doBody>

Attribute name Java type Dynamic value accepted Description
var String No Optional. The name of the variable to hold the body evaluation result as a String.
varReader String No Optional. The name of the variable to hold the body evaluation result as a java.io.Reader.
scope String No Optional. The variable scope; one of page, request, session, or application. Default is page.

The difference between the var and varReader attributes is the type of Java object used for capturing the result. The var attribute captures it as a String and is sufficient for most cases. When the varReader attribute is used, the result is captured as a java.io.Reader object instead. For large results, this can be slightly more efficient when combined with an action or function for the transformation that reads its input from a Reader. Along with one of var or varReader, you can also specify the scope for the variable with the scope attribute.

The rest of the tag file in Example 11-4 transforms the captured body. First it uses the JSTL fn:escapeXml( ) function to convert all special characters to character entity codes, and then it replaces all occurrences of [code] and [/code] with the HTML <pre> and </pre> tags using the JSTL fn:replace( ) function, to preserve formatting in these sections. Finally, the converted body evaluation result is added to the calling page with a simple EL expression.

Processing Fragment Attributes

Processing the custom action body is easy and powerful as you can see, but wait, there's more! The custom action body is actually just a special case of what's called a JSP fragment in the JSP specification. A JSP fragment is an executable representation of a set of dynamic elements (actions and EL expressions), optionally mixed with template text. When the tag file invokes the fragment, all the dynamic elements in the fragment are executed. Since the elements have access to the current values of all scoped variables, the result typically differs from invocation to invocation, and the tag file can invoke it any number of times (e.g., once or none for a conditional action or multiple times for an iteration action).

In Example 11-4, the <jsp:doBody> action invokes the special fragment representing a custom action element body, but named fragments can also be provided as custom action attributes and be invoked by the tag file. Such fragments are invoked with the <jsp:invoke> action, described in Table 11-2.

Table 11-2. Attributes for <jsp:invoke>

Attribute name Java type Dynamic value accepted Description
fragment String No Mandatory. The name of the fragment to invoke.
var String No Optional. The name of the variable to hold the body evaluation result as a String.
varReader String No Optional. The name of the variable to hold the body evaluation result as a java.io.Reader.
scope String No Optional. The variable scope; one of page, request, session, or application. Default is page.

Let's develop a variant of the JSTL <c:forEach> action to illustrate how you can use named fragments. Say you want to loop through all the elements in a collection to generate an HTML table, and you want to render even rows one way and odd rows another. Here's a page that solves this problem by using a custom action with separate fragment attributes for even and odd rows:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<html> 
<head> 
<title>Even and Odd Rows</title> 
</head> 
<body bgcolor="white"> 
<h1>Even and Odd Rows</h1> 
<table> 
<my:forEvenAndOdd items="a,b,c,d,e"> 
<jsp:attribute name="even"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="red"><td>${counter}: Even Row</td></tr> 
</jsp:attribute> 
<jsp:attribute name="odd"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="blue"><td>${counter}: Odd Row</td></tr> 
</jsp:attribute> 
</my:forEvenAndOdd> 
</table> 
</body> 
</html>

A fragment attribute value is defined using the <jsp:attribute> action introduced earlier. The body of this action element makes up the content of the fragment. In the page shown here, each fragment attribute values contain a JSTL <c:set> action for incrementing a counter and HTML table row and cell elements for showing the counter's value plus the static text "Even Row" and "Odd Row", respectively. The fragments also set different row background colors to make the differences clear. The result of processing this page is shown in Figure 11-2.

Note how the current value of the counter page scope variable is used for each new row, and how the rows alternate between the even and odd fragments. Example 11-5 shows the tag file for the <my:forEvenAndOdd> custom action.


Figure 11-2. Representing even and odd rows as fragments

Example 11-5. Using fragment attributes (forEvenAndOdd.tag)

<%@ tag body-content="empty" %> 
<%@ attribute name="items" rtexprvalue="true" required="true" %> 
<%@ attribute name="even" fragment="true" required="true" %> 
<%@ attribute name="odd" fragment="true" required="true" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<c:forEach items="${items}" varStatus="status"> 
<c:choose> 
<c:when test="${status.count % 2 == 0}"> 
<jsp:invoke fragment="even" /> 
</c:when> 
<c:otherwise> 
<jsp:invoke fragment="odd" /> 
</c:otherwise> 
</c:choose> 
</c:forEach>  

The tag directive specifies that the body must be empty; in this example, it must only contain the <jsp:attribute> elements (no template text of other elements), and they are considered alternatives to regular element attributes, not body content.

To tell the container to use an executable fragment as the attribute value, the attribute must be declared as such. Note that the attribute directive's fragment attribute is set to true for both the even and odd attributes. Otherwise the container evaluates the <jsp:attribute> body once and sets the attribute to the resulting value, as described in Chapter 6.

After the directives in Example 11-5, JSTL actions are used to loop through the list of items, and decide whether it's an even or odd row. The <jsp:invoke> action then invokes the appropriate fragment. The result is what you see in Figure 11-2.

Exposing Data to the Calling Page Through Variables

Attributes provide input to a custom action, but sometimes you also need to give the page that contains the custom action access to data produced by the custom action. For instance, the <my:forEvenAndOdd> action is not all that useful unless the page can access the current iteration value in the fragments for even and odd rows. To handle this requirement, data can be passed from a custom action to the caller by exposing it through declared variables.

Example 11-6 shows a version of the tag file from Example 11-5 that's been extended to expose the current iteration value as a variable named current. All differences between the examples are highlighted.

Example 11-6. Exporting data through variables (forEvenAndOdd2.tag)

<%@ tag body-content="empty" %> 
<%@ attribute name="items" rtexprvalue="true" required="true" %> 
<%@ attribute name="even" fragment="true" required="true" %> 
<%@ attribute name="odd" fragment="true" required="true" %> 
<%@ variable name-given="current" variable-class="java.lang.Object" 
scope="NESTED" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<c:forEach items="${items}" varStatus="status" var="current"> 
<c:choose> 
<c:when test="${status.count % 2 == 0}"> 
<jsp:invoke fragment="even" /> 
</c:when> 
<c:otherwise> 
<jsp:invoke fragment="odd" /> 
</c:otherwise> 
</c:choose> 
</c:forEach>  

The variable directive declares the variable. The name-given attribute specifies its name and the variable-class attribute its type. (Here I use the most generic class possible, java.lang.Object, because the collection to iterate over can contain elements of any type.)

The scope attribute accepts one of three values: AT_BEGIN, AT_END, or NESTED. It controls where the caller sees the variable. Despite its name, it has nothing to do with the scopes we've talked about earlier (page, request, session, and application), so visibility would have been a better name for this attribute. If it's set to AT_BEGIN, the variable is visible to the caller immediately after the start tag for the custom action element. If the attribute is set to AT_END, the variable is visible after the end tag. NESTED means it's only visible between the start and end tags.

To make the data visible to the caller, the tag file sets a page scope variable with the name declared by the variable directive. I told you earlier that the tag file has its own page scope, separate from the caller, so the container must do a bit of magic for this to work. For a variable declared as AT_BEGIN or NESTED, it copies the value of the variable in the tag file's page scope to the caller's page scope before invoking a fragment. If the variable is declared as AT_BEGIN or AT_END, it copies the value before exiting the tag file. In the case of a NESTED variable, it also saves and restores the value of the caller's page scoped variable with the same name, if any, before entering and exiting the tag file. Don't worry if this sounds confusing at first; it actually ends up working as you would expect it to.

The tag file in Example 11-6 exposes a variable named current, containing the value of the current iteration value. The local variable is set indirectly with help of the var attribute of the <c:forEach> action. As you may recall, the <c:forEach> action makes the current iteration value available in the page scope variable named by this attribute. By setting the name of the <c:forEach> variable to the name of the declared tag file variable, the variable value set by the <c:forEach> action is also exposed to the caller.

With the new version of the tag file, I can use it to display the current iteration value in each row:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<html> 
<head> 
<title>Even and Odd Rows</title> 
</head> 
<body bgcolor="white"> 
<h1>Even and Odd Rows</h1> 
<table> 
<my:forEvenAndOdd2 items="a,b,c,d,e"> 
<jsp:attribute name="even"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="red"><td>${counter}: Even Row: ${current}</td></tr> 
</jsp:attribute> 
<jsp:attribute name="odd"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="blue"><td>${counter}: Odd Row: ${current}</td></tr> 
</jsp:attribute> 
</my:forEvenAndOdd2> 
</table> 
</body> 
</html>  

Note how the exposed variable is used in EL expressions in both fragments. There's still a problem here: the exposed variable name is hardcoded into the tag file. This may be okay in some cases, but it's better if the variable name can be specified using an attribute, just as you can pick a name with the var attribute for all JSTL actions that expose data. Fortunately, there's a solution, shown in Example 11-7.

Example 11-7. Letting the page author specify the variable name (forEvenAndOdd3.tag)

<%@ tag body-content="empty" %> 
<%@ attribute name="items" rtexprvalue="true" required="true" %> 
<%@ attribute name="var" rtexprvalue="false" required="true" %> 
<%@ attribute name="even" fragment="true" required="true" %> 
<%@ attribute name="odd" fragment="true" required="true" %> 
<%@ variable name-from-attribute="var" alias="current" 
variable-class="java.lang.Object" scope="NESTED" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<c:forEach items="${items}" varStatus="status" var="current"> 
<c:choose> 
<c:when test="${status.count % 2 == 0}"> 
<jsp:invoke fragment="even" /> 
</c:when> 
<c:otherwise> 
<jsp:invoke fragment="odd" /> 
</c:otherwise> 
</c:choose> 
</c:forEach> 

Instead of the name-given attribute used in the previous example, I use the namefrom-attribute and alias attributes of the variable directive in Example 11-7. The name-from-attribute attribute value is the name of the custom action attribute used to name the variable. The named attribute (var in this example) must be declared as required and must not accept a request time value. The alias attribute value declares the name of the tag file's local page scope variable, which the JSP container copies to the caller's page scope as described earlier. The aliasing trick is needed because the page author can assign any name for the variable when she uses the custom action, but a fixed name must be used when developing the tag file. The rest of Example 11-7 is identical to Example 11-6, but I can now specify the variable name in the calling page like this:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<html> 
<head> 
<title>Even and Odd Rows</title> 
</head> 
<body bgcolor="white"> 
<h1>Even and Odd Rows</h1> 
<table> 
<my:forEvenAndOdd3 items="a,b,c,d,e" var="anyName"> 
<jsp:attribute name="even"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="red"><td>${counter}: Even Row: ${anyName}</td></tr> 
</jsp:attribute> 
<jsp:attribute name="odd"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="blue"><td>${counter}: Odd Row: ${anyName}</td></tr> 
</jsp:attribute> 
</my:forEvenAndOdd3> 
</table> 
</body> 
</html>  

Aborting the Page Processing

In Chapter 9, I described how using the <jsp:forward> action or the JSTL <c:redirect> action shifts processing from the current page to the page specified by the page attribute, effectively aborting processing of the current page. Custom actions implemented as Java classes can cause the same thing to happen.

There's no directive or similar mechanism that a tag file can use to explicitly abort processing, but using <jsp:forward>, <c:redirect>, or a custom action that aborts page processing in a tag file has the same effect; both the tag file processing and the processing of the page that invokes the tag file stop after it aborts the processing. You can use this feature to, for instance, develop a smart forwarding action that decides which page to forward to based on runtime conditions, such as the time of the day, the current user, or the type of browser accessing the page.

Packaging Tag Files for Easy Reuse

All examples in this chapter use a taglib directive with a tagdir attribute to specify the directory that contains the tag files. While this is handy for custom actions implemented as tag files in an application you control, it's not as easy as one would want for deployment and use of the tag library in third-party applications. As you may recall from Chapter 7, it's very easy to deploy a tag library packaged as a JAR file; just put the JAR file in the WEB-INF/lib directory and use the default URI as the uri attribute value in the taglib directive.

You can do the same with a tag library developed as tag files, but in this case you must also create a Tag Library Descriptor (TLD) and include it in the JAR file. I described the purpose of the TLD briefly in Chapter 7, but let's take a closer look at it here. Example 11-8 shows the TLD for a tag library with some of the tag files we've developed in this chapter.

Example 11-8. TLD for tag files

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" 
version="2.0"> 
<tlib-version>1.0</tlib-version> 
<short-name>my</short-name> 
<uri>mytaglib</uri> 
<tag-file> 
<name>copyright</name> 
<path>/META-INF/tags/mytags/copyright.tag</path> 
</tag-file> 
<tag-file> 
<name>forEvenAndOdd</name> 
<path>/META-INF/tags/mytags/forEvenAndOdd.tag</path> 
</tag-file> 
<tag-file> 
<name>htmlFormat</name> 
<path>/META-INF/tags/mytags/htmlFormat.tag</path> 
</tag-file> 
</taglib>  

As you can see, this file is an XML document. Don't worry about the <taglib> element attributes; just copy the element exactly as its shown here into your TLD. I describe it in more detail in Chapter 21, along with the TLD elements not covered here.

The first elements provide information about the tag library itself. The <tlib-version> element contains the version of this tag library and the <short-name> element contains the default namespace prefix for this library. An authoring tool may use the default namespace prefix when it generates the taglib directive and action elements, but a page author can pick different prefixes if needed, as described in Chapter 7. The <uri> element is important. This element declares the default URI (identifier) for the library. The value you use for this element is the value that must be used as the uri attribute value for the taglib directive in the JSP pages to take advantage of the auto-deploy feature, as I described in Chapter 7.

Next comes a <tag-file> element for each tag file. The nested <name> element gives the name for the custom action. It's typically the same as the filename (minus the .tag extension) but you can specify a different name if you want. The <path> element holds the path within the JAR file to the tag file. It must start with /META-INF/tags/. The TLD file itself must also be located in the /META-INF directory in the JAR file, so you need to create a directory structure like this for the tag files and the TLD:

META-INF/ 
mytags.tld 
tags/ 
mytags/ 
copyright.tag 
forEvenAndOdd.tag 
htmlFormat.tag 

Then create the JAR file with the jar command (included with the Java SDK) like this:

C:\> jar cvf mytags.jar META-INF 

This creates a JAR file named mytags.jar containing the contents of the META-INF directory structure. You can now place this JAR file in the WEB-INF/lib directory of any web application that needs the library. When you restart the web container, it will locate the JAR file and its TLD, so that you can identify the tag library in a JSP page with a taglib directive like this:

<%@ taglib prefix="my" uri="mytags" %>

In other words, use the uri attribute with the default URI for the library (declared in the TLD), just as for the JSTL libraries we've used in previous chapters, instead of the tagdir attribute.

You can also use a TLD to identify tag files placed directly in the filesystem, i.e., not packaged in a JAR file. Doing this allows you to use the uri attribute instead of the tagdir attribute for the taglib directive, potentially saving you from changing the taglib directives in a number of JSP pages if you eventually decide to package the tag files in a JAR file for easier reuse in other applications. With this approach, you put the tag files under WEB-INF/tags (or a subdirectory) just as in the first examples in this chapter, but you also place a TLD in the WEB-INF directory (or a subdirectory, like tlds):

WEB-INF/ 
tlds/ 
mytags.tld 
tags/ 
mytags/ 
copyright.tag 
forEvenAndOdd.tag 
htmlFormat.tag 

In this case, the <path> elements in the TLD must specify the context-relative path to the tag files in the filesystem:

<tag-file> 
<name>copyright</name> 
<path>/WEB-INF/tags/mytags/copyright.tag</path> 
</tag-file>  

Other than that, the TLD is the same as in Example 11-7.


Return to ONJava.com.