ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button O'Reilly Book Excerpts: JavaServer Pages, 3rd Edition

Developing Custom Tag Libraries as Tag Files

Related Reading

JavaServer Pages
By Hans Bergsten

by Hans Bergsten

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.

Pages: 1, 2, 3

Next Pagearrow