Writing Custom JSP Tag Libraries

by

John B. Moore

Micro-Phyla Systems


Overview

One of the first impressions many got when viewing the source code for a JSP page when it was first introduced was "How in the world can that mess ever be maintained?". With the mixing of HTML, Java, and special JSP tags, there was justification for this feeling. Fortunately, the JSP API did not stop there, and further advances in the specification developed solutions to this major and significant concern. Some still argue that it was not enough. That will be for you to decide. Using a combination of JavaBeans, standard JSP tags and custom JSP tags, the separation of presentation and logic can be achieved. This paper will focus on one of the most significant components in this separation Custom JSP Tags.

On the surface, custom JSP tags appear to be rather mysterious. They are not. There is a very clear and definitive architecture that, once it is understood, allows development to proceed quite easily. It does require significant planning and up-front coding investments. Be forewarned that there are entire 600 page books on Tag library development so this short paper will leave much out, but hopefully will give you the start you need, and most importantly, lower the apprehension and learning curve, as well as show you how to leverage JBuilder in your tag development. Though it is assumed you have a basic understanding of JSP and where Custom JSP Tags fit in, the intention is to get you started on actual development and enthused about the journey, not to deliver you completely to the destination.


Tag Anatomy 101

A Tag is specific collection of characters that tell a page compiler to stop and go out and perform some task at this location. There has to be some "description" or road map for the page compiler to locate the "code" that it must process. There can be parts of that tag that modify the process in some way and can be changed by the person using that tag. Most of you will have a basic understanding of tags, if you ever have used HTML. The "font" tag,

<font style="Ariel" size="2">Some text</font>

tells the HTML page processor to take the text enclosed and convert that text to Ariel and then change the size to "2". What we need to discover is how we can take this simple concept and use this to embed sophisticated actions using the Custom JSP Tag API.

Tags have their own library package, javax.servlet.jsp.tagext, that contains an interface called Tag. To enable and simplify Tag authoring, Sun provides a library that contains some interface definitions and helper classes.

The tag interface looks like this:

// tag interface
  int doStartTag() 
  int doEndTag() 

  Tag getParent() 
  void setParent(Tag t) 

  void release() 
  void setPageContext(PageContext pc)

This interface must be implemented to write a simple tag. Since much of this interface is often implemented the same way by most tag authors, there is a helper class called TagSupport. TagSupport implements all the methods from Tag and adds a few more of its own. To implement a simple tag, typically you would extend TagSupport, overriding the necessary methods. In addition to TagSupport, there is another important helper class, BodyTagSupport. This convenience class provides the additional default implementations for the BodyTag interface. The BodyTag interface extends Tag, and adds support for inspecting and manipulating the body of a tag. Between the two interfaces and their helper classes, the Tag author can respond with custom actions at the tag level, as well as use and manipulate the body contents of the tag.

JSP Parser vs. JSP Runtime vs JSP compiler

There will be references in what follows to parsers, compilers and runtime. There is unfortunately overlap in common usage. For clarity, when I refer to the JSP parser, I am discussing the interpretation of the file, either the actual JSP or the TLD file. The JSP compiler translates that parsed output into a servlet and the JSP Runtime does the actual job of executing the compiled servlet. Often I see these terms mixed up a bit, and I dare say I probably am guilty as well, but I will endeavor to be careful.

Tags vs Scriptlets

Scriptlets are tags. Scriptlets are special JSP tags in which Java code can be embedded in the HTML page. Abuse of Scriptlets has given JSP a bad name with many people. Here is an example of the type of thing that I feel should not happen:

<%
    for ( int i = 0; i < 10; i++ ) {
    %>
    <TR>
      <TD>Number</TD>
      <TD><%= i+1 %></TD>
    </TR>
 <%
    } 
 %>

This bit of Scriptlet code is creating rows in a table. What you have in this example is Java embedded in HTML writing HTML. This does not cause problems for most Java programmers, but to an HTML designer this looks like "Greek" What is needed is something that might look like:

<mylib:tablelist start="1" end="10"/>

Which could also be written as:

<mylib:tablelist start="1" end="10"></mylib:tablelist>

The HTML designer now can make more sense of what is happing. "Make a table that starts with 1 and ends with 10". Behind the "curtain", that same Java code is probably exactly what is used, but it has be hidden, "separated" from the page definition. The page compiler sees the tag and then goes to a "tag library descriptor" (TLD) which is an xml definition file that tells the compiler to inline a method with code from a specific Java class using the arguments of 1 and 10. The compiler knows what method to call, as it is part of the API specification (more on that later), and the returned "string" is embedded in the HTML document at that location.

Specifically, the functional differences between Scriptlets and Custom Tags are:

  1. Syntax is simpler.
  2. Easier to test and debug because there is a clear separation of languages.
  3. Better support for tags in most development environments.
  4. Clear separation of presentation and logic.

Tag Library Descriptor

At the simplest level, the Tag Library Descriptor (TLD) is an xml document with a specific DTD that tells the page compiler how to find and use your custom tag and inline the necessary methods and logic. It provides the following:

  1. Name of Tag.
  2. Fully qualified class.
  3. Whether to use the body contents or not.
  4. Define Attributes that the tag uses to modify the tag behavior.
  5. Define which attributes are required or optional.

The JSP compiler and runtime will use this information to locate the correct class, and provide the attribute values to the tag Java code. In a way, the TLD is a "contract" between the tag in the document and the custom Java class that the Java programmer wants to execute at that point in the document. If the TLD file is included in the jar file, it must be in a META-INF directory inside the .jar. The TLD file only needs to be included in the .jar file if the URI attribute in the taglib directive is the name of the .jar file used.

Tags and Attributes

As illustrated above, tags can have attributes. Attributes allow the HTML designer to pass values or properties that the tag designer can use to alter the behavior of the tags response. Think of them as "arguments" to a method. Attribute names should be descriptive and self-documenting. As the tag designer, your response to the attribute values should be clear and predictable. Documenting the attributes and their possible values is critical.

Basic Tag Deployment Components

Tags require the support of a number of components. All of these components must be properly deployed. Those components are:

This can seem a bit intimidating at first with so many "pieces", but this all provides the power and flexibility that makes custom JSP tags so useful. A little practice and it will get fairly routine. To help, the Tag Libraries page in JBuilder web.xml properties pages, has an entry tab page to map URIs used in a JSP with the actual locations of the Tag Library Definition (TLD) files. Other aspects of the TLD file will be covered as we explore various aspects of the custom JSP tag API.

Custom Tag Requirements

Tag API

The Tag API provides the interface for responding to the necessary events associated with a tag. One would expect to respond to the start of the tag and the end of the tag. But in addition, the tag needs to be aware of its "context" on the page via "pagecontext", as well as the position of this tag relative to other tags in "getParent". A Basic management function "release" allows the tag to clean up after itself.

doStartTag()

In the basic Tag API, this is the method that is most often implemented. The helper class TagSupport is designed to provide implementations for all of the other methods, allowing the Tag author to focus on the basic purpose of the tag for the core of the custom code.

doEndTag()

Not as commonly used for basic functionality, the doEndTag() method is used for cleanup and handling any tasks that you want to occur at the very end.

A class that implements these methods is referred to as a "Tag handler". Following, is a simple example that will be used again during our "Basic First Example".

public class CustTag extends TagSupport {
  private customer g_cust = null;
  private HttpServletRequest g_request = null;
  private HttpSession g_session = null;
  private String g_sName = "error";

  public CustTag() {
    g_request = (HttpServletRequest) this.pageContext.getRequest();
    g_session = g_request.getSession();
  }

  public int doStartTag() {

     try {
        JspWriter _out = this.pageContext.getOut();
        //first retreive the service javabean
        g_cust = (customer)g_session.getAttribute("custbean");

        if (g_sName.equals("custID")) {
          _out.print(g_cust.getCustID());
        } else if (g_sName.equals("lastName")) {
          _out.print(g_cust.getLastName());
        } else if (g_sName.equals("address")) {
          _out.print(g_cust.getAddress());
        } else {
          _out.print("Wrong Attribute value");
        }
     } catch (java.io.IOException ioex) {
        //error handler here
     }
     return (SKIP_BODY);
  }

   public void setFieldVal(String arg_sVal) {
      g_sName = arg_sVal;
  }

}

The example implements the doStartTag() and utilizes a number of tools available to the Tag author. The request object is retrieved via the pageContext.getRequest() and from that the session object is retrieved. The response's "out" reference is also acquired via the pageContext. Basically, the pageContext object is the "key to the castle" and allows the Tag author to integrate the custom JSP tag into the entire JSP environment.

How does the JSP runtime know how to run this code? There are a number of steps that lead to the actual execution of the above code, all of which must be handled by the JSP parser and compiler before we ever get to the actual executable code. There are a number of minimal steps that will set the stage for the JSP compiler to create the code for the JSP runtime.

The first step we have completed. The next step is to create the 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"> 
<!-- a tag library descriptor --> 

<taglib>

  <tlibversion>1.0</tlibversion>
  <jspversion>1.1</jspversion> 

<shortname>customerjsp</shortname> 
<uri>http://www.microps.com/JSPDev/customer_taglib</uri>
 <info>
        Sample Tag Library Descriptor - Customer
  </info>
  <!-- Defines a tag that supplies a value based on the name attribute --> 

<tag> 
    <name>customer</name> 
    <tagclass>com.microps.jsptags.CustTag</tagclass> 
    <bodycontent>EMPTY</bodycontent> 
    <info> Insert values based on attribute values </info> 
    <attribute> 
       <name>fieldVal</name> 
       <required>true</required> 
    </attribute> 
  </tag> 

</taglib>

The bold portions indicate the "custom" portions, and the non-bold indicate the default requirements for all tag libraries. What I have done is to create a template hot key in JBuilder that inputs all the basics. To do this, go to Tools|Editor Options|Templates and then click "Add". Add a new name, for example "taglib", and then a description. Click "OK". Then paste the above code into that template. Clean out the specific values and close. Now the basics are created for you by typing "taglib" then Ctrl-J. If you don't use this templating feature for all of your coding, you are missing a very important time saver in JBuilder.

The "shortname" and "uri" are mostly for IDE and other tools. The important parts begin with the "tag" tag. The "name" is used for the JSP parser to label each tag. Every tag is stored in the TagLibraryInfo object which is used later by the JSP parser to lookup tag information while parsing the JSP file. Each individual tag is then contained in a "TagInfo" object. "Tagclass" is the fully qualified name of the class that the JSP parser must instantiate. The "bodycontent" indicates whether the body will be evaluated and how.

A "bodycontent" of "empty" means that the body will not be evaluated, an entry of "JSP" means the body might be evaluated and that its contents should be evaluated as though it was JSP content. (By extension this means that any HTML content is acceptable as well)

The next tag group is the "attribute" definition. The attribute of "fieldVal" is a "required" attribute. This means that the JSP parser will throw an error if this attribute is missing during page compilation.

Following is a summary of what we just covered:

Note: The version of the tag lib DTD is important. 

http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd" 

I used this version in my examples for maximum portability.  The advance features for
JSP 1.2  are only available if you use the latter DTD version: 

"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_2.dtd" 

Be aware of this detail.

The tag is now defined. Next, it has to be registered with the JSP parser, compiler, and runtime. This is a critical step in that the JSP parser and compiler must be able to find and parse the TLD file. JBuilder has a handy tool for registering a custom JSP tag. In the navigation browser pane, open the webapp node and then the "Deployment descriptors" node.

Double click on the "web.xml" file and this will fire off the GUI view of this file using the "WebApp DD Editor". In the structure pane, click the "Tag Libraries" node. In the DD Editor click "Add" and a new record is opened.

The burning question now is "What is needed for a "TagLib URI" and "TagLib Location"?" This can be a bit confusing at first. The "TagLib URI" can be a real URL path to the TLD file. Generally, this is not a good idea because of the variations in deployment. More typically, the "URI" is a "reference" label that will be used both here and in the JSP file (more on that later). Next, the "TagLib Location" must be defined. Where do we put the TLD file?

Technically, anywhere is acceptable, but realistically we want to make sure it is packaged in the WAR file. A good location is anywhere in the WEB-INF branch of the webapp. One feature I would like to see added is for JBuilder to support showing the TLD files if they are placed in this part of the webapp structure. For now, save the TLD to the WEB-INF directory, and use this as the location in the Tag Library definition.

Clicking the "Source" tab will now show the following:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> 

<web-app> 

  <taglib> 

    <taglib-uri>http://www.microps.com/JSPDev/customer_taglib</taglib-uri> 
    <taglib-location>/WEB-INF/customer_taglib.tld</taglib-location>

  </taglib> 

</web-app>

Our last step is to use this custom tag in a JSP. Following is an example:

<HTML> 
<HEAD>
    <%@ taglib url="http://www.microps.com/JSPDev/customer_taglib" prefix="mpsjsp" %> 
	 <TITLE></TITLE> 
  </HEAD> 
  <BODY>
	 <DIV ALIGN="CENTER">
		<H1>Customer Info</H1> 
<TABLE WIDTH="100%" BORDER="2" CELLPADDING="0" CELLSPACING="0">
   <TR>
     <TD>Customer ID:<mpsjsp:customer fieldVal="custID"/> </TD>
   </TR>
   <TR>
     <TD>Customer Name:<mpsjsp:customer fieldVal="lastName"/></TD>
   </TR>
   <TR>
     <TD>Address:<mpsjsp:customer fieldVal="address"/></TD>		  
   </TR> 
</TABLE></DIV></BODY> 
</HTML>

We have now assembled all the pieces. Let's tie them together in a consistent sequence. For simplicity and flow, I have left out or over simplified a lot of details, some of which we will cover later. But this will at least provide a general "feeling" for what is happening.

Following is some pseudo code that illustrates the basic code created for the servlet. (Note that I have shortened the reference names in order to make the code easier to read. For example the reference "_custTag" might actually be " _jspx_th_mpsjsp_customer_0" in a real servlet generated from a JSP. (The sample is derived from actual generated code.) The comments in bold describe what is happening in the code.

/* ----  mpsjsp:customer ---- */
// instantiate the tag handler
com.microps.jsptags.CustTag _custTag = new com.microps.jsptags.CustTag();
// call methods implemented by TagSupport class to set necessary properties 
_custTag.setPageContext(pageContext);
_custTag.setParent(null);
_custTag.setFieldVal("custID");
// begin evaluation of tag
try {
       // call doStartTag custom code and capture return value
        int _eval = _custTag.doStartTag();
        if (_eval == BodyTag.EVAL_BODY_TAG)
           // sorry there is no bodytag defined
            throw new JspTagException("Can't return BodyTag.EVAL_BODY_TAG");
        // evaluate the body if required
        if (_eval != Tag.SKIP_BODY) {
           do {
           // Here where code will go to handle a body tag, not present in our example
           // since we have this set for "empty"
           } while (false);
        }
       // check the endtag method for any cleanup or other custom code
        if (_custTag.doEndTag() == Tag.SKIP_PAGE)
          // something bad happened, cancel page evaluation
          return;
} finally {
        // clean up 
        _custTag.release();
}

Obviously, we do not have to be aware of the servlet code that is generated, but if you review this code you will have a better understanding of how the "magic" occurs. This will help you use the JSP tag API more efficiently.

Body Tag Interface

If we stopped here, we would have a lot of functionality, but that would not provide the same functions that a web designer would expect from a tag. In addition to having a tag "do something", there is the expectation that a tag would also need to "do something TO some input" in the tag body. Going back to the Font HTML tag introduced at the beginning, we expected the HTML tag to take the text in the body of the tag and change the style and size of that text.

To accomplish this, the Jsp Tag API provides an API for the body of the tag. This is called the "BodyTag" interface. This interface a number of additional methods:

import javax.servlet.jsp.tagext.*;

public interface BodyTag extends Tag {
     
     public final static int EVAL_BODY_TAG = 2;

     void setBodyContent(BodyContent b);
     void doInitBody() throws JspException;
     int doAfterBody() throws JspException;

}

Like the Tag interface the BodyTag interface has a helper class, SupportBodyTag, that implements all the basics and which allows the tag author to ignore implementing all or part of this API.

The Tag author has two methods that can be implemented; doInitBody and doAfterBody. The other method, setBodyContent, is called by the JSP runtime to handle the management issues and setup the required information.

doInitBody()

This method is called by the JSP runtime to handle any tasks the Tag author requires before the body of the tag is evaluated. At this point, the Tag author could initialize variables, setup connections or objects that will be used in evaluating the body of the tag.

doAfterBody()

This is the "meat and potatoes" of the BodyTag. You might wonder why it is called "doAfter"? This is because we can not evaluate the body until "after" it has been read by the JSP runtime. Therefore if we are interested in evaluating and modifying the body, it has to be done "after" the body has been processed. Like "pageContext" there is a default object available to the Tag author called "bodyContent". Through this object, the body can be cleared with "clearBody()" , get the content via "getString()", retrieve the reader or writer objects via "getReader()" and "getWriter()", and lastly acquire a reference to the parent writer (the JSPWriter) via "getEnclosingWriter()". Basically, all the tools needed to evaluate, manipulate, and output anything the Tag author requires to the tag's body. A simple example will help understand what is happening:

package com.microps.jsptags;

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

public class FormatBodyTag extends BodyTagSupport {
  public String _sCase = null;

  public FormatBodyTag() {
  }

  public int doAfterBody() {
      BodyContent _body = getBodyContent();
      String _sBody = null;
      if (_sCase.equals("upper") ) {
          _sBody = _body.getString().toUpperCase();
      } else if (_sCase.equals("lower")) {
          _sBody = _body.getString().toLowerCase();
      } else {
          _sBody = _body.getString();
      }
      try {
        JspWriter _out = _body.getEnclosingWriter();
        _out.print(_sBody);
      }
      catch (java.io.IOException ioex) {
        //handle error
      }
      return SKIP_BODY;
  }

  public void setCase(String arg_sVal) {
     _sCase = arg_sVal;
  }
}

Referring to the preceding example, note (in bold) that the body content is acquired using "getBodyContent()". The "body" object now has methods by which to manipulate the body. The body text is retrieved using "getString()". This example is basically trivial, but a very common usage would be to put a query request (SQL maybe) that could be evaluated. If need be, the evaluation or results can then be outputed in replacement of the previously existing body by using "getEnclosingWriter()" to get a handle on the main JSPWriter. Lastly, it is important to tell the JSP runtime to skip outputing the existing body. Why is it that we did not "evaluate" the body? We could have used the "clearBody" and then used getWriter to get the bodycontent writer to place our newly formatted text into the body, then called the JSP parser to evaluate the body. Basically, we took the short cut. But it is instructive to consider the options and why.

First, why the "enclosingWriter", and what is happening here? The JSP tag architects had a problem. Each part of a tag needed to buffer the contents, and then at the last minute, be able to either "include" or "exclude" those contents. Writing to a single output buffer would have made this very complicated, if not impossible. By splitting it up into multiple "out baskets" these output streams could be concatenated in any way at the last second, complete flexibility. In our example we made an end-run around this and acquired the "enclosing" output stream to write directly to that stream. Had we not done so, we would have had to take the previously outlined path of clearing the content, writing to this body content and then allowing it to be evaluated. There will be cases when this will be the better option. Hence you should be aware of both paths to the same effect.

Next the TLD that describes this tag and how it is to be used:

<?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"> 

<!-- a tag library descriptor--> 

<taglib>

   <tlibversion>1.0</tlibversion>
   <jspversion>1.1</jspversion> 

<shortname>BodyFormatter</shortname> 
   <uri>http://www.microps.com/JSPDev/formatbody_taglib</uri>

 <info>
 Formats the body of the tag to uppercase or lower case
 </info>
 <!-- Alters the case of the body of the tag --> 

<tag> 
   <name>caseformat</name> 
   <tagclass>com.microps.jsptags.FormatBodyTag</tagclass> 
   <bodycontent>JSP</bodycontent> 
   <info>
   </info> 
   <attribute> 
      <name>case</name> 
      <required>true</required> 
      <rtexprvalue>true</rtexprvalue> 
   </attribute> 
</tag> 

</taglib>

Most of the settings are the same as our previous example, but there are some notable differences. First the "bodycontent" is set for "JSP". This is because we will need to manipulate the body content. We could have used "tagdependent" but in general "JSP" covers all the bases: from ignoring it completely, to evaluating the body as JSP, HTML or any other content. In this case, we will be grabbing the content, altering the content and then making an end-run by outputting directly to the basic output as described previously. Another new wrinkle is the "rtexprvalue", which indicates to the JSP parser that the value that comes from this attribute "may" be generated at runtime. The default is "false", and therefore the parser vendor could "inline" the value from the original JSP page. Whereas, if the setting is "true", the parser vendor cannot, and must add the extra code to allow for this evaluation. For maximum flexibility, I tend to set this to "true" by default and then change this to "false" (or remove it) after careful evaluation of its use.

In the following JSP example, this new body tag will be combined with the our "customer" tag, so we can analyze the interaction between two tag types.

<HTML>
 <HEAD>
 <%@ taglib uri="http://www.microps.com/JSPDev/customer_taglib" prefix="mpscust"%> 
<%@ taglib uri="http://www.microps.com/JSPDev/formatbody_taglib" 
      prefix="mpsbody"%>
				<TITLE></TITLE>
 </HEAD>
 <BODY>
 <DIV ALIGN="CENTER">
 <H1>Customer Info</H1>
 <TABLE WIDTH="100%" BORDER="2" CELLPADDING="0" CELLSPACING="0">
    <TR>
       <TD>Customer ID:<mpscust:customer fieldVal="custID"/></TD></TR>
    <TR>
       <TD>Customer Name:
           <mpsbody:caseformat case="upper">
           <mpscust:customer fieldVal="lastName"/> 
           </mpsbody:caseformat></TD></TR>
    <TR>
       <TD>Address:
           <mpsbody:caseformat	case="upper">
           <mpscust:customer	fieldVal="address"/> 
           </mpsbody:caseformat></TD></TR>

  </TABLE></DIV>
 </BODY> 
</HTML>

At the beginning of the JSP file, the new "taglib" tag was added to provide a reference back to the web.xml file.

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> 

<web-app> 

<taglib>
				<taglib-uri>http://www.microps.com/JSPDev/customer_taglib</taglib-uri>
				<taglib-location>/WEB-INF/customer_taglib.tld</taglib-location> 
</taglib>
<taglib>
  <taglib-uri>http://www.microps.com/JSPDev/formatbody_taglib</taglib-uri> 
  <taglib-location>/WEB-INF/formatbody_taglib.tld</taglib-location> 
</taglib> 

</web-app>

This reference in the web.xml file provides the "taglib-location", which points the JSP parser to the correct tag library where the correct class can be instantiated so that it will be available for execution. The JSP compiler compiles the JSP file, and the following code (simplified for this discussion) is generated.

/* ----  mpsbody:caseformat ---- */
com.microps.jsptags.FormatBodyTag _mpsbody = new com.microps.jsptags.FormatBodyTag();
_mpsbody.setPageContext(pageContext);
_mpsbody.setParent(null);
_mpsbody.setCase("upper");
try {
   int _eval_mpsbody = _mpsbody.doStartTag();
   if (_eval_mpsbody == Tag.EVAL_BODY_INCLUDE)
      throw new JspTagException("Can't return Tag.EVAL_BODY_INCLUDE");
   if (_eval_mpsbody != Tag.SKIP_BODY) {
      try {
         if (_eval_mpsbody != Tag.EVAL_BODY_INCLUDE) {
            out = pageContext.pushBody();
            _mpsbody.setBodyContent((BodyContent) out);
         }
         _mpsbody.doInitBody();
         do {
            out.write("\r\n                                 ");
            /* ----  mpscust:customer ---- */
            com.microps.jsptags.CustTag _mpscust = new com.microps.jsptags.CustTag();
            _mpscust.setPageContext(pageContext);
            _mpscust.setParent(_mpsbody);
            _mpscust.setFieldVal("lastName");
            try {
               int _eval_mpscust = _mpscust.doStartTag();
               if (_eval_mpscust == BodyTag.EVAL_BODY_TAG)
                  throw new JspTagException("Can't return BodyTag.EVAL_BODY_TAG");
               if (_eval_mpscust != Tag.SKIP_BODY) {
                  do {
                     // Code could be here to repeatedly evaluate the body
                     // 
                  } while (false);
               }
               if (_mpscust.doEndTag() == Tag.SKIP_PAGE)
                  return;
            } finally {
               _mpscust.release();
            }
            out.write("\r\n                              ");
        } while (_mpsbody.doAfterBody() == BodyTag.EVAL_BODY_TAG);
      } finally {
         if (_eval_mpsbody != Tag.EVAL_BODY_INCLUDE)
            out = pageContext.popBody();
      }
   }
   if (_mpsbody.doEndTag() == Tag.SKIP_PAGE)
      return;
} finally {
   _mpsbody.release();
}

Notice how the code for the "customer" tag has been nested within the code for the "formatbody" tag. A key line before the "customer" tag begins is "out = pageContext.pushBody();". This means that a new "out" is available just for the bodycontent. The "customer" tag will now use this "out" instead of the original "enclosingWriter", to write the contents. After the "customer" tag is finished, then the "_mpsbody.doAfterBody()" is called to evaluate the "bodycontent", placed there by the "customer" tag. The doAfterBody then uses the JSPWriter to send the final formatting to the JSP output.

Tag LifeCyle - A Summary

As indicated previously, in order to really work with custom JSP tags, it is very helpful to fully understand how the different parts of the system are interacting. Much of this was introduced in the above discussion. Now we will bring it all together, one more time, in a single flowchart that illustrates our final example.



Database Iterating Tag Example

A common need is to have a table output on a page from a database. This example brings together all the concepts we have discussed thus far. First, we will review briefly all the components that we need to assemble. In JSP 1.2, there is also a new IterationTag interface, which will not be covered in this example.

The DatabaseBean

Our first step is to create a class that will handle all the database access. In real life we would make this very generic and flexible. For this example, it will be a bit more specific, but will illustrate the necessary direction of componentizing all aspects of development.

public class ProdGrpBean {

    Database g_dbProdgrp = null;
    QueryDataSet g_qdsProduct = null;

  public ProdGrpBean() { }

  public void loadData(String arg_sSql) throws  com.borland.dx.dataset.DataSetException {
    g_dbProdgrp = new Database();
    g_qdsProduct = new QueryDataSet();
    g_qdsProduct.setQuery(new com.borland.dx.sql.dataset.QueryDescriptor(g_dbProdgrp, arg_sSql, null, true, Load.ALL));
    g_dbProdgrp.setConnection(new com.borland.dx.sql.dataset.ConnectionDescriptor("jdbc:interbase://localhost/D:\\IBDATA\\SMILEYDB.GDB", "SYSDBA", "masterkey", false, "interbase.interclient.Driver"));
    g_qdsProduct.open();
 }

 public boolean isNextRecord() {
    return g_qdsProduct.inBounds();
 }

 public String getString(String arg_sField) throws com.borland.dx.dataset.DataSetException{
    return g_qdsProduct.getString(arg_sField);
 }

 public void next() {
    g_qdsProduct.next();
 }

 public void close() { g_qdsProduct.close(); }

}

For simplicity, a lot of error checking has been omitted. There are a methods for loading the data, closing the database, two navigation methods, and a single access method. In the following tag handler, we will see how these methods are applied.

The Tag Handler

The tag handler is the class that will be called by the JSP runtime to handle the tags logic. This class must extend one of the Tag interfaces or the implemented "support" classes. In our case, we will extend the BodyTagSupport class. The entire class is getting a bit too big so we will focus on the specific components.

public void doInitBody() throws JspException {

    g_request = (HttpServletRequest) this.pageContext.getRequest();
    g_session = g_request.getSession();

    //first retreive the service javabean if available
    g_prodGrp = (ProdGrpBean)g_session.getAttribute("prodgrp");
    if (g_prodGrp == null) {
      // if not available create a new one
      g_prodGrp = new ProdGrpBean();
      // load the product groups into memory
      g_prodGrp.loadData("Select * from PRODGRP");
    }

  }

The first step is the "doInitBody" method. Here we set the stage for references that will be needed in later methods. The database bean might need to be cached (which we are not doing) therefore, there is an example of how to retrieve the cached database bean from the session.

			 public int doStartTag() throws JspException {
 try {

    JspWriter _out = this.pageContext.getOut();
     if (g_sAction.equals("header")){
        System.out.println("doStart - Write to out"); 
        _out.println("<TR><TD ALIGN='CENTER' WIDTH='65%'>&nbsp;<B>Product Category</B></TD>"); 
       _out.println("<TD ALIGN='CENTER'VALIGN='MIDDLE' WIDTH='15%'><B>List</B></TD></TR>");
       _out.flush();
       return SKIP_BODY;
     } else if (g_sAction.equals("rows")) {
        return EVAL_BODY_TAG; 
     } else {
        _out.println("Wrong Attribute value");
     }
  } catch (java.io.IOException ioex) {
     //error handler here
  }
  // something went wrong.. shutdown..
  return SKIP_BODY;
}

The "doStartTag" is the next method that will be called by the JSP runtime. It has to evaluate the "g_sAction" variable that was loaded by the JSP runtime using the single "setter" method. (To be shown later) This variable determines whether the header or the rows will be the output. In the case of the rows, a EVAL_BODY_TAG is returned so that the next method , "doAfterBody", will be called.

public int doAfterBody() throws JspException {
  
   if (this.g_prodGrp.isNextRecord()) {
      try {
         JspWriter _out = getBodyContent().getEnclosingWriter();
         _out.println("<TR>"); 
         _out.println("<TD ALIGN='LEFT'WIDTH='85%'>"+
         g_prodGrp.getString("PROD_GRPDESCRIPT")+"</TD>"); 
         _out.println("<TD ALIGN='CENTER' WIDTH='15%'>"); 
         _out.println("<INPUT TYPE='BUTTON' NAME='"+
         g_prodGrp.getString("PROD_GRP")+"' VALUE='List Products'>");
         getBodyContent().write("ONMOUSEDOWN='parent.ContentFrame.ProdList_OnClick(\""+
         g_prodGrp.getString("PROD_GRP")+"\")'>");
         _out.println("</TD></TR>"); 
         g_prodGrp.next();
      } catch (java.io.IOException ioex){
         // error handler here 
      }
      return EVAL_BODY_TAG; 
   } else {
      return SKIP_BODY;
   }
}

The "doAfterBody" is the generator of the table. As long as the database bean method "isNextRecord" returns true it writes a row and returns EVAL_BODY_TAG. This causes the "doAfterBody" to be executed repeatedly, until it returns SKIP_BODY, thus providing the fundamental "iteration" required.

public int doEndTag()throws JspException {
   if (g_sAction.equals("rows")) {
      this.g_prodGrp.close();
   }
   return super.doEndTag();
}

  /**
   *  Attribute Setters
   */

public void setAction(String arg_sAction) {
   g_sAction = arg_sAction;
}

Lastly, cleanup is required, therefore the "doEndTag" calls the database beans "close" method to insure that the database, and any other loose ends, are cleaned up. Also shown is the single "setter" method used by the JSP runtime, to load our "action" attribute found in the tag.

The Tag Descriptor

As before, a contract needs to be established between the tag handler and the JSP compiler. This is accomplished via the TLD file.

<tag>
   <name>prodgroups</name>
   <tagclass>com.microps.jsptags.ProductGroupTag</tagclass>
   <bodycontent>JSP</bodycontent>
   <info>
   </info>
   <attribute>
      <name>action</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
</tag>

In this case the bodycontent is set for JSP because we might want to place the SQL statement in the body at a later time, therefore eliminating the hardcoded query in the tag handler. The action attribute is required and the value might be created dynamically by our JSP page.

Wrap Up

Obviously there is a lot more that can be done with the previous example, but time and paper is short so hopefully this material has help get you started with custom Tag development in JBuilder. Custom JSP tags clearly bring the JSP technology up to the level of code/presentation separation we desire. Frameworks like "Struts" take this technology and illustrate what can be done on a more sophisticated scale. Check out the links and books listed below. They will take you to the next level in truly utilizing the "power of Tags".

Enjoy.

Links

JDance - Tag Lib News and Articles - http://www.jdance.com/jsptags.shtm - This link provides a lot of good sub-links with great articles and sample code.

The Struts Framework. The goal of this project is to provide an open source framework useful in building web applications with Java Servlet and JavaServer Pages (JSP) technology. Struts encourages application architectures based on the Model-View-Controller (MVC) design paradigm, colloquially known as Model 2 in discussions on various servlet and JSP related mailing lists.

http://jakarta.apache.org/struts/

Books

JSP Tag Libraries, Shachor, Chace, & Rydin, Manning Publishing, ISBN 1-930110-09-X, The best "all Tag" book around.

More Servlets and JavaServer Pages, Marty Hall, Sun Microsystems Press, ISBN 0-13-067614-4. This is a just released book that covers material that most other Servlet and JSP pages do not cover in much detail. I consider it a "must have" book.

Core Servlets and JavaServer pages, Marty Hall, ISBN 0-13-089340-4 Very good "basic" reference book with useful examples. (Some books have such trivial examples)

JavaServer Pages, Larne Pekowsky, ISBN 0201704218, If you want to "read a book from beginning to end" on JSP, this would be the one. Simple, clear and well organized. A good place to start.

Professional JSP, Karl Avedal, et. al., ISBN 1-861003-62-5, Some great code examples, the best I have found. More of a reference, though some chapters are worth reading if that is a area in which you are focusing.