[ Team LiB ] Previous Section Next Section

The JSP 2.0 SimpleTag Interface

If you think there's a lot to remember to develop your own custom tags, you're right. Seizing an opportunity to make the task of developing custom tags easier, the JSP 2.0 specification introduced Simple Tag extensions. Simple Tag extensions provide two new ways to create custom tags:

  • Through the use of Java and the SimpleTag interface

  • By using tag files, which consist of JSP code that can be packaged and used as a custom tag

Simple Tags, SimpleTag, and Simple Tag Extensions

graphics/watchout_icon.gif

Unfortunately, tags that implement the basic Tag interface are known as "Simple Tags." It can be difficult to distinguish between "Simple Tag" and "SimpleTag" and "Simple Tag extensions" unless you take some care, particularly when speaking to somebody or searching the Web.


In this section, we'll discuss the SimpleTag interface. Unlike classic tag handlers, SimpleTag does not extend Tag and thereby avoids the complex invocation protocols. SimpleTag declares only one method for handling the tag: doTag. Listing 16.17 shows the "Hello World" custom tag rewritten as a SimpleTag implementation.

Listing 16.17 Source Code for HelloWorldSimpleTag.java
package examples.taglibs;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

public class HelloWorldSimpleTag extends SimpleTagSupport
{
    public void doTag()
        throws JspException, IOException
    {
    getJspContext().getOut().write("Hello World");
    }
}

SimpleTagSupport is a utility class that provides default implementations for the methods of SimpleTag and serves as the base class. The method getJspContext returns a JspContext, which is similar to a PageContext. In fact, JspContext is the base class for PageContext. Its purpose is to abstract all information that isn't specific to servlets, making it possible to use SimpleTag outside of the context of a servlet. In this case, we're using the convenience method getOut to get to the current JspWriter stream.

For this example, the TLD looks the same as it would for a classic tag handler.

JSP Fragments

Working with body content using simple tags requires the use of JSP fragments. JSP fragments are exactly what they appear to be—pieces of JSP code. Written using standard JSP syntax, a fragment is translated into an implementation of the interface JspFragment for use by tag handlers. A JSP fragment is used to represent the body of a tag for use with a SimpleTag handler, or it represents the body of a <jsp:attribute> standard action when the attribute's value is declared to be of type JspFragment.

The JspFragment interface declares only two methods: invoke and getJspContext. You're already familiar with getJspContext. Invoke has the method signature


public void invoke(java.io.Writer out)

Invoke causes the fragment to be executed, writing the output produced to the Writer passed to it. You can invoke a fragment as many times as needed.

Invoke can throw a SkipPageException, which signals that the fragment has determined that the remainder of the page doesn't need to be evaluated.

Accessing the Body of a SimpleTag

Now that you understand the role of a JSP fragment, let's take a look at how to access the body of a SimpleTag. Listing 16.18 shows the example from Listing 16.6 rewritten to use a SimpleTag implementation.

Listing 16.18 Source Code for TestBodySimpleTag.java
package examples.taglibs;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

public class TestBodySimpleTag extends SimpleTagSupport
{
    public void doTag()
        throws JspException, IOException
    {
        getJspBody().invoke(null);
    }
}

Like the previous example, this example simply writes the body to the output stream. When the Writer given to invoke is null, the output from invoke goes to the JspWriter associated with the JspContext of the tag handler.

SimpleTagSupport provides the convenience method getJspBody to return the JspFragment generated for the body content.

Listing 16.19 demonstrates one way that a SimpleTag can obtain a copy of the body so that it can use or manipulate it.

Listing 16.19 Source Code for TestBodySimpleTag2.java
package examples.taglibs;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

public class TestBodySimpleTag2 extends SimpleTagSupport
{
   StringWriter sw = new StringWriter();

   public void doTag()
      throws JspException, IOException
    {
       getJspBody().invoke(sw);
       getJspContex().getOut().write(sw.toString());
    }
}

In this case, the output resulting from the invocation is first captured into a StringWriter before being written to the JspWriter associated with the tag.

TLDs for either of these examples vary slightly from the previous tags in that the <body-content> element of <tag> must now contain a value other than empty.

Listing 16.20 shows what the TLD for this tag might look like.

Listing 16.20 Source Code for TestBodySimpleTag.tld
<?xml version="1.0" encoding="ISO-8859-1"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation=
         "http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
      version="2.0">
   <tlib-version>1.0</tlib-version>
   <short-name>testtag</short-name>
   <description>
       An example of a tag that invokes the body JspFragment
   </description>
   <tag>
     <name>testbody</name>
        <tag-class>examples.taglibs.TestBodySimpleTag </tag-class>
        <body-content>scriptless</body-content>
   </tag>
</taglib>

As you can see, the <body-content> value has been set to scriptless, which instructs JspFragment to evaluate the body content upon invocation. As you learned earlier, <body-content> can also be set to tagdependent, which causes JspFragment to produce the body content verbatim. Using scriptless in our example means that EL expressions and child actions will be evaluated to produce the results.

By now, you are probably impressed with the simplicity of tags based on the SimpleTag interface. In fact, you may wonder why you would ever use classic tags again. For most conditions, you'll probably use simple tags. The most notable exception occurs when the body content, or any fragment in general, contains a JSP expression or scriptlet; fragments must contain only template text and JSP action elements. However, with EL and tag extensions, it's not difficult to do this.

Using Attributes and Variables with SimpleTag

There's not much difference between classic and simple tag handlers when working with attributes or variables. The most significant change occurs because attributes may now have values that are JSP fragments. This means that you have to invoke the fragment. The rest of the mechanisms are the same. Like classic tag handlers, simple tag handlers must provide get and set methods for each attribute. The following code excerpt demonstrates how to do this:


public class UseAttributeSimpleTag extends SimpleTagSupport
{
   private JspFragment attribute;

   public void doTag()
      throws JspException, IOException
    {
       // Do some stuff  ...
       // Now invoke the fragment
       attribute.invoke( null );
       // Do some more ...
    }

public void setAttribute( JspFragment attribute ) {
      this.attribute = attribute;
    }
}

Configuring attributes that are JSP fragments in a TLD is as simple as adding a <fragment> element:


...
  <attribute>
     <name>attribute</name>
     <required>no</required>
     <fragment>true</fragment>
  </attribute
...

When you declare that a tag attribute is a fragment type, you are stating that the handler will be responsible for evaluating the attribute. Otherwise, the container will evaluate the attribute prior to passing it to the tag handler. Also, declaring an attribute to be a fragment means that the type of the attribute is fixed at JspFragment. It's an error to try and declare the type using a <type> element. In addition, <rtexprvalue> is also fixed to true.

All tags use dynamic attributes the same way, and you will be happy to know that the use of variables with simple tags does not change either.

    [ Team LiB ] Previous Section Next Section