Skip to main content

Easy Custom Tags with Tag Files, Part 2

November 25, 2003

{cs.r.title}






The new feature of JSP 2.0, tag files, makes your life as a JSP programmer much easier,
as you learned in the first part of this series. In the second part, we'll cover more advanced features of the tag files, such as the variable directive, the jsp:doBody, and jsp:invoke actions, as well as how to package tag files into a JAR so they can be easily deployed.

The variable Directive

It is sometimes useful to expose values in a tag file to the calling JSP page. This is
possible through the use of the variable directive in the tag file. In a tag file, the variable directive is analogous to the variable element in a tag library descriptor and defines the details of a variable in the tag handler that is accessible from the calling JSP page.
Because a tag file can have multiple variable directives, it is possible to provide multiple
values to the calling JSP page. Compare variable with the attribute directive that you use
to pass a value from the JSP page to the tag file.

The syntax of the variable directive is as follows:

<%@ variable (attribute="value")* %>

The syntax can be expressed in the following more informal form:

<%@ variable attribute1="value1" 
attribute2="value2" ... %>

The list of attributes for the variable directive is given in Table 1.

Table 1. The variable directive's attributes

Attribute Description
name-given The variable name that will be available to
the scripting language or the EL
expressions in the calling JSP page. If the
name-from-attribute is used, the
name-given attribute must not be present, and
vice versa. The value for name-given must
not be the same as any of the attributes in
this tag file.
name-from-attribute This attribute is similar to the name-given
attribute. However, the value for this
attribute is the name of an attribute whose
value at the start of the tag invocation will
give the name of the variable. A translation
error will result if both or neither of the
name-given and name-from-attribute
attributes is specified.
alias A locally scoped attribute to hold the value
of this variable.
variable-class The type of this variable. The default is
java.lang.String.
declare Indicates whether the variable is declared
in the calling page or tag file after this tag
invocation. The default is true.
scope The scope of the scripting variable defined.
The possible values are AT_BEGIN,
AT_END, and NESTED (default).
description The description of this variable.

You may ask why you need the variable directive at all if you can just output the
processing result to the JspWriter of the calling JSP page. This is true, but simply
sending output to the JspWriter deprives the calling JSP page of some flexibility in using processing results. As an example, the firstTag.tag file in Part 1 of this
article outputs the server's current date in long format. Suppose you want to also provide
the server's current date in short format, then you must write another tag file. Having two
tag files for similar functionality creates unnecessary maintenance headaches. Alternatively,
you can expose two variables in the tag file, longDate and shortDate.
The following tag file varDemo.tag provides the server's current date in two forms: long and short.
It has two variables called longDate and shortDate.

<%@ tag import="java.util.Date" 
import="java.text.DateFormat"%>
<%@ variable name-given="longDate" %>
<%@ variable name-given="shortDate" %>
<%
  Date now =
new Date(System.currentTimeMillis());
  DateFormat longFormat =
DateFormat.getDateInstance(DateFormat.LONG);
  DateFormat shortFormat =
DateFormat.getDateInstance(DateFormat.SHORT);
  jspContext.setAttribute(
"longDate", longFormat.format(now));
  jspContext.setAttribute(
"shortDate", shortFormat.format(now));
%>
<jsp:doBody/>

Notice that you set a variable using the setAttribute method on the JspContext object of
the tag. The jspContext implicit object represents this object. The set tag in the JSP
Standard Tag Library (JSTL) encapsulates this functionality. If you are familiar with
JSTL, you can use this tag instead of the setAttribute method.

Also note that you must use the jsp:doBody standard action to invoke the tag body. For
more information, see the sections jsp:doBody and jsp:invoke below.
To test the varDemo.tag file, use the varDemoTest.jsp page:

<%@ taglib prefix="tags" 
tagdir="/WEB-INF/tags" %>
Today's date:
<br/>
<tags:varDemo>
In long format: ${longDate}
<br/>
In short format: ${shortDate}
</tags:varDemo>

You can invoke the varDemoTest.jsp page using the following URL (assuming, as before, that you're running Tomcat on port 8080):

http://localhost:8080/tagfiles/varDemoTest.jsp

Figure 1 shows the result.

The result of varDemoTest.jsp
Figure 1. The result of varDemoTest.jsp

There are many cases where you will want to use variables. As another example,
suppose you want to write a custom action that fetches the details of a product from the
database given the product identifier. Here is what you can do. You can use an attribute
to pass the product identifier. For each piece of information you provide a variable to
store that information. That gives you variables like name, price,
description, imageUrl, and so on.

jsp:doBody

The jsp:doBody standard action can be used only from inside a tag file. You use it to
invoke the body of a tag. You've seen jsp:doBody in action in the tag file in varDemo.tag. In this section you'll learn jsp:doBody in more detail.

The jsp:doBody action can have attributes. You use these attributes if you want to direct
the output of the tag invocation to a variable. If used without an attribute, the jsp:doBody
standard action writes the output to the JspWriter object of the calling JSP page. The list
of the jsp:doBody standard action's attributes are given in Table 2.

Note: All the attributes of jsp:doBody are optional.

Table 2. The attributes of jsp:doBody

Attribute Description
var The name of a scoped attribute to store the output of the tag body
invocation. The value is stored as a java.lang.String object. Only one of the
var or varReader attribute can be present.
varReader The name of a scoped attribute to store the output of the tag body
invocation. The value is stored as a java.io.Reader object. Only one of the
var or varReader attribute can be present.
scope The scope for the resulting variable.

The following example shows how to use jsp:doBody to invoke the tag body and store
the output in a session-scoped variable called referer. The scenario is like this: you have
a web site that sells toys and you advertised your web site heavily in many search
engines. Of course, you want to know which search engine is the most effective in
generating sales. You do this by recording the referer header of the main page of your
web application. You use a tag file to store the value of referer as a session
attribute. Later, when the user purchases a product, you can obtain the session attribute
value and insert it into a database table.

The example consists of one HTML file (searchEngine.html), two JSP pages (main.jsp
and viewReferer.jsp), and one tag file (doBodyDemo.tag). The main.jsp page is the main page of the web site. It uses the doBodyDemo custom tag to store the referer header. To view the referer header value, you use the viewReferer.jsp page. If you invoke the main.jsp page by directly typing in its URL, the referer header will be null. Therefore, you use the searchEngine.html file to go to the main.jsp page.

The doBodyDemo.tag file is as follows:

<jsp:doBody var="referer" 
scope="session"/>

That's right. The doBodyDemo.tag file only consists of one line: a jsp:doBody standard
action. What it does is invoke the body of the tag and store the output in a session
attribute called referer.

The main.jsp page is given below:

<%@ taglib prefix="tags" 
tagdir="/WEB-INF/tags" %>
Your referer header: ${header.referer}
<br/>
<tags:doBodyDemo>
  ${header.referer}
</tags:doBodyDemo>
<a href="viewReferer.jsp">View</a>
the referer as a Session attribute.

The main.jsp page prints the value of the referer header, using text and an EL
expression:

Your referer header: ${header.referer}
<br/>

It then uses the doBodyDemo tag, passing the referer header as the body.

<tags:doBodyDemo>
  ${header.referer}
</tags:doBodyDemo>

It then prints a link to the viewReferer.jsp page for your convenience:

<a href="viewReferer.jsp">View</a>
the referer as a Session attribute.

The viewReferer.jsp page is shown below.

The Referer header of the previous page 
is ${sessionScope.referer}

The viewReferer.jsp page prints the value of a session attribute called referer, using an
EL expression.

Last, the searchEngine.html looks like this:

Please click <a 
href="main.jsp">here</a>

To test the example, first invoke the searchEngine.html file using the following URL:

http://localhost:8080/tagfile/searchEngine.html

You'll see the page in Figure 2.

The searchEngine.html page
Figure 2. The searchEngine.html page

Now, click the link to go to the main.jsp page. The referer header of the main.jsp page will be the URL of the searchEngine.html. Figure 3 shows the main.jsp page.

The main.jsp page
Figure 3. The main.jsp page

The main.jsp page invokes the doBodyDemo custom action that stores the referer session attribute. Now, click the view link in the main.jsp page to see the session
attribute value. You'll see something similar to Figure 4.

The viewReferer.jsp page
Figure 4. The viewReferer.jsp page

jsp:invoke

The jsp:invoke standard action is similar to jsp:doBody, and, like jsp:doBody, you can use jsp:invoke only in a tag file. You use the jsp:invoke standard action to invoke a fragment attribute. Recall that an attribute can have the fragment attribute whose value is either true or false. When the fragment attribute value is true, the attribute is a fragment attribute, which you can invoke as many times as you want from a tag file. The
jsp:invoke can have attributes too. The list of attributes is presented in Table 3. Note that
only the fragment attribute is required.

Table 3. The attributes of jsp:invoke

Attribute Description
fragment The name used to identify this fragment during this tag invocation.
var The name of a scoped attribute to store the output of the tag body
invocation. The value is stored as a java.lang.String object. Only one of the
var or varReader attribute can be present.
varReader The name of a scoped attribute to store the output of the tag body
invocation. The value is stored as a java.io.Reader object. Only one of the
var or varReader attribute can be present.
scope The scope for the resulting variable.

As an example, consider the invokeDemo.tag file:

<%@ attribute name="productDetails" 
fragment="true" %>
<%@ variable name-given="productName" %>
<%@ variable name-given="description" %>
<%@ variable name-given="price" %>
<%
  jspContext.setAttribute(
"productName", "Pelesonic DVD Player");
  jspContext.setAttribute(
"description",
    "Dolby Digital output through
coaxial digital-audio jack," +
    " 500 lines horizontal
resolution-image digest viewing");
  jspContext.setAttribute("price", "65");
%>
<jsp:invoke fragment="productDetails"/>

The tag file defines the attribute productDetails whose fragment attribute is true. It also defines three variables and sets the value for those variables. The last line of the tag file
invokes the fragment productDetails. Because there is no var or varReader attribute in the jsp:invoke standard action, the result of tag invocation will be directed to the JspWriter object of the calling JSP page.

To test the tag file, use this invokeTest.jsp page:

<%@ taglib prefix="easy" 
tagdir="/WEB-INF/tags" %>
<html>
<head>
<title>Product Details</title>
</head>
<body>
<easy:invokeDemo>
  <jsp:attribute name="productDetails">
    <table width="220" border="1">
    <tr>
      <td><b>Product Name</b></td>
      <td>${productName}</td>
    </tr>
    <tr>
      <td><b>Description</b></td>
     <td>${description}</td>
    </tr>
    <tr>
      <td><b>Price</b></td>
      <td>${price}</td>
    </tr>
    </table>
  </jsp:attribute>
</easy:invokeDemo>
</body>
</html>

You can use the following URL to call the invokeTest.jsp page.

http://localhost:8080/tagfiles/invokeTest.jsp

The result is shown in Figure 5.

Using fragment attribute
Figure 5. Using fragment attribute

Packaging Tag Files

Just like the traditional custom actions, you can package tag-file-based custom actions
into a JAR file. To be accessible, tag files bundled in a JAR file need a tag library
descriptor. For each tag, you must use a tag-file element, as opposed to the tag element
for traditional tag handlers. Each tag-file element needs two sub-elements: name and
path. The name element defines the tag name and must be unique throughout all tag
names in the application. The path element specifies the full path of the tag file and must
begin with /META-INF/tags.

As an example, here is how you bundle a tag handler based on the encode.tag file. You
need the following tag library descriptor, which for this example, I call myTLD2.tld:

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

<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 web-jsptaglibrary_2_0.xsd"
  version="2.0">
 
  <description>JSP 2.0 tag files</description>
  <tlib-version>1.0</tlib-version>
  <short-name>My Tag Files</short-name>
  <uri>http://www.brainysoftware.com/tagfiles</uri>
  <tag-file>
    <name>encode</name>
    <path>/META-INF/tags/encode.tag</path>
  </tag-file>
</taglib>

Note that there is one tag-file element with a name and a path subelements. There is
also the uri element that serves as the unique identifier for this tag library descriptor. The
value of the uri element can be anything. In the example, I use my domain name to
guarantee uniqueness. However, if you use a domain name, the domain name does not
have to exist.

Then you have to create a working directory, which in this example I call deploy. Under
deploy, you'll have a META-INF directory with a tags subdirectory. You copy your tag
file(s) to deploy under META-INF/tags and the tag library descriptor under META-INF.
Figure 6 show the directory structure for this example.

The directory structure of the tag files to be packaged
Figure 6. The directory structure of the tag files to be packaged

Now, change directories to the deploy directory and type in the following:

jar cvf mytagfiles.jar *

where mytagfiles.jar is the name of the resulting JAR.

To use the custom actions in the JAR file, just copy the JAR file to the WEB-INF/lib
directory of your application and start/restart your Web container. You can then use the
tag(s) from a JSP page. The following is a JSP page that shows how you can use the
encode tag from your application.

<%@ taglib prefix="easy" 
uri="http://www.brainysoftware.com/tagfiles" %>
<easy:encode input="<br/> means changing line"/>

Note that in the JSP page you use the uri attribute in your taglib directive, instead of the
tagdir attribute.

Note also that you can package customs tags based on tag handlers into the same JAR
as the custom tags that are based on tag files.

Be warned that the tag files in your JAR file remain as tag files; that is, anyone with a
text editor can see the contents of your code.

Summary

In this article you have learned the new feature in JSP 2.0 that makes the process of
writing tag extensions much simpler: the tag file. With tag files, you don't need a tag
library descriptor. You don't even need to compile the tag handler. You have also seen
how to use the jsp:invoke and jsp:doBody standard actions and package tag handlers
based on tag files.

Budi Kurniawan is a senior J2EE architect.
Related Topics >> JSP   |