JSP Classic Tag Handler Design

JSP Classic tags

  • All standard JSP tags are handled by tag libraries.
  • JSP provides the capability for you to encapsulate reusable functionality into your own custom tags.
  • These custom tags will get their behavior from a Java class known as a tag handler.
  • In this section we will look at the classic tag handler, which is more complex then the simple tag handler found in the previous section.
  • For a class to bee a classic tag handler we must implement the javax.servlet.jsp.tagext.Tag interface, one of its subinterfaces or override classes that has an default implementation (javax.servlet.jsp.tagext.TagSupport or javax.servlet.jsp.tagext.BodyTagSupport).
    Here are the relationships between the individual interface and the classes:
  • The tag handler must also follow the required rules of a Javabean with a no-argument constructor and include private members with setXXX and getXXX methods for the tag attributes.
  • When using classic tags, there are two methods you can implement: doStartTag() and doEndTag().
  • In most cases, we use the TagSupport class and overwrite the one or both of the methods as mentioned above.

The TagSupport class

  • The TagSupport class has the following methods which can be overwritten:
    Method Description
    int doAfterBody() Has a default processing for a body.
    int doEndTag() The default processing of the end tag returns EVAL_PAGE.
    int doStartTag() The default processing of the start tag, returns SKIP_BODY.
    Tag findAncestorWithClass(Tag from, Class klass) Find the instance of a given class type that is closest to a given instance.
    String getId() Returns the value of the id attribute of this tag; or null.
    Tag getParent() The Tag instance most closely enclosing this tag instance.
    Object getValue(String k) Get a the value associated with a key.
    Enumeration getValues() Enumerate the keys for the values kept by this tag handler.
    void release() Release state.
    void removeValue(String k) Remove a value associated with a key.
    void setId(String id) Set the id attribute for this tag.
    void setPageContext(PageContext pageContext) Set the page context in the variable pageContext.
    void setParent(Tag t) Set the nesting tag of this tag.
    void setValue(String k, Object o) Associate a value with a String key.
  • When the tag appears in a JSP file, the translator(container) creates code that:
    1. Creates an instance of the tag handler.
    2. Calls first the setPageContext(PageContext) method and then the setParent(Tag) method.
    3. Initializes the tag handler attributes.
    4. The doStartTag() method is called by the container.
    5. The doAfterBody() method is called by the container if doStartTag() returns EVAL_BODY_INCLUDE and not SKIP_BODY. If doAfterBody() returns EVAL_BODY_AGAIN the doAfterBody() will excute again until it returns SKIP_BODY.
    6. The doEndTag() method is called by the container if doStartTag() or doAfterBody() returns SKIP_BODY. The doEndTag() method can return EVAL_PAGE to evaluate the rest of the page or SKIP_PAGE to not do so. In reality, the SKIP_PAGE return value is rarely used, because there aren’t many circumstances in which you’ll want to actually stop the rest of the page from being processed.
    Example using doEndTag() method:
    public class HelloTag extends TagSupport {
      public int doEndTag() throws JspException {
        System.out.println("doEndTag()");
        String body="Hello world";
        try {
          pageContext.getOut().write("<b>"+body+"</b>");
        } catch (IOException ex) {
          Logger.getLogger(ListQuestions.class.getName()).log(Level.SEVERE, null, ex);
        }
        return EVAL_PAGE;
      }
    }
  • If you like that the tag should contain a body use the BodyTagSupport class and overwrite the one or both of the methods as mentioned above.

The BodyTagSupport class

  • The BodyTagSupport class is an extention of the TagSupport class and has some redefined methods and some extra methods to handle the body of the tag you define.
    The BodyTagSupport class the following methods which can be overwritten:
    Method Description
    int doAfterBody() After the body evaluation: do not reevaluate and continue with the page.
    int doEndTag() Default processing of the end tag returning EVAL_PAGE.
    void doInitBody() Prepare for evaluation of the body just before the first body evaluation: no action.
    int doStartTag() Default processing of the start tag returning EVAL_BODY_BUFFERED.
    BodyContent getBodyContent() Get current bodyContent.
    JspWriter getPreviousOut() Get surrounding out JspWriter.
    void release() Release state.
    void setBodyContent(BodyContent b) Prepare for evaluation of the body: stash the bodyContent away.
  • When the tag appears in a JSP file, the translator(container) creates code that:
    1. Creates an instance of the tag handler.
    2. Calls first the setPageContext(PageContext) method and then the setParent(Tag) method.
    3. Initializes the tag handler attributes.
    4. The doStartTag() method is called by the container which returns default EVAL_BODY_BUFFERED.
    5. The setBodyContent() is called on the tag handler so that the tag can hold on to the BodyContent reference and use it later. This method is called if doStartTag() returns EVAL_BODY_BUFFERED.
    6. The doInitBody() is called and can be used to set up any state before the body content is eventually evaluated. This method is called if doStartTag() returns EVAL_BODY_BUFFERED.
    7. The doAfterBody() method is called by the container if doStartTag() returns EVAL_BODY_INCLUDE and not SKIP_BODY or EVAL_BODY_BUFFERED. If doAfterBody() returns EVAL_BODY_AGAIN the doAfterBody() will excute again until it returns SKIP_BODY.
    8. The doEndTag() method is called by the container if doStartTag() or doAfterBody() returns SKIP_BODY. The doEndTag() method can return EVAL_PAGE to evaluate the rest of the page or SKIP_PAGE to not do so. In reality, the SKIP_PAGE return value is rarely used, because there aren’t many circumstances in which you’ll want to actually stop the rest of the page from being processed.
    Example using doEndTag() method:
    public class HelloTag extends BodyTagSupport {
      public int doEndTag() throws JspException {
        System.out.println("doEndTag()");
        String body="Hello world";
        try {
          pageContext.getOut().write("<b>"+body+"</b>");
        } catch (IOException ex) {
          Logger.getLogger(ListQuestions.class.getName()).log(Level.SEVERE, null, ex);
        }
        return EVAL_PAGE;
      }
    }

    All tags must be defined in a Tag Library Descriptor File (a XML type file) that can be placed under the WEB-INF/tlds directory.

Tag Library Descriptor File

  • A tag library descriptor is an XML document that contains information about a library as a whole and about each tag contained in the library.
  • TLDs are used by a web container to validate the tags and by JSP page development tools.
  • Tag library descriptor file names must have the extension .tld.
    The root tag is taglib and must be defined in this way:
    <?xml version="1.0" encoding="UTF-8"?>
    <taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
    
    (Here comes the taglib sub-element described below)
    
    </taglib>
    taglib sub-element(s):
    Element Description
    description (optional) A string describing the use of the tag library.
    display-name (optional) Name intended to be displayed by tools.
    icon (optional) Icon that can be used by tools.
    tlib-version The tag library’s version.
    short-name (optional) Name that could be used by a JSP page-authoring tool to create names with a mnemonic value.
    uri A URI that uniquely identifies the tag library.
    validator Defines an optional tag library validator that can be used to validate the conformance of any JSP page importing this tag library to its requirements.
    validator sub-element:
    Element Description
    validator-class The class implementing javax.servlet.jsp.tagext.TagLibraryValidator.
    init-param (optional) Initialization parameters.
    listener (optional) A tag library can specify some classes that are event listeners.
    listener sub-element:
    Element Description
    listener-class Must contain the fully qualified name of the listener class.
    tag-file | tag Declares the tag files or tags defined in the tag library. As we want to write tag handlers in Java, each tag in the library must be declared in the TLD with a tag element.
    tag sub-element:
    Element Description
    description (optional) A description of the tag.
    display-name (optional) name intended to be displayed by tools.
    icon (optional) Icon that can be used by tools.
    name The unique tag name.
    tag-class The fully qualified name of the tag handler class.
    tei-class (optional) Subclass of javax.servlet.jsp.tagext.TagExtraInfo. See Declaring Tag Variables for Tag Handlers.
    body-content The body content type.
    • tagdependent: The body of the tag is interpreted by the tag implementation itself, and is most likely in a different language, for example, embedded SQL statements.
    • empty: The body must be empty.
    • scriptless: The body accepts only static text, EL expressions, and custom tags. No scripting elements are allowed.
    variable (optional) Declares an EL variable exposed by the tag to the calling page.
    attribute Declares an attribute of the custom tag.
    attribute sub-element:
    Element Description
    description (optional) A description of the attribute.
    name The unique name of the attribute being declared. A translation error results if more than one attribute element appears in the same tag with the same name
    required (optional) Whether the attribute is required. The default is false.
    rtexprvalue (optional) Whether the attribute’s value can be dynamically calculated at runtime by an EL expression. The default is false. When this element is set to true and the attribute definition also includes either a deferred-value or deferred-method element then the attribute accepts both dynamic and deferred expressions.
    type (optional) The runtime type of the attribute’s value. Defaults to java.lang.String if not specified.
    fragment (optional) Whether this attribute is a fragment to be evaluated by the tag handler (true) or a normal attribute to be evaluated by the container before being passed to the tag handler.
    If this attribute is true:
    • You do not specify the rtexprvalue attribute. The container fixes the rtexprvalue attribute at true.
    • You do not specify the type attribute. The container fixes the type attribute at javax.servlet.jsp.tagext.JspFragment.
    • Defaults to false.
    deferred-value (optional) A description of the attribute.
    deferred-method (optional) A description of the attribute.
    dynamic-attributes Whether the tag supports additional attributes with dynamic names. Defaults to false. If true, the tag handler class must implement the javax.servlet.jsp.tagext.DynamicAttributes interface.
    example (optional) Indicates that the tag attribute accepts deferred value expressions. This element includes an optional type child element, which indicates the type of object to which the expression resolves. If no type element is included, the type is java.lang.Object. Either the deferred-value or deferred-method element (but not both) can be defined for the same attribute.
    tag-extension (optional) Indicates that the tag attribute accepts deferred method expressions. This element includes an optional method-signature child element, which indicates the signature of the method that the expression invokes. If no method signature is defined, the method signature default is void methodName(). Either the deferred-value or deferred-method element (but not both) can be defined for the same attribute.
    function (optional) Zero or more EL functions (static methods) defined in the tag library.
    tag-extension (optional) Extensions that provide extra information about the tag library for tools.

Example of using JSP Simple tags.

In the example we use Netbeans IDE and Glassfish Server.

You can download this example here (needed tools can be found in the right menu on this page).

If you like to participate in the review of this example you must first create a Web project in Netbeans (the project name is CustomClassicTag).

In this example, we will add two files, header.jsp and footer.jsp.

  • It is customary to place all include files, which are used several times in the WEB-INF folder. Some would also like the include files should have the extension .jspf, but this is not a requirement.
    Here is the file we want to include at the top of all pages:
    <h2 class="siteblue">Metrics Converter</h2>
    <hr>

    For those who participate in the review: create a JSP file in Netbeans and replace generated code for the JSP with that shown above (the JSP file name is Header.jsp and folder should be WEB-INF).

    Here is the file we want to include at the bottom of all pages:
    <hr >
    <span class="siteblue">Date: <%= (new java.util.Date()).toString() %></span>

    For those who participate in the review: create a JSP file in Netbeans and replace generated code for the JSP with that shown above (the JSP file name is Footer.jsp and folder should be WEB-INF).

In this example, we will add three java files; MetricConverter.java and the taghandlers, ListTypes.java and ConvertResult.java.

  • We need a java class, MetricConverter.java, to handle metrics conversion.
    Here is the MetricConverter.java:
    package app;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class MetricConverter {
      private Map toTypes= new HashMap();
      private int numTypes;
      public MetricConverter() {
        toTypes.put("Inches", 0.3937007874);
        toTypes.put("Feet", 0.03280839895);
        toTypes.put("Yards", 0.01093613298);
        toTypes.put("Miles", 0.000006213711922);
        toTypes.put("Nautical Miles", 0.000005399568035);
        toTypes.put("Centimeters", 1.0);
        toTypes.put("Meters", 0.01);
        toTypes.put("Kilometers", 0.00001);
        setNumTypes(toTypes.size());        
      }
      public Set getTypes(){
        return toTypes.keySet();
      }
      public Map getToTypes() {
        return toTypes;
      }
    
      public void setToTypes(Map toTypes) {
        this.toTypes = toTypes;
      }
    
      public int getNumTypes() {
        return numTypes;
      }
    
      public void setNumTypes(int numTypes) {
        this.numTypes = numTypes;
      }
      public double getConverted(String from, String to, double value){
        Double fromValue=(Double) toTypes.get(from);
        double inCM=value/fromValue;
        Double toValue=(Double) toTypes.get(to);
        double converted=toValue*inCM;
        return converted;
      }
      
    }

    For those who participate in the review: create a java class in Netbeans and replace generated code for the java file with that shown above (the java file name is MetricConverter and package should be app).

  • We need a Taghandler Java class, ListTypes.java, that handles a tag, convertTypes, that lists all converting types as options in a html select tag.
    Here is the ListTypes.java:
    package app;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.JspTagException;
    import javax.servlet.jsp.tagext.TagSupport;
    
    public class ListTypes extends TagSupport {
    
      private List<String> convertTypes;
      private MetricConverter metricConverter;
      private String selected;
    
      public ListTypes() {
        metricConverter = new MetricConverter();
        convertTypes = new ArrayList<>(metricConverter.getTypes());
      }
    
      public String getSelected() {
        return selected;
      }
    
      public void setSelected(String selected) {
        this.selected = selected;
      }
    
      @Override
      public int doEndTag() throws JspException {
        for (String type : convertTypes) {
          String str = "<option ";
          if (selected.equals(type)) {
            str += "selected='selected' value='" + type + "'>" + type + "</option>\n";
          } else {
            str += "value='" + type + "'>" + type + "</option>\n";
          }
          try {
            pageContext.getOut().write(str);
          } catch (IOException e) {
            throw new JspTagException("Error writing to out");
          }
        }
        return super.doEndTag();
      }
    
    }

    For those who participate in the review: create a java class in Netbeans and replace generated code for the java file with that shown above (the java file name is ListTypes and package should be app).

  • We let this class be an extension of the TagSupport class.
  • In the constructor of the class we creates all possible metric types using the MetricConverter class.
  • We overwrite the doEndTag() method to produce an option list for the html select tag.
  • To control which option that is selected we also need an accessor, selected, which is set by the JSP through the tag, convertTypes, attribute.
  • The last Java class, ConvertResult.java, is a taghandler for a tag, convertResult, that produce the result of a metric conversion.
    Here is the ConvertResult.java:
    package app;
    
    import java.io.IOException;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.BodyTagSupport;
    
    public class ConvertResult extends BodyTagSupport{
      private String from;
      private String to;
      private double value;
      private MetricConverter metricConverter;
    
      public ConvertResult() {
        metricConverter = new MetricConverter();
      }
    
      public String getFrom() {
        return from;
      }
    
      public void setFrom(String from) {
        this.from = from;
      }
    
      public String getTo() {
        return to;
      }
    
      public void setTo(String to) {
        this.to = to;
      }
    
      public double getValue() {
        return value;
      }
    
      public void setValue(double value) {
        this.value = value;
      }
    
      @Override
      public int doEndTag() throws JspException {
        Double result=metricConverter.getConverted(from, to, value);
        try {
          pageContext.getOut().write(result.toString());
        } catch (IOException ex) {
          Logger.getLogger(ConvertResult.class.getName()).log(Level.SEVERE, null, ex);
        }
        return super.doEndTag(); 
      }
      
    }

    For those who participate in the review: create a java class in Netbeans and replace generated code for the java file with that shown above (the java file name is ConvertResult and package should be app).

  • We let this class be an extension of the BodyTagSupport class.
  • In the constructor of the class we creates an instance of the MetricConverter class as we need it to do the metric conversion as an helper class.
  • We overwrite the doEndTag() method to produce the result of a metric conversion.
  • Further we need accessors for a "from metric type", a "to metric type" and "the value" to convert. These must be attributes to the tag, convertResult. JSP will then set these accessors before the doEndTag() method is executed.

Then we need to write the Tag Library Descriptor File.

  • With the tag handler written and compiled, the next step is to write the TLD file (SearchTag.tld), which describes the two tags, how these will be used on the page, the type of body content, whether the tag accepts any attributes, and so on.
    Here is the Metric.tld file:
    <?xml version="1.0" encoding="UTF-8"?>
    <taglib version="2.0" 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">
      <tlib-version>1.0</tlib-version>
      <short-name>con</short-name>
      <uri>/conversions</uri>
      <tag>
        <name>convertTypes</name>
        <tag-class>app.ListTypes</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
          <name>selected</name>
          <required>yes</required>
          <rtexprvalue>true</rtexprvalue>
        </attribute>
       </tag>
      <tag>
        <name>convertResult</name>
        <tag-class>app.ConvertResult</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
          <name>from</name>
          <required>yes</required>
          <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
          <name>to</name>
          <required>yes</required>
          <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
          <name>value</name>
          <required>yes</required>
          <rtexprvalue>true</rtexprvalue>
        </attribute>
      </tag>
    </taglib>

    For those who participate in the review: create a tld file in Netbeans and replace generated code for the tld file with that shown above.

  • The tag convertTypes has one attribute (selected) and is handled by the class app.ListTypes.java
  • The tag convertResult has three attributes (from, to and value) and is handled by the class app.ConvertResult.java

To demonstrate how to use the these new tags we need a jsp file, Converter.jsp .

  • The Converter.jsp file should look like this:
    <!DOCTYPE html >
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page language="java" contentType="text/html; charset=UTF-8"%>
    <%@ taglib uri="/conversions" prefix="con"%>
    <html>
      <head><title>Metric Converter</title>
        <style>
          .gradientdown {
            background:  #e0e6ff;
            background: -webkit-linear-gradient(top, #e0e6ff 0%,#eeeeee 100%); /* Chrome10+,Safari5.1+ */
            background: -o-linear-gradient(#e0e6ff, #eeeeee); /* For Opera 11.1 to 12.0 */
            background: -moz-linear-gradient(top, #e0e6ff, #eeeeee); /* For Firefox 3.6 to 15 */
            background: linear-gradient(top, #e0e6ff 0%,#eeeeee 100%); /* W3C Standard syntax (must be last) */
          }
        </style>
      </head>
      <c:set var="from" scope="request" value="Centimeters"/>
      <c:set var="to" scope="request" value="Inches"/>
      <c:set var="value" scope="request" value="1"/>
      <c:if test="${param.submit == 'convert'}">
        <c:set var="from" scope="request" value="${param.from}"/>
        <c:set var="to" scope="request" value="${param.to}"/>
        <c:set var="value" scope="request" value="${param.value}"/>
      </c:if>
      <body>
        <div style="max-width:300px; ">
          <%@ include file="/WEB-INF/header.jsp" %>
          <form  method="POST" >
            <div style="margin: 4px;">
              <table >
                <tr class="gradientdown">
                  <th style="width:105px;">From type</th> 
                  <th style="width:105px;">To type</th>
                </tr>
                <tr >
                  <td>
                    <select name="from" style="width:110px; background-color: beige;">
                      <con:convertTypes selected="${from}" >
                      </con:convertTypes>
                      </select>
                    </td>
                    <td>
                      <select name="to" style="width:110px; background-color: beige;">
                      <con:convertTypes selected="${to}" >
                      </con:convertTypes>
                      </select>
                    </td>
                  </tr>
                </table>            
              </div>
              <div style="margin: 4px;">
                <table >
                  <tr class="gradientdown">
                    <th style="width:105px;">From value</th> 
                    <th style="width:105px;">To value</th>
                  </tr>
                  <tr >
                    <td >
                      <input name="value" type="text" 
                             style="width:105px; background-color: beige; text-align: right;" 
                             value="${value}"/>
                  </td>
                  <td style="text-align: right;">
                    <con:convertResult from="${from}" to="${to}" value="${value}">
                    </con:convertResult>
                    </td>
                  </tr>
                </table>            
              </div>          
              <div style="margin: 4px; text-align: center;">
                <input name="submit" type="submit" value="convert"/>
              </div>
            </form>  
          <%@ include file="/WEB-INF/footer.jsp" %>
        </div>
      </body>
    </html>

    For those who participate in the review: create a JSP file in Netbeans and replace generated code for the JSP with that shown above (the JSP file name is Converter).

Creating Deployment descriptor.

  • To run this JSP you have to deploy it to a web-server or a Application server. To deploy means to install the JSP with some instruction to a such server.
  • The instructions are mainly defined to be deployment descriptors. The standard part of the deployment descriptor should be in an XML-file with the name web.xml.

    You may need to create a Deployment descriptor file, web.xml in Netbeans.

  • The contents of the web.xml file should look like this:
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
     http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
      <servlet>
        <servlet-name>Converter</servlet-name>
        <jsp-file>/Converter.jsp</jsp-file>
      </servlet>
      <welcome-file-list>
        <welcome-file>Converter.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
  • This file starts with the normal xml tag for a XML file and the root tag for the deployment descriptor is web-app. Every ting inside the last tag is to tell the server about our application, which in this case is a JSP file.
  • The welcome-file tag specifies the startup for our application, which in this case and our application is the welcome file Converter.jsp. Reorganize the welcome-file-list to what is shown above.

Creating Web-server Deployment descriptor.

  • The context-root (in example /CustomClassicTag) for the application will in most cases be specified by a server vendor deployment descriptor.

    For those who participate in the review: create a Glassfish deployment descriptor in Netbeans.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, 
    Inc.//DTD GlassFish Application Server 3.0 Servlet 3.0//EN" 
    "http://www.sun.com/software/appserver/dtds/sun-web-app_3_0-0.dtd">
    <sun-web-app error-url="">
      <context-root>/CustomClassicTag</context-root>
      <class-loader delegate="true"/>
      <jsp-config>
        <property name="keepgenerated" value="true">
          <description>
            Keep a copy of the generated servlet class' java code.
          </description>
        </property>
      </jsp-config>
    </sun-web-app>

Run the application.

  • For those who participate in the review: right click the Web-project and select Run.
  • This results in that the application will be deployed to the server and started.
  • The browser will display:
© 2010 by Finnesand Data. All rights reserved.
This site aims to provide FREE programming training and technics.
Finnesand Data as site owner gives no warranty for the correctness in the pages or source codes.
The risk of using this web-site pages or any program codes from this website is entirely at the individual user.