3.7. Defining Custom JSP Tag Libraries

JSP 1.1 introduced an extremely valuable new capability: the ability to create your own JSP tags. You define how a tag, its attributes, and its body are interpreted, then group your tags into collections called tag libraries that can be used in any number of JSP files. The ability to define tag libraries in this way permits Java developers to boil down complex server-side behaviors into simple and easy-to-use elements that content developers can easily incorporate into their JSP pages. This section introduces the basic capabilities of custom tags. New features introduced in JSP 1.2 are covered in Chapter 11 (New Tag Library Features in JSP 1.2).

Custom tags accomplish some of the same goals as beans that are accessed with jsp:useBean (see Figure 3-18)—encapsulating complex behaviors into simple and accessible forms. There are several differences, however:

Figure 3-18. Strategies for invoking dynamic code from JSP.


  1. Custom tags can manipulate JSP content; beans cannot.

  2. Complex operations can be reduced to a significantly simpler form with custom tags than with beans.

  3. Custom tags require quite a bit more work to set up than do beans.

  4. Custom tags usually define relatively self-contained behavior, whereas beans are often defined in one servlet and then used in a different servlet or JSP page (see the following section on integrating servlets and JSP).

  5. Custom tags are available only in JSP 1.1 and later, but beans can be used in all JSP 1.x versions.

The Components That Make Up a Tag Library

To use custom JSP tags, you need to define three separate components: the tag handler class that defines the tag’s behavior, the tag library descriptor file that maps the XML element names to the tag implementations, and the JSP file that uses the tag library. The rest of this subsection gives an overview of each of these components, and the following subsections give details on how to build these components for various styles of tags. Most people find that the first tag they write is the hardest—the difficulty being in knowing where each component should go, not in writing the components. So, I suggest that you start by just downloading the examples of this subsection and getting the example tag working. After that, you can move on to the following subsections and try some of your own tags.

The Tag Handler Class

When defining a new tag, your first task is to define a Java class that tells the system what to do when it sees the tag. This class must implement the javax.servlet.jsp.tagext.Tag interface. You usually accomplish this by extending the TagSupport or BodyTagSupport class.

Listing 3.29 is an example of a simple tag that just inserts “ Custom tag example (msajsp.tags.ExampleTag) ” into the JSP page wherever the corresponding tag is used. Don’t worry about understanding the exact behavior of this class; that will be made clear in the next subsection. For now, just note that the class is in the moreservlets.tags package and is called ExampleTag. Consequently, the class file needs to be placed in tags subdirectory of the moreservlets subdirectory of whatever directory the current Web application is using for Java class files (i.e.,.../WEB-INF/classes—see Sections 1.7 and 1.9). With Tomcat, for example, the class file would be in install_dir/webapps/ROOT/WEB-INF/classes/moreservlets/tags/ ExampleTag.class.

Listing 3.29. ExampleTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 

/** Very simple JSP tag that just inserts a string 
 *  ("Custom tag example...") into the output. 
 *  The actual name of the tag is not defined here; 
 *  that is given by the Tag Library Descriptor (TLD) 
 *  file that is referenced by the taglib directive 
 *  in the JSP file. 
 */ 

public class ExampleTag extends TagSupport {
  public int doStartTag() {
    try {
      JspWriter out = pageContext.getOut(); 
      out.print("Custom tag example " + 
                "(moreservlets.tags.ExampleTag)"); 
    } catch(IOException ioe) {
      System.out.println("Error in ExampleTag: " + ioe); 
    } 
    return(SKIP_BODY); 
  } 
} 

The Tag Library Descriptor File

Once you have defined a tag handler, your next task is to identify the class to the server and to associate it with a particular XML tag name. This task is accomplished by means of a tag library descriptor file (in XML format) like the one shown in Listing 3.30. This file contains some fixed information, an arbitrary short name for your library, a short description, and a series of tag descriptions. The nonbold part of the listing is the same in virtually all tag library descriptors and can be copied verbatim from the source code archive at http://www.moreservlets.com.

The format of tag descriptions is described in later sections. For now, just note that the tag element defines the main name of the tag (really tag suffix, as will be seen shortly) and identifies the class that handles the tag. Since the tag handler class is in the moreservlets.tags package, the fully qualified class name of moreservlets.tags.ExampleTag is used. Note that this is a class name, not a URL or relative path name. The class can be installed anywhere on the server that beans or other supporting classes can be put. With Tomcat, the base location for classes in the default Web application is install_dir/webapps/ROOT/WEB-INF/classes, so ExampleTag.class would be in install_dir/webapps/ROOT/WEB-INF/classes/moreservlets/tags. Although it is always a good idea to put your servlet classes in packages, a surprising feature of Tomcat is that tag handlers are required to be in packages.

Listing 3.30. msajsp-taglib.tld
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE taglib 
 PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" 
 "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"> 

<taglib> 
  <tlibversion>1.0</tlibversion> 
  <jspversion>1.1</jspversion> 
  <shortname>msajsp-tags</shortname> 
  <info> 
    A tag library from More Servlets and JavaServer Pages, 
    http://www.moreservlets.com/. 
  </info> 
  <tag>
								<name>example</name>
								<tagclass>moreservlets.tags.ExampleTag</tagclass>
								<bodycontent>empty</bodycontent>
								<info>Simplest example: inserts one line of output</info>
								</tag>
								... 
</taglib> 

The JSP File

Once you have a tag handler implementation and a tag library description, you are ready to write a JSP file that makes use of the tag. Listing 3.31 gives an example. Somewhere before the first use of your tag, you need to use the taglib directive. This directive has the following form:

<%@ taglib uri="..." prefix="..." %> 

The required uri attribute can be either an absolute or relative URL referring to a tag library descriptor file like the one shown in Listing 3.30. For now, we will use a simple relative URL corresponding to a TLD file that is in the same directory as the JSP page that uses it. When we get to Web applications, however (see Chapter 4), we will see that it makes more sense for larger applications to put the TLD files in a subdirectory inside the WEB-INF directory. This configuration makes it easier to reuse the same TLD file from JSP pages in multiple directories, and it prevents end users from retrieving the TLD file. Furthermore, as we will see in Section 5.13 (Locating Tag Library Descriptors), you can use the Web application deployment descriptor (i.e., web.xml) to change the meaning of strings supplied to the uri attribute of the taglib directive. When starting out, however, you will probably find it easiest to put the TLD file in the same directory as the JSP page that uses it and then use a simple filename as the value of the uri attribute.

The prefix attribute, also required, specifies a prefix that will be used in front of whatever tag name the tag library descriptor defined. For example, if the TLD file defines a tag named tag1 and the prefix attribute has a value of test, the actual tag name would be test:tag1. This tag could be used in either of the following two ways, depending on whether it is defined to be a container that makes use of the tag body:

<test:tag1>Arbitrary JSP</test:tag1> 

or just

<test:tag1 /> 

To illustrate, the descriptor file of Listing 3.30 is called msajsp-taglib.tld and resides in the same directory as the JSP file shown in Listing 3.31 (i.e., any of the standard locations for JSP files described in Section 3.3, not the directory where Java class files are placed). Thus, the taglib directive in the JSP file uses a simple relative URL giving just the filename, as shown below.

<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 

Furthermore, since the prefix attribute is msajsp (for More Servlets and JavaServer Pages), the rest of the JSP page uses msajsp:example to refer to the example tag defined in the descriptor file. Figure 3-19 shows the result.

Figure 3-19. Result of SimpleExample.jsp.


Listing 3.31. SimpleExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 
<TITLE><msajsp:example /></TITLE> 
<LINK REL=STYLESHEET 
      HREF="JSP-Styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<H1><msajsp:example /></H1> 
<msajsp:example /> 
</BODY> 
</HTML> 

Defining a Basic Tag

This subsection gives details on defining simple tags without attributes or tag bodies; the tags are thus of the form <prefix:tagname />.

A Basic Tag: Tag Handler Class

Tags that either have no body or that merely include the body verbatim should extend the TagSupport class. This is a built-in class in the javax.servlet.jsp.tagext package that implements the Tag interface and contains much of the standard functionality basic tags need. Because of other classes you will use, your tag should normally import classes in the javax.servlet.jsp and java.io packages as well. So, most tag implementations contain the following import statements after the package declaration:

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 

I recommend that you grab an example from http://www.moreservlets.com and use it as the starting point for your own implementations.

For a tag without attributes or body, all you need to do is override the doStartTag method, which defines code that gets called at request time at the place where the element’s start tag is found. To generate output, the method should obtain the JspWriter (the specialized Writer available in JSP pages through use of the predefined out variable) from the automatically defined pageContext field by means of getOut. In addition to the getOut method, the pageContext field (of type PageContext) has methods for obtaining other data structures associated with the request. The most important ones are getRequest, getResponse, getServletContext, and getSession.

Since the print method of JspWriter throws IOException, the print statements should be inside a try/catch block. To report other types of errors to the client, you can declare that your doStartTag method throws a JspException and then throw one when the error occurs.

If your tag does not have a body, your doStartTag should return the SKIP_BODY constant. This instructs the system to ignore any content between the tag’s start and end tags. As we will see shortly, SKIP_BODY is sometimes useful even when there is a tag body (e.g., if you sometimes include it and other times omit it), but the simple tag we’re developing here will be used as a stand-alone tag (<prefix:tagname />) and thus does not have body content.

Listing 3.32 shows a tag implementation that uses this approach to generate a random 50-digit prime number through use of the Primes class (Listing 3.33), which is adapted from Section 7.3 (Persistent Servlet State and Auto-Reloading Pages) of Core Servlets and JavaServer Pages. Remember that the full text of Core Servlets and JavaServer Pages is available in PDF at http://www.moreservlets.com.

Listing 3.32. SimplePrimeTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import java.math.*; 
import moreservlets.*; 

/** Generates a prime of approximately 50 digits. 
 *  (50 is actually the length of the random number 
 *  generated -- the first prime above that number will 
 *  be returned.) 
 */ 

public class SimplePrimeTag extends TagSupport {
  protected int len = 50; 

  public int doStartTag() {
    try {
      JspWriter out = pageContext.getOut(); 
      BigInteger prime = Primes.nextPrime(Primes.random(len)); 
      out.print(prime); 
    } catch(IOException ioe) {
      System.out.println("Error generating prime: " + ioe); 
    } 
    return(SKIP_BODY); 
  } 
} 

Listing 3.33. Primes.java
package moreservlets; 

import java.math.BigInteger; 

/** A few utilities to generate a large random BigInteger, 
 *  and find the next prime number above a given BigInteger. 
 */ 
public class Primes {
  // Note that BigInteger.ZERO and BigInteger.ONE are 
  // unavailable in JDK 1.1. 
  private static final BigInteger ZERO = BigInteger.ZERO; 
  private static final BigInteger ONE = BigInteger.ONE; 
  private static final BigInteger TWO = new BigInteger("2"); 
  // Likelihood of false prime is less than 1/2^ERR_VAL 
  // Assumedly BigInteger uses the Miller-Rabin test or 
  // equivalent, and thus is NOT fooled by Carmichael numbers. 
  // See section 33.8 of Cormen et al.'s Introduction to 
  // Algorithms for details. 
  private static final int ERR_VAL = 100; 

  public static BigInteger nextPrime(BigInteger start) {
    if (isEven(start)) 
      start = start.add(ONE); 
    else 
      start = start.add(TWO); 
    if (start.isProbablePrime(ERR_VAL)) 
      return(start); 
    else 
      return(nextPrime(start)); 
  } 

  private static boolean isEven(BigInteger n) {
    return(n.mod(TWO).equals(ZERO)); 
  } 

  private static StringBuffer[] digits = 
    { new StringBuffer("0"), new StringBuffer("1"), 
      new StringBuffer("2"), new StringBuffer("3"), 
      new StringBuffer("4"), new StringBuffer("5"), 
      new StringBuffer("6"), new StringBuffer("7"), 
      new StringBuffer("8"), new StringBuffer("9") }; 

  private static StringBuffer randomDigit(boolean isZeroOK) {
    int index; 
    if (isZeroOK) {
      index = (int)Math.floor(Math.random() * 10); 
    } else {
      index = 1 + (int)Math.floor(Math.random() * 9); 
    } 
    return(digits[index]); 
  } 
  /** Create a random big integer where every digit is 
   *  selected randomly (except that the first digit 
   *  cannot be a zero). 
   */ 

  public static BigInteger random(int numDigits) {
    StringBuffer s = new StringBuffer(""); 
    for(int i=0; i<numDigits; i++) {
      if (i == 0) {
        // First digit must be non-zero. 
        s.append(randomDigit(false)); 
      } else {
        s.append(randomDigit(true)); 
      } 
    } 
    return(new BigInteger(s.toString())); 
  } 

  /** Simple command-line program to test. Enter number 
   *  of digits, and it picks a random number of that 
   *  length and then prints the first 50 prime numbers 
   *  above that. 
   */ 

  public static void main(String[] args) {
    int numDigits; 
    try {
      numDigits = Integer.parseInt(args[0]); 
    } catch (Exception e) { // No args or illegal arg. 
      numDigits = 150; 
    } 
    BigInteger start = random(numDigits); 
    for(int i=0; i<50; i++) {
      start = nextPrime(start); 
      System.out.println("Prime " + i + " = " + start); 
    } 
  } 
} 

A Basic Tag: Tag Library Descriptor File

The general format of a descriptor file is almost always the same: it should contain an XML version identifier followed by a DOCTYPE declaration followed by a taglib container element, as shown earlier in Listing 3.10. To get started, just download a sample from the source code archive at http://www.moreservlets.com. The important part to understand is what goes in the taglib element: the tag element. For tags without attributes, the tag element should contain four elements between <tag> and </tag>:

  1. name, whose body defines the base tag name to which the prefix of the taglib directive will be attached. In this case, I use

    <name>simplePrime</name> 

    to assign a base tag name of simplePrime.

  2. tagclass, which gives the fully qualified class name of the tag handler. In this case, I use

    <tagclass>moreservlets.tags.SimplePrimeTag</tagclass> 

    Note that tagclass was renamed tag-class in JSP 1.2. So, if you use features specific to JSP 1.2 and use the JSP 1.2 DOCTYPE, you should use tag-class, not tagclass.

  3. bodycontent, which can be omitted, but if present should have the value empty for tags without bodies. Tags with normal bodies that might be interpreted as normal JSP use a value of JSP (the default value), and the rare tags whose handlers completely process the body themselves use a value of tagdependent. For the SimplePrimeTag discussed here, I use empty as below:

    <bodycontent>empty</bodycontent> 

    Note that bodycontent was renamed body-content in JSP 1.2. However, as with the other new element names, you are only required to make the change if you use the JSP 1.2 DOCTYPE.

  4. info, which gives a short description. Here, I use

    <info>Outputs a random 50-digit prime.</info> 

    Note that info was renamed description in JSP 1.2.

Core Note

In JSP 1.2, tagclass was renamed tag-class, bodycontent was renamed body-content, and info was renamed description. However, the old element names still work in JSP 1.2 servers as long as the TLD file uses the JSP 1.1 DOCTYPE.


Listing 3.34 shows the relevant part of the TLD file.

Listing 3.34. msajsp-taglib.tld (Excerpt 1)
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE ...> 
<taglib> 
  ... 
  <tag>
								<name>simplePrime</name>
								<tagclass>moreservlets.tags.SimplePrimeTag</tagclass>
								<bodycontent>empty</bodycontent>
								<info>Outputs a random 50-digit prime.</info>
								</tag> 
  ... 
</taglib> 

A Basic Tag: JSP File

JSP documents that make use of custom tags need to use the taglib directive, supplying a uri attribute that gives the location of the tag library descriptor file and a prefix attribute that specifies a short string that will be attached (along with a colon) to the main tag name. Remember that the uri attribute can be an absolute or relative URL. When first learning, it is easiest to use a simple relative URL corresponding to a TLD file that is in the same directory as the JSP page that uses it. When we get to Web applications, however (see Chapter 4), we will see that it makes more sense for larger applications to put the TLD files in a subdirectory inside the WEB-INF directory. This configuration makes it easier to reuse the same TLD file from JSP pages in multiple directories, and it prevents end users from retrieving the TLD file.

Furthermore, as we will see in Section 5.13 (Locating Tag Library Descriptors), you can use the Web application deployment descriptor (i.e., web.xml) to change the meaning of strings supplied to the uri attribute of the taglib directive. For now, however, you will probably find it easiest to put the TLD file in the same directory as the JSP page that uses it and then use a simple filename as the value of the uri attribute.

Listing 3.35 shows a JSP document that uses

<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 

to use the TLD file just shown in Listing 3.34 with a prefix of msajsp. Since the base tag name is simplePrime, the full tag used is

<msajsp:simplePrime /> 

Figure 3-20 shows the result.

Figure 3-20. Result of SimplePrimeExample.jsp.


Listing 3.35. SimplePrimeExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Some 50-Digit Primes</TITLE> 
<LINK REL=STYLESHEET 
      HREF="JSP-Styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<H1>Some 50-Digit Primes</H1> 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 
<UL> 
  <LI><msajsp:simplePrime /> 
  <LI><msajsp:simplePrime /> 
  <LI><msajsp:simplePrime /> 
  <LI><msajsp:simplePrime /> 
</UL> 
</BODY> 
</HTML> 

Assigning Attributes to Tags

Allowing tags like

<prefix:name attribute1="value1" attribute2="value2"... /> 

adds significant flexibility to your tag library. This subsection explains how to add attribute support to your tags.

Tag Attributes: Tag Handler Class

Providing support for attributes is straightforward. Use of an attribute called attribute1 simply results in a call to a method called setAttribute1 in your class that extends TagSupport (or that otherwise implements the Tag interface). Consequently, adding support for an attribute named attribute1 is merely a matter of implementing the following method:

public void setAttribute1(String value1) {
  doSomethingWith(value1); 
} 

Note that an attribute of attributeName (lowercase a) corresponds to a method called setAttributeName (uppercase A).

Static values (i.e., those determined at page translation time) are always supplied to the method as type String. However, you can use rtexprvalue and type elements in the TLD file to permit attributes of other types to be dynamically calculated. See the following subsection for details.

One of the most common things to do in the attribute handler is to simply store the attribute in a field that will later be used by doStartTag or a similar method. For example, the following is a section of a tag implementation that adds support for the message attribute.

private String message = "Default Message"; 

public void setMessage(String message) {
  this.message = message; 
} 

If the tag handler will be accessed from other classes, it is a good idea to provide a getAttributeName method in addition to the setAttributeName method. Only setAttributeName is required, however.

Listing 3.36 shows a subclass of SimplePrimeTag that adds support for the length attribute. When such an attribute is supplied, it results in a call to setLength, which converts the input String to an int and stores it in the len field already used by the doStartTag method in the parent class.

Listing 3.36. PrimeTag.java
package moreservlets.tags; 

/** Generates an N-digit random prime (default N = 50). 
 *  Extends SimplePrimeTag, adding a length attribute 
 *  to set the size of the prime. The doStartTag 
 *  method of the parent class uses the len field 
 *  to determine the length of the prime. 
 */ 

public class PrimeTag extends SimplePrimeTag {
  public void setLength(String length) {
    try {
      len = Integer.parseInt(length); 
    } catch(NumberFormatException nfe) {
      len = 50; 
    } 
  } 
} 

Tag Attributes: Tag Library Descriptor File

Tag attributes must be declared inside the tag element by means of an attribute element. The attribute element has five nested elements that can appear between <attribute> and </attribute>.

  1. name, a required element that defines the case-sensitive attribute name. In this case, I use

    <name>length</name> 
  2. required, a required element that stipulates whether the attribute must always be supplied (true) or is optional (false) . In this case, to indicate that length is optional, I use

    <required>false</required> 

    If required is false and the JSP page omits the attribute, no call is made to the setAttributeName method. So, be sure to give default values to the fields that the method sets. Omitting a required attribute results in an error at page translation time.

  3. rtexprvalue, an optional element that indicates whether the attribute value can be a JSP expression like <%= expression %> (true) or whether it must be a fixed string (false). The default value is false, so this element is usually omitted except when you want to allow attributes to have values determined at request time.

  4. type, an optional element that designates the class to which the value should be typecast. Designating a type is only legal when rtexprvalue is true.

  5. example, an optional element that gives an example of how to use the tag. This element is intended for development environments and has no effect on execution; it is available only in JSP 1.2.

Listing 3.37 shows the relevant tag element within the tag library descriptor file. In addition to supplying an attribute element to describe the length attribute, the tag element also contains the standard name (prime), tagclass (moreservlets.tags.PrimeTag), bodycontent (empty), and info (short description) elements. Note that if you use features specific to JSP 1.2 and the JSP 1.2 DOCTYPE (see Chapter 11, “ New Tag Library Features in JSP 1.2 ”), you should change tagclass, bodycontent, and info to tag-class, body-content, and description, respectively.

Listing 3.37. msajsp-taglib.tld (Excerpt 2)
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE ...> 
<taglib> 
  ... 
  <tag>
								<name>prime</name>
								<tagclass>moreservlets.tags.PrimeTag</tagclass>
								<bodycontent>empty</bodycontent>
								<info>Outputs a random N-digit prime.</info>
								<attribute>
								<name>length</name>
								<required>false</required>
								</attribute>
								</tag> 
  ... 
</taglib> 

Tag Attributes: JSP File

Listing 3.38 shows a JSP document that uses the taglib directive to load the tag library descriptor file and to specify a prefix of msajsp. Since the prime tag is defined to permit a length attribute, Listing 3.38 uses

<msajsp:prime length="xxx" /> 

Remember that custom tags follow XML syntax, which requires attribute values to be enclosed in either single or double quotes. Also, since the length attribute is not required, it is permissible to just use

<msajsp:prime /> 

The tag handler is responsible for using a reasonable default value in such a case. Figure 3-21 shows the result of Listing 3.38.

Figure 3-21. Result of PrimeExample.jsp.


Listing 3.38. PrimeExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Some N-Digit Primes</TITLE> 
<LINK REL=STYLESHEET 
      HREF="JSP-Styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<H1>Some N-Digit Primes</H1> 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 
<UL> 
  <LI>20-digit: <msajsp:prime length="20" /> 
  <LI>40-digit: <msajsp:prime length="40" /> 
  <LI>80-digit: <msajsp:prime length="80" /> 
  <LI>Default (50-digit): <msajsp:prime /> 
</UL> 
</BODY> 
</HTML> 

Including the Tag Body

Up to this point, all of the custom tags you have seen ignore the tag body and thus are used as stand-alone tags of the form

<prefix:tagname /> 

In this section, we see how to define tags that use their body content and are thus written in the following manner:

<prefix:tagname>body</prefix:tagname> 

Tag Bodies: Tag Handler Class

In the previous examples, the tag handlers defined a doStartTag method that returned SKIP_BODY. To instruct the system to make use of the body that occurs between the new element’s start and end tags, your doStartTag method should return EVAL_BODY_INCLUDE instead. The body content can contain JSP scripting elements, directives, and actions, just like the rest of the page. The JSP constructs are translated into servlet code at page translation time, and that code is invoked at request time.

If you make use of a tag body, then you might want to take some action after the body as well as before it. Use the doEndTag method to specify this action. In almost all cases, you want to continue with the rest of the page after finishing with your tag, so the doEndTag method should return EVAL_PAGE. If you want to abort the processing of the rest of the page, you can return SKIP_PAGE instead.

Listing 3.39 defines a tag for a heading element that is more flexible than the standard HTML H1 through H6 elements. This new element allows a precise font size, a list of preferred font names (the first entry that is available on the client system will be used), a foreground color, a background color, a border, and an alignment (LEFT, CENTER, RIGHT). Only the alignment capability is available with the H1 through H6 elements. The heading is implemented through use of a one-cell table enclosing a SPAN element that has embedded style sheet attributes. The doStartTag method generates the TABLE and SPAN start tags, then returns EVAL_BODY_INCLUDE to instruct the system to include the tag body. The doEndTag method generates the </SPAN> and </TABLE> tags, then returns EVAL_PAGE to continue with normal page processing. Various setAttributeName methods are used to handle the attributes like bgColor and fontSize.

Listing 3.39. HeadingTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 

/** Generates an HTML heading with the specified background 
 *  color, foreground color, alignment, font, and font size. 
 *  You can also turn on a border around it, which normally 
 *  just barely encloses the heading, but which can also 
 *  stretch wider. All attributes except the background 
 *  color are optional. 
 */ 

public class HeadingTag extends TagSupport {
  private String bgColor; // The one required attribute 
  private String color = null; 
  private String align="CENTER"; 
  private String fontSize="36"; 
  private String fontList="Arial, Helvetica, sans-serif"; 
  private String border="0"; 
  private String width=null; 

  public void setBgColor(String bgColor) {
    this.bgColor = bgColor; 
  } 

  public void setColor(String color) {
    this.color = color; 
  } 

  public void setAlign(String align) {
    this.align = align; 
  } 

  public void setFontSize(String fontSize) {
    this.fontSize = fontSize; 
  } 

  public void setFontList(String fontList) {
    this.fontList = fontList; 
  } 
  public void setBorder(String border) {
    this.border = border; 
  } 

  public void setWidth(String width) {
    this.width = width; 
  } 

  public int doStartTag() {
    try {
      JspWriter out = pageContext.getOut(); 
      out.print("<TABLE BORDER=" + border + 
                " BGCOLOR="" + bgColor + """ + 
                " ALIGN="" + align + """); 
      if (width != null) {
        out.print(" WIDTH="" + width + """); 
      } 
      out.print("><TR><TH>"); 
      out.print("<SPAN STYLE="" + 
                "font-size: " + fontSize + "px; " + 
                "font-family: " + fontList + "; "); 
      if (color != null) {
        out.println("color: " + color + ";"); 
      } 
      out.print(""> "); // End of <SPAN ...> 
    } catch(IOException ioe) {
      System.out.println("Error in HeadingTag: " + ioe); 
    } 
    return(EVAL_BODY_INCLUDE); // Include tag body 
  } 

  public int doEndTag() {
								try {
								JspWriter out = pageContext.getOut();
								out.print("</SPAN></TABLE>");
								} catch(IOException ioe) {
								System.out.println("Error in HeadingTag: " + ioe);
								}
								return(EVAL_PAGE); // Continue with rest of JSP page
								} 
} 

Tag Bodies: Tag Library Descriptor File

There is only one new feature in the use of the tag element for tags that use body content: the bodycontent element should contain the value JSP as below.

<bodycontent>JSP</bodycontent> 

Remember, however, that bodycontent is optional (JSP is the default value) and is mainly intended for IDEs. The name, tagclass, info, and attribute elements are used in the same manner as described previously. Listing 3.40 gives the relevant part of the code.

Listing 3.40. msajsp-taglib.tld (Excerpt 3)
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE ...> 
<taglib> 
  ... 
  <tag>
								<name>heading</name>
								<tagclass>moreservlets.tags.HeadingTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>Outputs a 1-cell table used as a heading.</info>
								<attribute>
								<name>bgColor</name>
								<required>true</required>  <!-- bgColor is required -->
								</attribute>
								<attribute>
								<name>color</name>
								<required>false</required>
								</attribute>
								<attribute>
								<name>align</name>
								<required>false</required>
								</attribute>
								<attribute>
								<name>fontSize</name>
								<required>false</required>
								</attribute>
								<attribute>
								<name>fontList</name>
								<required>false</required>
								</attribute>
								<attribute>
								<name>border</name>
								<required>false</required>
								</attribute>
								<attribute>
								<name>width</name>
								<required>false</required>
								</attribute>
								</tag>
								... 
</taglib> 

Tag Bodies: JSP File

Listing 3.41 shows a document that uses the heading tag just defined. Since the bgColor attribute was defined to be required, all uses of the tag include it. Figure 3-22 shows the result.

Figure 3-22. The custom msajsp:heading element gives you much more succinct control over heading format than do the standard H1 through H6 elements in HTML.


Listing 3.41. HeadingExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Some Tag-Generated Headings</TITLE> 
</HEAD> 
<BODY> 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %>
								<msajsp:heading bgColor="#C0C0C0"> 
Default Heading 
</msajsp:heading> 
<P> 
<msajsp:heading bgColor="BLACK" color="WHITE"> 
White on Black Heading 
</msajsp:heading> 
<P> 
<msajsp:heading bgColor="#EF8429" fontSize="60" border="5"> 
Large Bordered Heading 
</msajsp:heading> 
<P> 
<msajsp:heading bgColor="CYAN" width="100%"> 
Heading with Full-Width Background 
</msajsp:heading> 
<P> 
<msajsp:heading bgColor="CYAN" fontSize="60"
								fontList="Brush Script MT, Times, serif"> 
Heading with Non-Standard Font 
</msajsp:heading> 
</BODY> 
</HTML> 

Optionally Including the Tag Body

Most tags either never make use of body content or always do so. In either case, you decide in advance whether the body content is used. However, you are also permitted to make this decision at request time. This subsection shows you how to use request time information to decide whether to include the tag body.

Optional Body Inclusion: Tag Handler Class

Optionally including the tag body is a trivial exercise: just return EVAL_BODY_INCLUDE or SKIP_BODY, depending on the value of some request time expression. The important thing to know is how to discover that request time information, since doStartTag does not have HttpServletRequest and HttpServletResponse arguments as do service, _jspService, doGet, and doPost. The solution to this dilemma is to use getRequest to obtain the HttpServletRequest from the automatically defined pageContext field of TagSupport. Strictly speaking, the return type of getRequest is ServletRequest, so you have to do a typecast to HttpServletRequest if you want to call a method that is not inherited from ServletRequest. However, in this case I just use getParameter, so no typecast is required.

Listing 3.42 defines a tag that ignores its body unless a request time debug parameter is supplied. Such a tag provides a useful capability whereby you embed debugging information directly in the JSP page during development but activate it only when a problem occurs.

Listing 3.42. DebugTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import javax.servlet.*; 

/** A tag that includes the body content only if 
 *  the "debug" request parameter is set. 
 */ 

public class DebugTag extends TagSupport {
  public int doStartTag() {
    ServletRequest request = pageContext.getRequest(); 
    String debugFlag = request.getParameter("debug"); 
    if ((debugFlag != null) && 
        (!debugFlag.equalsIgnoreCase("false"))) {
      return(EVAL_BODY_INCLUDE); 
    } else {
      return(SKIP_BODY); 
    } 
  } 
} 

Optional Body Inclusion: Tag Library Descriptor File

If your tag ever makes use of its body, you should provide the value JSP inside the bodycontent element (if you use bodycontent at all). Other than that, all the elements within tag are used in the same way as described previously. Listing 3.43 shows the entries needed for DebugTag.

Listing 3.43. msajsp-taglib.tld (Excerpt 4)
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE ...> 
<taglib> 
  ... 
  <tag>
								<name>debug</name>
								<tagclass>moreservlets.tags.DebugTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>Includes body only if debug param is set.</info>
								</tag> 
  ... 
</taglib> 

Optional Body Inclusion: JSP File

Suppose that you have an application where most of the problems that occur are due to requests occurring close together in time, the host making the request, or session tracking. In such a case, the time, requesting host, and session ID would be useful information to track. Listing 3.43 shows a page that encloses debugging information between <msajsp:debug> and </msajsp:debug>. Figures 3-23 and 3-24 show the normal result and the result when a request time debug parameter is supplied, respectively.

Figure 3-23. The body of the msajsp:debug element is normally ignored.


Figure 3-24. The body of the msajsp:debug element is included when a debug request parameter is supplied.


Listing 3.44. DebugExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Using the Debug Tag</TITLE> 
<LINK REL=STYLESHEET 
      HREF="JSP-Styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<H1>Using the Debug Tag</H1> 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 
Top of regular page. Blah, blah, blah. Yadda, yadda, yadda. 
<P> 
<msajsp:debug> 
<B>Debug:</B> 
<UL> 
  <LI>Current time: <%= new java.util.Date() %> 
  <LI>Requesting hostname: <%= request.getRemoteHost() %> 
  <LI>Session ID: <%= session.getId() %> 
</UL> 
</msajsp:debug> 
<P> 
Bottom of regular page. Blah, blah, blah. Yadda, yadda, yadda. 
</BODY> 
</HTML> 

Manipulating the Tag Body

The msajsp:prime element ignored any body content, the msajsp:heading element used body content, and the msajsp:debug element ignored or used it, depending on a request time parameter. The common thread among these elements is that the body content was never modified; it was either ignored or included verbatim (after JSP translation). This section shows you how to process the tag body.

Tag Body Processing: Tag Handler Class

Up to this point, all of the tag handlers have extended the TagSupport class. This is a good standard starting point, since it implements the required Tag interface and performs a number of useful setup operations like storing the PageContext reference in the pageContext field. However, TagSupport is not powerful enough for tag implementations that need to manipulate their body content, and BodyTagSupport should be used instead.

BodyTagSupport extends TagSupport, so the doStartTag and doEndTag methods are used in the same way as before. Two important new methods are defined by BodyTagSupport:

  1. doAfterBody, a method that you should override to handle the manipulation of the tag body. This method should normally return SKIP_BODY when it is done, indicating that no further body processing should be performed.

  2. getBodyContent, a method that returns an object of type BodyContent that encapsulates information about the tag body. In tag libraries that are intended only for JSP 1.2, you can use the bodyContent field of BodyTagSupport instead of calling getBodyContent. Most libraries, however, are intended to run in either JSP version.

The BodyContent class has three important methods:

  1. getEnclosingWriter, a method that returns the JspWriter being used by doStartTag and doEndTag.

  2. getReader, a method that returns a Reader that can read the tag’s body.

  3. getString, a method that returns a String containing the entire tag body.

The ServletUtilities class (see Listing 2.10) contains a static filter method that takes a string and replaces <, >, ", and & with <, >, &quot;, and &amp;, respectively. This method is useful when servlets output strings that might contain characters that would interfere with the HTML structure of the page in which the strings are embedded. Listing 3.45 shows a tag implementation that gives this filtering functionality to a custom JSP tag.

Listing 3.45. FilterTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import moreservlets.*; 

/** A tag that replaces <, >, ", and & with their HTML 
 *  character entities (<, >, &quot;, and &amp;). 
 *  After filtering, arbitrary strings can be placed 
 *  in either the page body or in HTML attributes. 
 */ 

public class FilterTag extends BodyTagSupport {
  public int doAfterBody() {
    BodyContent body = getBodyContent(); 
    String filteredBody = 
      ServletUtilities.filter(body.getString()); 
    try {
      JspWriter out = body.getEnclosingWriter(); 
      out.print(filteredBody); 
    } catch(IOException ioe) {
      System.out.println("Error in FilterTag: " + ioe); 
    } 
    // SKIP_BODY means we're done. If we wanted to evaluate 
    // and handle the body again, we'd return EVAL_BODY_TAG 
    // (JSP 1.1/1.2) or EVAL_BODY_AGAIN (JSP 1.2 only) 
    return(SKIP_BODY); 
  } 
} 

Tag Body Processing: Tag Library Descriptor File

Tags that manipulate their body content should use the bodycontent element the same way as tags that simply include it verbatim; they should supply a value of JSP. Other than that, nothing new is required in the descriptor file, as you can see by examining Listing 3.46, which shows the relevant portion of the TLD file.

Listing 3.46. msajsp-taglib.tld (Excerpt 5)
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE ...> 
<taglib> 
  ... 
  <tag>
								<name>filter</name>
								<tagclass>moreservlets.tags.FilterTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>Replaces HTML-specific characters in body.</info>
								</tag> 
  ... 
</taglib> 

Tag Body Processing: JSP File

Listing 3.47 shows a page that uses a table to show some sample HTML and its result. Creating this table would be tedious in regular HTML since the table cell that shows the original HTML would have to change all the < and > characters to < and >. This necessity is particularly onerous during development when the sample HTML is frequently changing. Use of the <msajsp:filter> tag greatly simplifies the process, as Listing 3.47 illustrates. Figure 3-25 shows the result.

Figure 3-25. The msajsp:filter element lets you insert text without worrying about it containing special HTML characters.


Listing 3.47. FilterExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>HTML Logical Character Styles</TITLE> 
<LINK REL=STYLESHEET 
      HREF="JSP-Styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<H1>HTML Logical Character Styles</H1> 
Physical character styles (B, I, etc.) are rendered consistently 
in different browsers. Logical character styles, however, 
may be rendered differently by different browsers. 
Here's how your browser 
(<%= request.getHeader("User-Agent") %>) 
renders the HTML 4.0 logical character styles: 
<P> 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 
<TABLE BORDER=1 ALIGN="CENTER"> 
<TR CLASS="COLORED"><TH>Example<TH>Result 
<TR> 
<TD><PRE><msajsp:filter> 
<EM>Some emphasized text.</EM><BR> 
<STRONG>Some strongly emphasized text.</STRONG><BR> 
<CODE>Some code.</CODE><BR> 
<SAMP>Some sample text.</SAMP><BR> 
<KBD>Some keyboard text.</KBD><BR> 
<DFN>A term being defined.</DFN><BR> 
<VAR>A variable.</VAR><BR> 
<CITE>A citation or reference.</CITE> 
</msajsp:filter></PRE> 
<TD> 
<EM>Some emphasized text.</EM><BR> 
<STRONG>Some strongly emphasized text.</STRONG><BR> 
<CODE>Some code.</CODE><BR> 
<SAMP>Some sample text.</SAMP><BR> 
<KBD>Some keyboard text.</KBD><BR> 
<DFN>A term being defined.</DFN><BR> 
<VAR>A variable.</VAR><BR> 
<CITE>A citation or reference.</CITE> 
</TABLE> 
</BODY> 
</HTML> 

Including or Manipulating the Tag Body Multiple Times

Rather than just including or processing the body of the tag a single time, you sometimes want to do so more than once. The ability to support multiple body inclusion lets you define a variety of iteration tags that repeat JSP fragments a variable number of times, repeat them until a certain condition occurs, and so forth. This subsection shows you how to build such tags.

Multiple Body Actions: the Tag Handler Class

Tags that process the body content multiple times should start by extending BodyTagSupport and implementing doStartTag, doEndTag, and, most importantly, doAfterBody as before. The difference lies in the return value of doAfterBody. If this method returns EVAL_BODY_TAG, then the tag body is evaluated again, resulting in a new call to doAfterBody. This process continues until doAfterBody returns SKIP_BODY. In JSP 1.2, the EVAL_BODY_TAG constant is deprecated and replaced with EVAL_BODY_AGAIN. The two constants have the same value, but EVAL_BODY_AGAIN is a clearer name. So, if your tag library is designed to be used only in JSP 1.2 containers (e.g., it uses some features specific to JSP 1.2 as described in Chapter 11), you should use EVAL_BODY_AGAIN. Most tag libraries, however, are designed to run in either JSP version and thus use EVAL_BODY_TAG.

Core Note

EVAL_BODY_TAG is renamed EVAL_BODY_AGAIN in JSP 1.2.


Listing 3.48 defines a tag that repeats the body content the number of times specified by the reps attribute. Since the body content can contain JSP (which is converted into servlet code at page translation time but is invoked at request time), each repetition does not necessarily result in the same output to the client.

Listing 3.48. RepeatTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 

/** A tag that repeats the body the specified 
 *  number of times. 
 */ 

public class RepeatTag extends BodyTagSupport {
  private int reps; 

  public void setReps(String repeats) {
    try {
      reps = Integer.parseInt(repeats); 
    } catch(NumberFormatException nfe) {
      reps = 1; 
    } 
  } 

  public int doAfterBody() {
    if (reps-- >= 1) {
      BodyContent body = getBodyContent(); 
      try {
        JspWriter out = body.getEnclosingWriter(); 
        out.println(body.getString()); 
        body.clearBody(); // Clear for next evaluation 
      } catch(IOException ioe) {
        System.out.println("Error in RepeatTag: " + ioe); 
      } 
      // Replace EVAL_BODY_TAG with EVAL_BODY_AGAIN in JSP 1.2. 
      return(EVAL_BODY_TAG); 
    } else {
      return(SKIP_BODY); 
    } 
  } 
} 

Multiple Body Actions: the Tag Library Descriptor File

Listing 3.49 shows the relevant section of the TLD file that gives the name msajsp:repeat to the tag just defined. To accommodate request time values in the reps attribute, the file uses an rtexprvalue element (enclosing a value of true) within the attribute element.

Listing 3.49. msajsp-taglib.tld (Excerpt 6)
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE ...> 
<taglib> 
  ... 
  <tag>
								<name>repeat</name>
								<tagclass>moreservlets.tags.RepeatTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>Repeats body the specified number of times.</info>
								<attribute>
								<name>reps</name>
								<required>true</required>
								<!-- rtexprvalue indicates whether attribute
								can be a JSP expression. -->
								<rtexprvalue>true</rtexprvalue>
								</attribute>
								</tag> 
  ... 
</taglib> 

Multiple Body Actions: the JSP File

Listing 3.50 shows a JSP document that creates a numbered list of prime numbers. The number of primes in the list is taken from the request time repeats parameter. Figure 3-26 shows one possible result.

Figure 3-26. Result of RepeatExample.jsp when accessed with a repeats parameter of 10.


Listing 3.50. RepeatExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Some 40-Digit Primes</TITLE> 
<LINK REL=STYLESHEET 
      HREF="JSP-Styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<H1>Some 40-Digit Primes</H1> 
Each entry in the following list is the first prime number 
higher than a randomly selected 40-digit number. 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %> 
<OL> 
<!-- Repeats N times. A null reps value means repeat once. --> 
<msajsp:repeat reps='<%= request.getParameter("repeats") %>'>
								<LI><msajsp:prime length="40" />
								</msajsp:repeat> 
</OL> 
</BODY> 
</HTML> 

Using Nested Tags

Although Listing 3.50 places the msajsp:prime element within the msajsp:repeat element, the two elements are independent of each other. The first generates a prime number regardless of where it is used, and the second repeats the enclosed content regardless of whether that content uses an msajsp:prime element.

Some tags, however, depend on a particular nesting. For example, in standard HTML, the TD and TH elements can only appear within TR, which in turn can only appear within TABLE. The color and alignment settings of TABLE are inherited by TR, and the values of TR affect how TD and TH behave. So, the nested elements can-not act in isolation even when nested properly. Similarly, the tag library descriptor file makes use of a number of elements like taglib, tag, attribute, and required where a strict nesting hierarchy is imposed.

This subsection shows you how to define tags that depend on a particular nesting order and where the behavior of certain tags depends on values supplied by earlier ones.

Nested Tags: the Tag Handler Classes

Class definitions for nested tags can extend either TagSupport or BodyTagSupport, depending on whether they need to manipulate their body content (these extend BodyTagSupport) or, more commonly, just ignore it or include it verbatim (these extend TagSupport).

Although nested tags use the standard tag handler classes, they use two new techniques within those classes. First, nested tags can use findAncestorWithClass to find the tag in which they are nested. This method takes a reference to the current class (e.g., this) and the Class object of the enclosing class (e.g., EnclosingTag.class) as arguments. If no enclosing class is found, the method in the nested class can throw a JspTagException that reports the problem. Second, if one tag wants to store data that a later tag will use, it can place that data in the instance of the enclosing tag. The definition of the enclosing tag should provide methods for storing and accessing this data.

Suppose that we want to define a set of tags that would be used like this:

<msajsp:if> 
  <msajsp:condition><%= someExpression %></msajsp:condition> 
  <msajsp:then>JSP to include if condition is true</msajsp:then> 
  <msajsp:else>JSP to include if condition is false</msajsp:else> 
</msajsp:if> 

To accomplish this task, the first step is to define an IfTag class to handle the msajsp:if tag. This handler should have methods to specify and check whether the condition is true or false (setCondition and getCondition). The handler should also have methods to designate and check whether the condition has ever been explicitly set (setHasCondition and getHasCondition), since we want to disallow msajsp:if tags that contain no msajsp:condition entry. Listing 3.51 shows the code for IfTag.

The second step is to define a tag handler for msajsp:condition. This class, called IfConditionTag, defines a doStartTag method that merely checks whether the tag appears within IfTag. It returns EVAL_BODY_TAG (EVAL_BODY_BUFFERED in tag libraries that are specific to JSP 1.2) if so and throws an exception if not. The handler’s doAfterBody method looks up the body content (getBodyContent), converts it to a String (getString), and compares that to "true". This approach means that an explicit value of true can be substituted for a JSP expression like <%= expression %> if, during initial page development, you want to temporarily designate that the then portion should always be used. Using a comparison to "true" also means that any other value will be considered false. Once this comparison is performed, the result is stored in the enclosing tag by means of the setCondition method of IfTag. The code for IfConditionTag is shown in Listing 3.52.

Listing 3.51. If Tag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import javax.servlet.*; 

/** A tag that acts like an if/then/else. */ 

public class IfTag extends TagSupport {
  private boolean condition; 
  private boolean hasCondition = false; 

  public void setCondition(boolean condition) {
    this.condition = condition; 
    hasCondition = true; 
  } 

  public boolean getCondition() {
    return(condition); 
  } 

  public void setHasCondition(boolean flag) {
    this.hasCondition = flag; 
  } 

  /** Has the condition field been explicitly set? */ 

  public boolean hasCondition() {
    return(hasCondition); 
  } 

  public int doStartTag() {
    return(EVAL_BODY_INCLUDE); 
  } 
} 

Listing 3.52. IfConditionTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import javax.servlet.*; 

/** The condition part of an if tag. */ 

public class IfConditionTag extends BodyTagSupport {
  public int doStartTag() throws JspTagException {
    IfTag parent =
								(IfTag)findAncestorWithClass(this, IfTag.class); 
    if (parent == null) {
      throw new JspTagException("condition not inside if"); 
    } 
    // If your tag library is intended to be used ONLY 
    // in JSP 1.2, replace EVAL_BODY_TAG with 
    // EVAL_BODY_BUFFERED. 
    return(EVAL_BODY_TAG); 
  } 

  public int doAfterBody() {
    IfTag parent =
								(IfTag)findAncestorWithClass(this, IfTag.class); 
    String bodyString = getBodyContent().getString(); 
    if (bodyString.trim().equals("true")) {
      parent.setCondition(true); 
    } else {
      parent.setCondition(false); 
    } 
    return(SKIP_BODY); 
  } 
} 

The third step is to define a class to handle the msajsp:then tag. The doStartTag method of this class verifies that it is inside IfTag and also checks that an explicit condition has been set (i.e., that the IfConditionTag has already appeared within the IfTag). The doAfterBody method checks for the condition in the IfTag class, and, if it is true, looks up the body content and prints it. Listing 3.53 shows the code.

The final step in defining tag handlers is to define a class for msajsp:else. This class is very similar to the one that handles the then part of the tag, except that this handler only prints the tag body from doAfterBody if the condition from the surrounding IfTag is false. The code is shown in Listing 3.54.

Listing 3.53. If ThenTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import javax.servlet.*; 

/** The then part of an if tag. */ 

public class IfThenTag extends BodyTagSupport {
  public int doStartTag() throws JspTagException {
    IfTag parent =
								(IfTag)findAncestorWithClass(this, IfTag.class); 
    if (parent == null) {
      throw new JspTagException("then not inside if"); 
    } else if (!parent.hasCondition()) {
      String warning = 
        "condition tag must come before then tag"; 
      throw new JspTagException(warning); 
    } 
    // If your tag library is intended to be used ONLY 
    // in JSP 1.2, replace EVAL_BODY_TAG with 
    // EVAL_BODY_BUFFERED. 
    return(EVAL_BODY_TAG); 
  } 

  public int doAfterBody() {
    IfTag parent =
								(IfTag)findAncestorWithClass(this, IfTag.class);
								if (parent.getCondition()) { 
      try {
        BodyContent body = getBodyContent(); 
        JspWriter out = body.getEnclosingWriter(); 
        out.print(body.getString()); 
      } catch(IOException ioe) {
        System.out.println("Error in IfThenTag: " + ioe); 
      } 
    } 
    return(SKIP_BODY); 
  } 
} 

Listing 3.54. IfElseTag.java
package moreservlets.tags; 

import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 
import javax.servlet.*; 

/** The else part of an if tag. */ 

public class IfElseTag extends BodyTagSupport {
  public int doStartTag() throws JspTagException {
    IfTag parent =
								(IfTag)findAncestorWithClass(this, IfTag.class); 
    if (parent == null) {
      throw new JspTagException("else not inside if"); 
    } else if (!parent.hasCondition()) {
      String warning = 
        "condition tag must come before else tag"; 
      throw new JspTagException(warning); 
    } 
    // If your tag library is intended to be used ONLY 
    // in JSP 1.2, replace EVAL_BODY_TAG with 
    // EVAL_BODY_BUFFERED. 
    return(EVAL_BODY_TAG); 
  } 

  public int doAfterBody() {
    IfTag parent =
								(IfTag)findAncestorWithClass(this, IfTag.class);
								if (!parent.getCondition()) { 
      try {
        BodyContent body = getBodyContent(); 
        JspWriter out = body.getEnclosingWriter(); 
        out.print(body.getString()); 
      } catch(IOException ioe) {
        System.out.println("Error in IfElseTag: " + ioe); 
      } 
    } 
    return(SKIP_BODY); 
  } 
} 

Nested Tags: the Tag Library Descriptor File

Even though there is an explicit required nesting structure for the tags just defined, the tags must be declared separately in the TLD file. This means that nesting validation is performed only at request time, not at page translation time. In JSP 1.1, you could instruct the system to do some validation at page translation time by using a TagExtraInfo class. This class has a getVariableInfo method that you can use to check whether attributes exist and where they are used. Once you have defined a subclass of TagExtraInfo, you associate it with your tag in the tag library descriptor file by means of the teiclass element (tei-class in JSP 1.2), which is used just like tagclass. In practice, however, TagExtraInfo is a bit cumbersome to use. Fortunately, JSP 1.2 introduced a very useful new class for this purpose: TagLibraryValidator. See Chapter 11 (New Tag Library Features in JSP 1.2) for information on using this class.

Listing 3.55. msajsp-taglib.tld (Excerpt 7)
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE ...> 
<taglib> 
  ... 
  <tag>
								<name>if</name>
								<tagclass>moreservlets.tags.IfTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>if/condition/then/else tag.</info>
								</tag>
								<tag>
								<name>condition</name>
								<tagclass>moreservlets.tags.IfConditionTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>condition part of if/condition/then/else tag.</info>
								</tag>
								<tag>
								<name>then</name>
								<tagclass>moreservlets.tags.IfThenTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>then part of if/condition/then/else tag.</info>
								</tag>
								<tag>
								<name>else</name>
								<tagclass>moreservlets.tags.IfElseTag</tagclass>
								<bodycontent>JSP</bodycontent>
								<info>else part of if/condition/then/else tag.</info>
								</tag> 
  ... 
</taglib> 

Nested Tags: the JSP File

Listing 3.56 shows a page that uses the msajsp:if tag three different ways. In the first instance, a value of true is hardcoded for the condition. In the second instance, a parameter from the HTTP request is used for the condition, and in the third case, a random number is generated and compared to a fixed cutoff. Figure 3-27 shows a typical result.

Figure 3-27. Result of IfExample.jsp.


Listing 3.56. IfExample.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>If Tag Example</TITLE> 
<LINK REL=STYLESHEET 
      HREF="JSP-Styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<H1>If Tag Example</H1> 
<%@ taglib uri="msajsp-taglib.tld" prefix="msajsp" %>
								<msajsp:if>
								<msajsp:condition>true</msajsp:condition>
								<msajsp:then>Condition is true</msajsp:then>
								<msajsp:else>Condition is false</msajsp:else>
								</msajsp:if> 
<P> 
<msajsp:if>
								<msajsp:condition><%= request.isSecure() %></msajsp:condition>
								<msajsp:then>Request is using SSL (https)</msajsp:then>
								<msajsp:else>Request is not using SSL</msajsp:else>
								</msajsp:if> 
<P> 
Some coin tosses:<BR> 
<msajsp:repeat reps="10"> 
  <msajsp:if>
								<msajsp:condition><%= Math.random() < 0.5 %></msajsp:condition>
								<msajsp:then><B>Heads</B><BR></msajsp:then>
								<msajsp:else><B>Tails</B><BR></msajsp:else>
								</msajsp:if> 
</msajsp:repeat> 
</BODY> 
</HTML> 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.192.59