Chapter 10. Creating Custom Tags

Welcome to Day 10—yesterday, you took a look at using and creating custom tags, and today you'll get more of the story. Creating a custom tag is actually a very large topic in JSP, which is why two days are dedicated to them. Today, you'll learn how to:

  • Create a simple tag that inserts text into a Web page

  • Use the pageContext object

  • Support custom tag attributes

  • Create iterator tags

  • Make custom tags cooperate, using pageContext attributes

  • Work with scripting variables to pass data between JSP code and custom tag code

Tip

There's even more to the topic of custom tags than there's room for here. You might want to track down more information online—a good place to start is Sun's custom tag tutorial at http://java.sun.com/webservices/docs/ea1/tutorial/doc/JSPTags.html.

That's the program for today. The first topic is about creating a simple tag that can insert text into a Web page.

A Simple Text-Inserting Tag

This first example tag will be called <ch10:message>, and it will insert a message into a Web page. You can see this tag in Listing 10.1. It's an empty tag with no body, so you can condense the opening and closing tags together in this element, XML-style, as you've seen before: <ch10:message />.

Example 10.1. A Simple Tag That Inserts Text (ch10_01.jsp)

<%@ taglib prefix="ch10" uri="WEB-INF/ch10_02.tld" %>
<HTML>
    <HEAD>
        <TITLE>A Simple Tag That Inserts Text</TITLE>
    </HEAD>
    <BODY>
        <H1>A Simple Tag That Inserts Text</H1>
        <ch10:message />
    </BODY>
</HTML>

You may remember from yesterday that to connect a custom tag to the supporting Java code, you need a TLD file. To let JSP find the TLD file, you saw that you can create a web.xml file with a <taglib> element in the WEB-INF subdirectory.

But there's also a shorter way to connect a custom tag to its TLD file. It turns out that you can simply use the relative URL of the TLD file in the taglib directive's uri attribute:

<%@ taglib prefix="ch10" uri="WEB-INF/ch10_02.tld" %> 

That enables JSP to find the TLD file for this <ch10:message> custom tag, ch10_02.tld. But you still have to create that TLD file itself.

Creating a TLD File

The TLD file that connects the <ch10:message> custom tag to its Java code is called ch10_02.tld, and you can start it off as you did yesterday, indicating that it's an XML file and using the standard <!DOCTYPE> element:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
        .
        .
        .
</taglib>

The specification for the tag library itself is stored in the <taglib> element. You were introduced to this element yesterday, but today you'll see a more systematic look at it. This element can contain a number of subelements—some of which you've already seen, and all of which you can see in Table 10.1.

Table 10.1. Subelements of the <taglib> Element

Subelement

Description

<tlib-version>

The tag library's version

<jsp-version>

The JSP version the tag library requires

<short-name>

Name that can be used by a JSP page authoring tool

<uri>

URI that identifies the tag library

<display-name>

Name intended to be displayed by JSP authoring tools

<small-icon>

Small icon that can be used by JSP authoring tools

<large-icon>

Large icon that can be used by JSP authoring tools

<description>

Tag-specific description

<listener>

Event listener classes that can help handle events

<tag>

Creates a custom tag

Here's how you might add the subelements of the <taglib> element in the TLD file for the <ch10:message> element:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>TagExamples</short-name>
  <description>Example tags.</description>
  <tag>
        .
        .
        .
  </tag>
</taglib>

You describe each custom tag with a <tag> element, which itself can contain the subelements you see in Table 10.2. Note that each <tag> element must, at the very least, contain a <name> and a <tag-class> subelement.

Table 10.2. Subelements of the <tag> Element

Subelement

Description

<name>

The unique tag name

<tag-class>

The fully qualified name of the tag's Java class

<tei-class>

Any used class based on javax.servlet.jsp.tagext.TagExtraInfo

<body-content>

The body content type

<display-name>

Name that can be displayed by JSP authoring tools

<small-icon>

Small icon that can be used by JSP authoring tools

<large-icon>

Large icon that can be used by JSP authoring tools

<description>

Tag-specific description

<variable>

Scripting variable data

<attribute>

Tag attribute data

You can see the resulting TLD file, ch10_02.tld, in Listing 10.2, where the file describes the message tag and connects it to the Java class taglib.ch10_03.

Example 10.2. The TLD File (ch10_02.tld)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>TagExamples</short-name>
  <description>Example tags.</description>
  <tag>
    <name>message</name>
    <tag-class>taglib.ch10_03</tag-class>
  </tag>
</taglib>

The next step is to create taglib.ch10_03.

Creating the Java Support

You place the Java class taglib.ch10_03 in the taglib package in the file ch10_03.java (you can use any name for your Java packages, of course):

package taglib; 
        .
        .
        .

The new class, ch10_03, is based on the Java TagSupport class, which you indicate with the extends keyword, as you've already seen:

package taglib; 

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

public class ch10_03 extends TagSupport
{
        .
        .
        .
}

You used the TagSupport class briefly yesterday, and it's time you saw the fields and methods of this class. You can see its fields in Table 10.3, and its members in Table 10.4.

Table 10.3. Fields of the TagSupport Class

Field

Means This

java.lang.String id

Holds an ID value.

PageContext pageContext

Holds a PageContext object.

Table 10.4. Methods of the TagSupport Class

Method

Does This

TagSupport()

Default constructor.

int doAfterBody()

Allows processing for a custom element's body.

int doEndTag()

Allows processing of the end tag returning EVAL_PAGE.

int doStartTag()

Allows processing of the start tag, returning SKIP_BODY.

java.lang.String getId()

Gets the value of the id attribute of this tag.

Tag getParent()

Gets the parent enclosing this tag.

java.lang.Object getValue (java.lang.String k)

Gets the value associated with a key.

java.util.Enumeration getValues()

Enumerates the values kept by this tag handler.

void removeValue(java.lang.String k)

Removes a value associated with a key.

void setId(java.lang.String id)

Sets the id attribute for this tag.

void setPageContext (PageContext pageContext)

Sets the page context to a particular PageContext object.

void setParent(Tag t)

Sets the nesting tag of this tag.

void setValue(java.lang.String k, java.lang.Object o)

Associates a value with a String key.

The TagSupport class is based on two Java interfaces (there will be more on interfaces tomorrow)—the javax.servlet.jsp.tagext.IterationTag and javax.servlet.jsp.tagext.Tag interfaces, so it also gives you access to the fields and methods in those interfaces.

The javax.servlet.jsp.tagext.IterationTag interface has only one field:

  • int EVAL_BODY_AGAIN—. Requests the re-evaluation of the current element's body.

and only one method:

  • int doAfterBody()—. Called after the body of the element has been processed.

The javax.servlet.jsp.tagext.Tag interface is more substantial—you can see its fields in Table 10.5, and its methods in Table 10.6.

Table 10.5. Fields of the javax.servlet.jsp.tagext.Tag Interface

Field

Means This

int EVAL_BODY_INCLUDE

Evaluate the custom element's body and put the result into the returned Web page.

int EVAL_PAGE

Keep evaluating the page.

int SKIP_BODY

Skip evaluation of the body.

int SKIP_PAGE

Skip the rest of the page.

Table 10.6. Methods of the javax.servlet.jsp.tagext.Tag Interface

Method

Does This

int doEndTag()

Processes the end tag for this custom element.

int doStartTag()

Processes the start tag for this custom element.

Tag getParent()

Gets the parent for this tag handler.

void setPageContext(PageContext°pc)

Sets the current page context.

void setParent(Tag°t)

Sets the parent of this tag handler.

To implement the <ch10:message> tag, you can insert a message into the Web page when the end tag (</ch10:message>) is processed. When that end tag is going to be processed, the TagSupport class's doEndTag method is called, and you can support that method in the Java code for this tag:

package taglib; 

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

public class ch10_03 extends TagSupport
{

  public int doEndTag()
{
        .
        .
        .
}

The next thing to do is display a message such as "Hello from JSP!" in the Web page. In JSP code, you just use the out.println method. So, how do you get access to that method in a custom tag? You use the TagSupport class's pageContext field, which gives you access to many of the built-in objects that you can use in JSP.

Working with the Page Context

When you want to get access to the out object or exception object that's available in JSP pages, but you're in a custom tag's Java code, you can use methods of pageContext to get those objects. The pageContext object itself is an object of the PageContext class. You can see the fields of this class in Table 10.7, and its methods in Table 10.8.

Table 10.7. Fields of the PageContext Class

Field

Means This

java.lang.String APPLICATION

Name used to store ServletContext.

int APPLICATION_SCOPE

Specifies application scope.

java.lang.String CONFIG

Name used to store the current servlet configuration.

java.lang.String EXCEPTION

Name used to store uncaught exceptions.

java.lang.String OUT

Name used to store the current JspWriter object.

java.lang.String PAGE

Name used to store the servlet.

int PAGE_SCOPE

Specifies page scope (this is the default).

java.lang.String PAGECONTEXT

Name used to store this PageContext object.

java.lang.String REQUEST

Name used to store the ServletRequest.

int REQUEST_SCOPE

Specifies request scope.

java.lang.String RESPONSE

Name used to store ServletResponse in PageContext name table.

java.lang.String SESSION

Name used to store HttpSession in PageContext name table.

int SESSION_SCOPE

Specifies session scope (only valid if this page participates in a session).

Table 10.8. Methods of the javax.servlet.jsp.tagext.Tag Interface

Method

Does This

java.lang.Object findAttribute (java.lang.String name)

Tries to find an attribute in page, request, session (if valid), and application scope(s) and returns the attribute's value or null.

void forward(java.lang.String relativeUrlPath)

Forwards the current ServletRequest and ServletResponse objects to another component in the application.

java.lang.Object getAttribute (java.lang.String name)

Gets the object associated with the attribute in the page scope, or null if not found.

java.lang.Object getAttribute (java.lang.String name, int scope)

Gets the object associated with the attribute in the specified scope, or null if not found.

java.util.Enumeration getAttributeNamesInScope (int scope)

Gets all the attributes in a given scope.

int getAttributesScope(java.lang.String name)

Gets the scope where a given attribute is defined.

java.lang.Exception getException()

Gets the value of the exception object.

JspWriter getOut()

Gets the out object (a JspWriter object).

java.lang.Object getPage()

Gets the page object (a Servlet object).

ServletRequest getRequest()

Gets the request object (a ServletRequest object).

ServletResponse getResponse()

Gets the response object (a ServletResponse object).

ServletConfig getServletConfig()

Gets the ServletConfig object.

ServletContext getServletContext()

Gets the ServletContext object.

HttpSession getSession()

Gets the session object (an HttpSession object).

void handlePageException(java.lang.Exception e)

Handles page exceptions.

void handlePageException(java.lang.Throwable t)

Same as handlePageException (Exception), except that it accepts a Throwable object.

void include(java.lang.String relativeUrlPath)

Includes the resource specified as part of the current ServletRequest and ServletResponse objects.

void initialize(Servlet servlet, ServletRequest request, ServletResponse response, java.lang.String errorPageURL, boolean needsSession, int bufferSize, and boolean autoFlush)

Initializes a PageContext object.

void release()

Resets the internal state of a PageContext object.

void removeAttribute(java.lang.String name)

Removes the attribute associated with the given name in page scope.

void removeAttribute(java.lang.String name, int scope)

Removes the attribute associated with the given name in the given scope.

void setAttribute(java.lang.String name, java.lang.Object attribute)

Sets the name and value specified in page scope.

void setAttribute(java.lang.String name, java.lang.Object o, int scope)

Sets the name and value specified in the given scope.

As you see in Table 10.8, you can use the pageContext object's getOut method to get access to the out object. With that object in hand, all you need is to use a method like print or println to send text to the Web page. You can also return a constant like EVAL_PAGE to indicate that you want the rest of the page evaluated:

package taglib; 

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

public class ch10_03 extends TagSupport
{

  public int doEndTag() throws JspException
{
    try {
      pageContext.getOut().print("Hello from JSP!");
        .
        .
        .
    }
    return EVAL_PAGE;
  }
}

When you use methods of the out object in a custom tag's code, Java will insist that you handle any exceptions that can come up, so you can add some code to throw that exception back to the default JSP exception handler, as you see in Listing 10.3.

Example 10.3. Supporting a Simple Tag (ch10_03.java)

package taglib;

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

public class ch10_03 extends TagSupport
{

  public int doEndTag() throws JspException
{
    try {
      pageContext.getOut().print("Hello from JSP!");
    } catch (Exception e) {
      throw new JspException(e.toString());
    }
    return EVAL_PAGE;
  }
}

And that's all you need—take a look at ch10_01.jsp; you can see the results in Figure 10.1.

Using a simple custom tag.

Figure 10.1. Using a simple custom tag.

That gives you a start with a simple tag that is able to insert some text into a Web page, and covers the creation of a basic tag. However, there's a great deal more to creating custom tags—for example, you can also support attributes.

Creating and Supporting Attributes in Custom Tags

You can support attributes in custom tags; for example, you can add an attribute to the <ch10:message> element that enables you to set the text that the element will display. If you were to call this attribute text, using it might look like this, where you want to display the message “Hello there!”:

The text is: <ch10:message text="Hello there!"/> 

How do you create and support such an attribute? You create it in the TLD file and support it in your Java code. In the TLD file, you use an <attribute> element, which has the subelements you see in Table 10.9 (note that of the subelements you see in that table, only <name> is required).

Table 10.9. Subelements of the <attribute> Element

Subelement

Does This

<name>

Gives the attribute a name.

<required>

Indicates whether an attribute is required to be present when you use the corresponding tag. Set to true, false, yes, or no.

<rtexprvalue>

Indicates whether the attribute can be assigned an expression's value at runtime. Set to true, false, yes, or no.

<description>

Gives a description of the attribute.

<type>

Sets the Java type of the attribute. Use a type fully qualified with package name, such as java.lang.String, not just String.

For example, Listing 10.4 shows the new version of the <ch10:message> element at work in a Web page that you can load to use this attribute.

Example 10.4. Supporting Attributes (ch10_04.jsp)

<%@ taglib prefix="ch10" uri="WEB-INF/ch10_05.tld" %>
<HTML>
  <HEAD>
        <TITLE>Supporting Attributes in Custom Tags</TITLE>
    </HEAD>
    <BODY>
        <H1>Supporting Attributes in Custom Tags</H1>
        The text is: <ch10:message text="Hello there!"/>
    </BODY>
</HTML>

The next step is to create the text attribute, adding it to the <ch10:message> element in the TLD file. You can see how that works in Listing 10.5, which includes a new <attribute> element for the text attribute, specifying the attribute's name and type (if you don't specify a type, the default is java.lang.String).

Example 10.5. The TLD File for Supporting an Attribute (ch10_05.tld)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>TagExamples</short-name>
  <description>Example tags.</description>
  <tag>
    <name>message</name>
    <tag-class>taglib.ch10_06</tag-class>
    <attribute>
      <name>text</name>
      <type>java.lang.String</type>
    </attribute>
  </tag>
</taglib>

That adds a new attribute to the <ch10:message> element as far as JSP is concerned. So, how do you support the new attribute in Java code? You do it by treating it as a property, with a set method. For example, you can create a new String variable named text, and add a method named setText to your Java code:

public class ch10_06 extends TagSupport 
{
  String text;

  public void setText(String s)
  {
    text = s;
  }

  public int doEndTag() throws JspException
{
    try {
        .
        .
        .
    }
    return EVAL_PAGE;
  }
}

Now JSP will call the setText method automatically with the value of the text attribute at runtime. That's great in this case, because that attribute's value is then stored in the internal variable text, which you can then display in the Web page using the pageContext.getOut().print method, as you see in Listing 10.6.

Example 10.6. Supporting Attributes in Code (ch10_06.java)

package taglib;

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

public class ch10_06 extends TagSupport
{
  String text;

  public void setText(String s) {
    text = s;
  }

  public int doEndTag() throws JspException
{
    try {
      pageContext.getOut().print(text);
    } catch (Exception e) {
      throw new JspException(e.toString());
    }
    return EVAL_PAGE;
  }
}

That's all you need—you can see the results in Figure 10.2, where the code has read the text attribute's value and displayed it in a Web page. You've made considerable progress working with custom tags.

Support attributes in custom tags.

Figure 10.2. Support attributes in custom tags.

There's another kind of tag besides the simple kind we've been working with here—iterating tags, which act like built-in loops. They're coming up next.

Iterating Tags

Suppose that you want to display a list of items, such as this one, in a Web page:

Cast member: Ralph 
Cast member: Alice
Cast member: Ed
Cast member: Trixie
Iterating Tags

This next example shows how this works. Here, you'll create a new element named <ch10:iterator> that will create the list you saw previously. You can place text in this element in a JSP page and that text will be repeated in each iteration, as you can see:

<ch10:iterator> 
    Cast member:
</ch10:iterator>

But what about the new text—the names of the various cast members—that this new element is to display? You can store them in an array called, say, names, and make that array an attribute of the pageContext object using the setAttribute method as you see in Listing 10.7. That will give the code for the <ch10:iterator> element access to those names.

Example 10.7. Using Iterating Tags (ch10_07.jsp)

<%@ taglib prefix="ch10" uri="WEB-INF/ch10_08.tld" %>
<HTML>
    <HEAD>
        <TITLE>Supporting Iterating Custom Tags</TITLE>
    </HEAD>

    <BODY>
        <H1>Supporting Iterating Custom Tags</H1>
        <%
            String[] names = new String[]{ "Ralph", "Alice", "Ed", "Trixie" };
            pageContext.setAttribute("names", names);
        %>

        <ch10:iterator>
            Cast member:
        </ch10:iterator>
    </BODY>
</HTML>

The next step in implementing the <ch10:iterator> element is to add it to a TLD file, as you see in Listing 10.8.

Example 10.8. The TLD File for the Iterating Tag (ch10_08.tld)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>TagExamples</short-name>
  <description>Example tags.</description>
  <tag>
    <name>message</name>
    <tag-class>taglib.ch10_06</tag-class>
    <attribute>
      <name>text</name>
      <type>javalang.String</type>
    </attribute>
  </tag>
  <tag>
    <name>iterator</name>
    <tag-class>taglib.ch10_09</tag-class>
  </tag>
</taglib>

In the Java code for this new tag, you can use the pageContext object's getAttribute method to get and store this array. You can do that as soon as JSP starts evaluating the tag, in the doStartTag method:

package taglib; 

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

public class ch10_09 extends TagSupport
{
  private String[] names = null;

  public int doStartTag()
  {
    names = (String[]) pageContext.getAttribute("names");
    return EVAL_BODY_INCLUDE;
  }
        .
        .
        .

Note

Note that this Java class is based on the TagSupport class, not on the IterationTag interface you saw earlier today that actually supports iterating tags. That's no problem, because the TagSupport class itself implements the IterationTag interface.

In this way, JSP pages can pass data items such as arrays (which cannot be stored in tag attributes) to Java code for custom tags—and as we'll see in a few pages, page context attributes can also let custom tags “cooperate” with each other by passing data back and forth.

Now that you have access to the names this element is supposed to display, you can display them by evaluating the body of the element several times. You can do that with the IterationTag interface's doAfterBody method, which is called after the body has been evaluated (and displays the element body's text, "Cast member:", in the Web page iteration).

If you return the IterationTag interface's constant EVAL_BODY_AGAIN from the doAfterBody method, the body is evaluated again, giving you the chance to print out another name. If you return the constant SKIP_BODY, on the other hand, JSP will no longer iterate over the body. To determine when to stop iterating, you can create a counter variable (just named counter here) and stop iterating when you've displayed all the names in the names array. Here's what that looks like in the doAfterBody method:

public class ch10_09 extends TagSupport 
{
  private int counter = 0;
  private String[] names = null;
        .
        .
        .
  public int doAfterBody() throws JspException
  {
    try{
      pageContext.getOut().print(" " + names[counter] + "<BR>");
    } catch(Exception e){
      throw new JspException(e.toString());
    }
    counter++;
    if(counter >= names.length) {
      return SKIP_BODY;
    }
    return EVAL_BODY_AGAIN;
  }
}
  }
}

You can see the complete Java support for this new iterating tag in Listing 10.9.

Example 10.9. Iterating Java Support (ch10_09.html)

package taglib;

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

public class ch10_09 extends TagSupport
{
  private int counter = 0;
  private String[] names = null;

  public int doStartTag()
  {
    names = (String[]) pageContext.getAttribute("names");
    return EVAL_BODY_INCLUDE;
  }

  public int doAfterBody() throws JspException
  {
    try{
      pageContext.getOut().print(" " + names[counter] + "<BR>");
    } catch(Exception e){
      throw new JspException(e.toString());
    }
    counter++;
    if(counter >= names.length) {
      return SKIP_BODY;
    }
    return EVAL_BODY_AGAIN;
  }
}

You can see the results of this new iterating element in Figure 10.3. As you can see, this element did indeed iterate over the data in the names array, displaying a new item from that array in each iteration.

Using an iterating custom element.

Figure 10.3. Using an iterating custom element.

Now you are able to build iterating custom tags. Note that iterating tags have also introduced the idea of passing data using the pageContext object's attributes. That's the subject of a whole new topic in custom tag work—creating cooperating tags, which can pass data back and forth using such attributes.

Cooperating Tags

As you saw in the previous topic, you can pass data to custom tags by setting pageContext object attributes. Here's the way that worked when you set an attribute called names:

<% 
    String[] names = new String[]{ "Ralph", "Alice", "Ed", "Trixie" };
    pageContext.setAttribute("names", names);
%>

However, you can also use custom tags themselves to set pageContext attributes that will be read by other custom tags. To see how that works, this next example will create a new custom tag, <ch10:createCast>, which will store the names the <ch10:iterator> tag needs using the names attribute. You can see this new tag, <ch10:createCast>, at work in a new JSP page, Listing 10.10. Note that all you have to do to set the names attribute is use the <ch10:createCast> tag—then the <ch10:iterator> tag can make use of that attribute.

Example 10.10. Using Cooperating Tags (ch10_10.jsp)

<%@ taglib prefix="ch10" uri="WEB-INF/ch10_11.tld" %>
<HTML>
    <HEAD>
        <TITLE>Supporting Cooperating Custom Tags</TITLE>
    </HEAD>

    <BODY>
        <H1>Supporting Cooperating Custom Tags</H1>
        <ch10:createCast/>
        <ch10:iterator>
            Cast member:
        </ch10:iterator>
    </BODY>
</HTML>

You also have to add this new tag to the TLD file, as you see in Listing 10.11.

Example 10.11. The Cooperating Tags TLD (ch10_11.tld)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>TagExamples</short-name>
  <description>Example tags.</description>
  <tag>
    <name>message</name>
    <tag-class>taglib.ch10_06</tag-class>
    <attribute>
      <name>text</name>
      <type>javalang.String</type>
    </attribute>
  </tag>
  <tag>
    <name>iterator</name>
    <tag-class>taglib.ch10_09</tag-class>
  </tag>
  <tag>
    <name>createCast</name>
    <tag-class>taglib.ch10_12</tag-class>
  </tag>
</taglib>

All this new tag, <ch10:createCast>, does is create the names array that the <ch10:iterator> tag will use and store that array in the names attribute. You can see how that works in Listing 10.12.

Example 10.12. Cooperating Tags Java Support (ch10_12.java)

package taglib;

import javax.servlet.jsp.tagext.*;

public class ch10_12 extends TagSupport
{

  public int doStartTag()
  {
    String[] names = new String[] {"Ralph", "Alice", "Ed", "Trixie"};
    pageContext.setAttribute("names", names);

    return SKIP_BODY;
  }
}

You can see the results in Figure 10.4—just like the previous example, except that this time, the data the iterating tag displays comes from a cooperating tag.

Using cooperating custom tags.

Figure 10.4. Using cooperating custom tags.

Using Scripting Variables

You've seen that you can supply data from a JSP page to a custom tag by setting a pageContext attribute—for example, you saw how to set an attribute called names that was used by the custom iterating tag:

<% 
    String[] names = new String[]{ "Ralph", "Alice", "Ed", "Trixie" };
    pageContext.setAttribute("names", names);
%>

<ch10:iterator>
    Cast member:
</ch10:iterator>

You can also go the other way—setting attributes in custom elements and reading them in JSP code. You can see an example in Listing 10.13, where the <ch10:createCast/> element assigns the names array to the names attribute, and the JSP code loops over that array and displays those names.

Example 10.13. Using the PageContext Object (ch10_13.jsp)

<%@ taglib prefix="ch10" uri="WEB-INF/ch10_11.tld" %>
<HTML>
    <HEAD>
        <TITLE>Using the pageContext Object</TITLE>
    </HEAD>

    <BODY>
        <H1>Using the pageContext Object</H1>
        <ch10:createCast/>
        <%
            String[] names = (String [])pageContext.getAttribute("names");
        %>

        <%
            for(int loopIndex = 0; loopIndex < names.length; loopIndex++) {
                out.println("Cast member: " + names[loopIndex] + "<BR>");
            }
        %>
    </BODY>
</HTML>

You can see this Web page at work in Figure 10.5, where the JSP code has read data from a custom tag using page context attributes.

Getting data from a custom tag.

Figure 10.5. Getting data from a custom tag.

Note that in this example, you have to use a scriptlet to get access to the names attribute and assign it to the names array:

<% 
    String[] names = (String [])pageContext.getAttribute("names");
%>
Getting data from a custom tag.

To support a scripting variable, you use the <variable> element in a TLD file. The <variable> element supports the subelements you see in Table 10.10—you need to use at least the <name-given> element or the <name-from-attribute> element, but the rest of the subelements are optional.

Table 10.10. Subelements of the <variable> Element

Subelement

Description

<name-given>

Gives the variable a name.

<name-from-attribute>

The name of an attribute whose value will give the name of the variable.

<variable-class>

Fully qualified name of the class of the variable. The default is java.lang.String.

<declare>

Indicates whether the variable refers to a new object. Set to true (the default) or false.

<scope>

The scope of the scripting variable. See Table 10.11; NESTED is the default.

The possible values for the <scope> element you see in Table 10.8 appear in Table 10.11; this element enables you to set the scope of the scripting variable.

Table 10.11. The <variable> Element's Scope

Value

Scope

Available in Methods

NESTED

Between the start tag and end tag

doInitBody, doAfterBody, doStartTag

AT_BEGIN

From start tag to end of page

doInitBody, doAfterBody, doEndTag, doStartTag

AT_END

After end tag to end of page

doEndTag

For example, if you use a scripting variable for the names array, you don't have to pass that array from a custom tag to JSP code using pageContext attributes—that array will already be available to your JSP code.

To see how that works, you can implement the names scripting variable in the <ch10:createCast> tag. When you do, that variable will become available to your JSP code automatically—no use of attributes needed. The resulting JSP page appears in Listing 10.14—as you can see. Now the <ch10:createCast /> element fills the names variable automatically (you don't have to use getAttribute), and then you can use that variable in your code.

Example 10.14. Using Scripting Variables (ch10_14.jsp)

<%@ taglib prefix="ch10" uri="WEB-INF/ch10_15.tld" %>
<HTML>
    <HEAD>
        <TITLE>Using Scripting Variables</TITLE>
    </HEAD>

    <BODY>
        <H1>Using Scripting Variables</H1>
        <ch10:createCast/>
        <%
            for(int loopIndex = 0; loopIndex < names.length; loopIndex++) {
                out.println("Cast member: " + names[loopIndex] + "<BR>");
            }
        %>
    </BODY>
</HTML>

How can you make the names array into a scripting variable available to your JSP scriptlets? All you need to do to create this scripting variable is declare it in the TLD file, as you see in Listing 10.15.

Example 10.15.  The Scripting Variables TLD File (ch10_15.tld)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>TagExamples</short-name>
  <description>Example tags.</description>
  <tag>
    <name>message</name>
    <tag-class>taglib.ch10_06</tag-class>
    <attribute>
      <name>text</name>
      <type>javalang.String</type>
    </attribute>
  </tag>
  <tag>
    <name>iterator</name>
    <tag-class>taglib.ch10_09</tag-class>
  </tag>
  <tag>
    <name>createCast</name>
    <tag-class>taglib.ch10_12</tag-class>
    <variable>
      <name-given>names</name-given>
      <variable-class>java.lang.String []</variable-class>
      <declare>true</declare>
      <scope>AT_END</scope>
    </variable>
  </tag>
</taglib>

And that's all you need—the names variable is filled automatically from the names array in the Java code for the <ch10:createCast> tag. You can see the results in Figure 10.6. Now you're communicating between custom tags and JSP code easily, using scripting variables.

Using scripting variables.

Figure 10.6. Using scripting variables.

You've seen that a great deal is possible with custom tags. (In fact, even more is possible with the advanced class BodyTag, but that's beyond the scope of this book.) If you want to provide your Java code to JSP users who aren't interested in using Java directly, custom tags offer an excellent alternative.

Summary

Today you learned how to create and work with custom tags. You learned that basing custom tags on the Java TagSupport class is a good choice for basic custom tags, including iterating tags.

You learned that various methods in classes based on the TagSupport class are called at various times when a custom tag is called. For example, when JSP first encounters a custom tag, the doStartTag method in that tag's Java code is called. You can return constants like SKIP_BODY or EVAL_BODY_INCLUDE to tell JSP how you want to process the custom element.

You also learned that you can support attributes in custom tags by adding an <attribute> element to the tag's TLD file. After creating an attribute in this way, you can support it in your Java code by adding a property to that code in the same way as you'd support a property in a JavaBean—you create a variable with the same name as the attribute and implement a set method for the attribute.

You can also create iterating tags using the doAfterBody method and returning the constant EVAL_BODY_AGAIN to iterate again, and the constant SKIP_BODY to stop iterating.

Finally, you learned how to transfer data from JSP code to Java custom tag code using both page context attributes and scripting variables. Tomorrow, you'll get some additional Java experience as you start creating more powerful JavaBeans.

Q&A

Q1:

Is there any way to support custom tags with Java code that is not precompiled, such as the Java code I use in scriptlets?

A1:

No, not yet—you must still use compiled Java classes, whether in .class or JAR files, to add code support to custom tags.

Q2:

Yesterday showed how to connect custom tags to TLD files with the web.xml file, but today showed that I don't have to use the web.xml file for that purpose. Is there a preferred technique?

A2:

No, although note that avoiding editing web.xml makes it easier to install custom tags. The big custom tag libraries already available tend to use web.xml files.

Workshop

This workshop tests whether you understand all the concepts you learned today. It's a good idea to master today's concepts by honing your knowledge here before starting tomorrow's material. You can find the answers to the quiz questions in Appendix A.

Quiz

1:

Name at least three subelements of the <taglib> element.

2:

Name at least three subelements of the <tag> element.

3:

What TagSupport method is called when a custom tag is first encountered? When its end tag is encountered?

4:

If you wanted to create an attribute with a String value, which subelements must you use in an <attribute> element?

5:

How do you get the value of a page attribute set in a scriptlet in Java code for a custom tag?

Exercises

1:

Attributes can be required or not, depending on the <attribute> element's <required> subelement. Give this a try: editing Listing 10.5, make the text attribute required with a <required>true</required> subelement. Then omit that attribute in a <ch10:message> element, and watch what happens when you try to load the element's Web page.

2:

You can also nest custom tags. Give that a try by nesting <ch10:message> elements inside a <ch10:iterator> element, creating an HTML list (using <LI> elements inside an <UL> element) that lists either the cast members (Ralph, Alice, Ed, and Trixie) used in today's examples, or a list of your own friends.

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

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