Custom Tag Development

If there is not a tag in the current five libraries that meet your needs, then you can write your own custom tag handler. There are three different ways to build your own tag handlers

Tag Files

Tag Files can invoke reusable content using a custom tag instead of the generic <jsp:include> or <c:import>. Tag Files are a kind of "tag handler lite" because they let page developers create custom tags without having to write a complicated Java tag handler class, they are just glorified includes.

There are 3 steps to take in order to use a File tag

Step One

# create the Tag File, take an include file "Header.jsp" and rename to "Header.tag"
# Remember there should are no <html><body> elements

<img src="images/companyLogo.gif" > <br>

Step Two Put the tag file "Header.tag" in a directory named "tags" inside your applications WEB-INF directory
Step Three # Put a taglib directive (with a tagdir attribute) in the JSP and invoke the tag

<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>

<html><body>

<myTags:Header/>

</body></html>

You invoke a Tag File with a tag, and the tags can have attributes, these attributes get sent to the Tag File and and we have to add a new attribute directive

tag attributes

# In the Tag File
<%@ attribute name="subTitle" required="true" rtexprvalue="true" %>

<strong>${subTitle}</strong>

# In the JSP File
<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>
<html><body>
<myTags:Header subTitle="This a Tag Attribute" />
<br>
<%-- out of scope will not work-->
${subTitle}
</html></body>

Note: that all tag attributes do have TAG scope, once the tag is closed, the tag attributes go out of scope

The attributes are defined in the TLD, so how does the web developer know what attributes are available to him/her, well there is a new type of directive and its just for Tag Files, you saw one in step one above, this attribute knows how to match the TLD attribute directives. If the attribute directive is missing from the file you will get an error if the attribute is required and you are not supplying it.

attribute directive <%@ attribute name="subTitle" required="true" rtexprvalue="true" %>

Sometimes the value we are passing my be very large (paragraph size), we can use the <jsp:doBody/> to pass this information to the Tag File, but again without a TLD we need to declare the body-type content, and we use another new tag directive and its a bit like a page directive in JSP, however you cannot use scripting inside a <body-content> element. The tagdependent value means the body-content will be treated like plain text which means no EL, tags and scripts, the only other values are empty or scriptless (the default).

passing large values

# In the Tag File
<%@ attribute name="subTitle" required="true" rtexprvalue="true" %>
<%@ tag body-content="tagdependent" %>

<h1>${subTitle}</h1>
<strong><jsp:doBody/></strong>
<br>
<img src="images/companyLogo.gif" >
<br>

# In the JSP File
<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>
<html><body>
<myTags:Header subTitle="This a Tag Attribute">
   This is a large piece of text that needs to be passed to the tag File
   this can span many lines
</myTags:Header>
</html></body>

The container will look for tags in a number of places

  • directory inside WEB-IN/tags
  • inside a sub-directory of WEB-INF/tags
  • inside the META-INF/tags directory inside a JAR file thats inside WEB-INF/lib
  • Inside a sub-directory of META-INF/tags inside a JAR file thats inside WEB-INF/lib
  • if the tag file is deployed in a JAR, there must be a TL for the the tag file

Simple Tags

When you need Java, you need a custom tag handler, a handler is simply a Java class that does the work of the tag. The tag handler has access to tag attributes, the tag body and even the page context so it can get scoped attributes and request and response. Custom tag handlers come in two favors simple and classic (will discuss these later). Simple handlers with tag files should be all you will ever need but the classic option is there just in case that by any chance if simple and the tag files don't give you what you need, bear in mind there still be a lot of classic code out there in the field.

There are 3 steps to creating a simple tag handler

Step One

# Write a class that extends SimpleTagSupport
package foo;

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

public class SimpleTagTest1 extends SimpleTagSupport {
   
  public void doTag() throws JspException, IOException {

    PageContext pageContext = (PageContext) getJspContext();
    JspWriter out = pageContext.getOut();

    try {
      out.println("This is a simple tag test");
    } catch (Exception ex) {
       // do nothing
    }
   
    // You can also use the below statement to archive the above
    //getJspContext().getOut().print("This is a simple tag test");
  }
}

Note: use the below to compile and make sure the package/file is in the classes dirctory

javac -cp d:\tomcat6\lib\jasper-api.jar;d:\tomcat6\lib\servlet-api.jar;d:\tomcat6\lib\jsp-api.jar -d classes src\SimpleTagTest1.java

Step Three # Create a TLD for the tag (WEB-INF/tlds/simpleTags.tld)
<taglib ...>
  <tlib-version>1.2</tlib-version>
  <short-name>simpleTags</short-name>
  <uri>simpleTags</uri>

  <tag>
    <description>simple custom tag</description>
    <name>simple1</name>
    <tag-class>foo.SimpleTagTest1</tag-class>
    <body-content>empty</body-content>
  </tag>
</taglib>
Step Four # Write the JSP that uses the tag
<%@ taglib prefix="myTags" uri="simpleTags" %>
<html><body>
<myTags:simple1>
</html></body>

A simple tag with a body, I have only highlighted the bits that need to change

JSP that uses the tag <myTags:simple2>
  This is the body message
</myTags:simple2>
tag handler class

# Change the above java to reflect the below and recompile
 try {
      getJspBody().invoke(null);
      out.println("<BR>This is a simple tag test");
    } catch (Exception ex) {
       // do nothing
    }

Note: this states process the body of the tag and print it to the response, the null means the output goes to the response rather than some other writer you pass in

javac -cp d:\tomcat6\lib\jasper-api.jar;d:\tomcat6\lib\servlet-api.jar;d:\tomcat6\lib\jsp-api.jar -d classes src\SimpleTagTest2.java

TL for the tag <tag>
    <description>simple cutom tag</description>
    <name>simple2</name>
    <tag-class>foo.SimpleTagTest2</tag-class>
    <body-content>scriptless</body-content>
</tag>

Note: scriptless means the tag can have a body but the body cannot have scripting (no scriptlets, scripting expressions or declarations)

The life of a simple tag handler is below, a JSP invokes a tag, a new instance of the tag handler class is instantiated, two or more methods are called on the handler and when the doTag() methods completes, the handler objects goes away, in other words words they are not reused.

Here are some more examples

a tag with dynamic row data

# JSP Tag
<table>
  <myTags:simple4>
    <tr><td>${movie}</td></tr>
  </myTags:simple4>
</table>

# The tag handler doTag() method
String[] movie = {"Rambo", "Midnight Express", "Platoon"};

public void doTag() throws JspException, IOexception {
  for ( int i = 0; i < movies.length; i++) {
    pageContext).setAttribute("movie", movies[i]);
    getJspBody().invoke(null);
  }

}

a simple tag with an attribute

# JSP Tag
<table>
  <myTags:simple5 movieList="{$movieCollection}">
    <tr><td>${movie.name}</td></tr>
    <tr><td>${movie.genre}</td></tr>
  </myTags:simple5>
</table>

# The tag handler doTag() method
public class SimpleTagTest5 extends SimpleTagSupport {

  private List movieList;

// A bean stype set method, it works out that the setter method should be remember   
  public void setMovieList(List movieList) {
    this.movieList = movieList;
  }

  public void doTag() throws JspException, IOexception {
    Iterator i = movieList.iterator();
    while(i.hastNext()) {
      Movie movie = (Movie) i.next();
      getJspContext().setAttribute("movie", movie);
      getJspBody().invoke(null);
    }
  }
}

# The TLD for the tag
<tag>
    <description>simple cutom tag</description>
    <name>simple5</name>
    <tag-class>foo.SimpleTagTest5</tag-class>
    <body-content>scriptless</body-content>
    <attribute>
      <name>movieList</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>

Sometimes you start creating a page then an exception is thrown by a Tag, you have part of the page created and you want to use this part to still appear as the response but you don't want the response to include anything still left to be processed after the tag throws an exception, this is why SkipPageException exists, it will show everything up until the exception. If the page that onvokes the tag was included from some other page, only the page that invokes the tag stops processing, the original page that did the include keeps going after the SkipPageException

SkipPageException

# tag handler
public void doTag() throws JspException, IOException {
  getJspContext.getOut().print("Message from within doTag().<br>");
  getJspContext.getOut().print("About to throw a SkipPageException");
  if (thingsDoNotWork) {
    throw new SkipPageException();
  }
}

# JSP that invokes the tag
<%@ taglib prefix="myTags" uri="simpleTags" %>
<html><body>
<myTags:simple6>
</html></body>

Classic Tag

The tag handler API is below, everything in the grey box is from the Classic tag model for custom tag handlers. The tag handler API has five interfaces and three supporting classes, there is no reason to implement the interfaces directly, so you will probably always extend a support class. One thing you should know about classic tags is that they can be pooled and reused by the Container so watch out on instance variables they need to be reset if you desire, otherwise they will have the previous values in them.

Now for a simple example of a classic tag, look at the comments to get an idea on what's going on.

Simple classic tag

# JSP Tag
<%@ taglib prefix="myTags" uri="classicTags" %>
<html><body>
<myTags:classic1/>
</html></body>

# The TLD for the tag
<tag>
  <description>classic custom tag</description>
  <name>classic1</name>
  <tag-class>foo.ClassicTagTest1</tag-class>
   <body-content>empty</body-content>
</tag>

# The tag handler
package foo;

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

// By extending TagSupport we are implementing both Tag and Iteration Tag
public class Classic1 extends TagSupport {

  // overriding doStartTag and only need to throw JspException
  public int doStartTag() throws JspException {

    // Here we use a try/catch because we can't declare the IOException
    try {
      pageContext.getOut().println("Classic tag - startTag "):
    } catch (Exception ex) {
      throw new JspException("Exception- " + ex.toString());
    }
  // This means do not evaluate the body if there is one, just go straight to the doEndTag()
  // the other option to this is EVAL_BODY_INCLUDE   
  return SKIP_BODY;
  }

  // The doEndTag() method is called after the body has been evaluated
  public int doEndTag() throws JspException {
    try {
      pageContext.getOut().println("in doEndTag() "):
    } catch (Exception ex) {
      throw new JspException("Exception- " + ex.toString());
    }
  // Means evaluate the rest of the page   
  return EVAL_PAGE;
  }
}

The classic tag lifecycle is below, it calls doStartTag() then doAfterBodyTag() and finally the doEndTag()

There are a number of return values when you extend TagSupport

doStartTag()
  • SKIP_BODY (default)
  • EVAL_BODY_INCLUDE
  • EVAL_BODY_BUFFERED (if using BodyTagSupport interface - see below)
doAfterBodyTag()
  • SKIP_BODY (default)
  • EVAL_BODY_AGAIN
doEndTag()
  • SKIP_PAGE
  • EVAL_PAGE (default)

Putting everything together we can create the following example

 

# JSP that invokes the tag
<%@ taglib prefix="myTags" uri="classicTags" %>
<html><body>
<table border="1">
  
<myTags:iterateMovies>
    <tr><td>${movie}</td></tr>
  </myTags:iterateMovies>
</table>
</html></body>

# The TLD for the tag
<tag>
  <description>classic custom tag</description>
  <name>iteratemovies</name>
  <tag-class>foo.MyIteratorTag</tag-class>
   <body-content>scriptless</body-content>
</tag>

# The tag handler class
public class MyIteratorTag extends TagSupport {
  String[] movies = {"Rambo", "Midnight Express", "Platoon"};
  int movieCounter;

  public int doStartTag() throws JspException {
    movieCounter = 0;
    // you need these two lines otherwise the body processed once without there being a movie     
    // attribute, so you would get an empty cell
    pageContext.setAttribute("movie", movies[movieCounter]);
    movieCounter++;
    return EVAL_BODY_INCLUDE
  }
 
  // Remember the body would have been processed once already before getting here
  public int doAfterBody() throws JspException {
    if (movieCounter < movies.length) {
      pageContext.setAttribute("movie", movies[movieCounter]);
      movieCounter++;
      return EVAL_BODY_AGAIN;
    } else {
      return SKIP_BODY;
    }
  }

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

MVC Tutorial Improvments

If you have a peek at the MVC tutorial we had to hard code some values into out html page

hard coded values <form method="POST" action="SelectCoffee.do">
    Select coffee
    Type:
    <select name="type" size=1">
      <option value="milky">Milky</option>
      <option value="froffy">Froffy</option>
      <option value="icey">Icey</option>
      <option value="strong">Spaced Out</option>

    </select>
    <br><br>
    <center>
      <input type="Submit">
    </center>
   </form>

We can now replace this with a more dynamic approach, using what we have just learned about tags. However we have added an additional part using the DynamicAttribute interface, without this interface we would have to provide a setter method for each attribute of a <select> element (id, class, style, title, etc) and thats of alot of coding, the Dynamic Attribute interface comes from the JSP API and it requires you to implement the setDynamicAttribute() method. This method needs to store the attribute name/value pairs, and a hashmap is the perfect data structure to hold this information. It can also be used in a classic tag and Tag Files as well.

HTML page Type:
      optionList='${applicationScope.typeList}'
<br><br>

Note: the custom tag will generate the list
Tag handler package com.example.taglib;

import jaba.io.Exception;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.SimpleTagSupport;

private class SelectTagHandler extends impleTagSupport implements DynamicAttributes {

  private static final String ATTR_TEMPLATE = "s='%s' ";
  private static final String OPTION_TEMPLATE = " <option value='%1$s'> %1$s </option>";

  private List optionsList = null;
  public void setOptionsList (List value) {
    this.optionsList = value;
  }

  private String name;
   public void setName (String value) {
    this.name = value;
  }

  private MAP<String,Object> tagAttrs = new HashMap<String, Object>();
  public void setDynamicAttributes(String uri, String name, Object value) {
    tagAttrs.put(name, value);
  }

  public void doTag() throws JspException, IOException {

    PageContext pageContext = (PageContext) getspContext();
    JspWriter out = pageContext.getOut();

     // creating the <select name="type", size="1">, using the String constants above
    out.print("<select ");
    out.print(String.format(ATTR_TEMPLATE, "name", this.name));

    for ( String attrName : tagAttrs.keyset() ) {
      String attrDefinition = tring.format(ATTR_TEMPLATE, attrName, tagAttrs.get(attrName));
      out.println(attrDefiniation);
    }
    out.print('>');

    // Creating the option list
    for ( Object option : this.optionList ) {
      String optionTag = String.format(OPTION_TEMPLATE, option.toString());
      out.println(optionTag);
    }

    // Finishing off the </select>
    out.println(" </select>");
  }
}  
TLD File <?xml version="1.0" encoding"ISO-8859-1" ?>

<taglib>

  <tlib-version>1.2</tlib-version>
  <jsp-version>1.2</jsp-version>

  <short-name>Forms TagLib</short-name>
  <uri>http://example.com/tags/forms</uri>
  <description>An example tag library of replacements for the HTML form tags</description>

  <tag>
    <name>select</name>
    <tag-class>com.example.taglib.SelectTagHandler</tag-class>
    <body-content>empty</body-content>

    <attribute>
      <name>optionsList</name>
      <type>java.util.List</type>
      <required>true</required>
      <rtexprvalue>true<rtexprvalue>
    </attribute>

    <attribute>
      <name>name</name>
      <required>true</required>
      <dynamic-attributes>true</dynamic-attributes>
    </attribute>
  </tag>
</taglib>

BodyTag

If you need access to the actual body contents, so that you may use it in an expression or filter or even alter in some way, then you need to extend BodyTagSupport instead of TagSupport and you will have access to the BodyTag interface methods.

Extending BodyTagSupport gives you two more lifecycle methods from the BodyTag interface - setBodyContext() and doInitBody(). You can use these two do something with the actual contents of the body of the tag used to invoke the handler. You also get one new return value for the doStartTag() EVAL_BODY_BUFFERED

Just remember that if you do NOT extend BodyTagSupport or implement BodyTag then you must not return EVAL_BODY_BUFFERED from the doStartTag().

A quick summary table to help you understand what we have discussed

doStartTag()
 
BodyTagSupport
TagSupport
possible return values

SKIP_BODY
EVAL_BODY_INCLUDE
EVAL_BODY_BUFFERED

SKIP_BODY
EVAL_BODY_INCLUDE
default return value from the implmenting class
EVAL_BODY_BUFFERED
SKIP_BODY
Number of times its can be called (per tag invocation from a JSP)
Exactly Once
Exactly Once
doAfterBody()
possible return values
SKIP_BODY
EVAL_BODY_AGAIN
SKIP_BODY
EVAL_BODY_AGAIN
default return value from the implmenting class
SKIP_BODY
SKIP_BODY
Number of times its can be called (per tag invocation from a JSP)
Zero to Many
Zero to Many
doEndTag()
possible return values
SKIP_PAGE
EVAL_PAGE
SKIP_PAGE
EVAL_PAGE
default return value from the implmenting class
EVAL_PAGE
EVAL_PAGE
Number of times its can be called (per tag invocation from a JSP)
Exactly Once
Exactly Once
doInitBody() and setBodyContent()
circumstances under which they can be called and number of times per tag invocation
Exactly once and only if doStartTag() returns EVAL_BODY_BUFFERED
NEVER

Tags that work Together

Presume that you had a <mine:Menu> tag that builds a custom navigation bar, which means you will have nested tags.

nested tags <mine:Menu>
  <mine:MenuItem itemValue="Dogs" />
  <mine:MenuItem itemValue="Cats" />
  <mine:MenuItem itemValue="Horses" />
</mine:Menu>

There is a mechanism for getting information to and from outer and inner tags, regardless of the depth of nesting. Both the SimpleTag and Tag interface have a getParent() method. The Tag getparent() returns a Tag but the SimpleTag getParent() returns an instance of JspTag (which means it can access a Classic Tag parent).

One more piece of information is that by using a getParent(), a Classic tag can access Classic tag parents and a Simple tag can access either a Classic or Simple parent, also you can walk up the items but not down, basically you cannot obtain a child item.

Classic tag handler public in doStartTag() throws JspException {

  OuterTag parent = (OuterTag) getParent();
  // do something with it
  return EVAL_BODY_INCLUDE;
}
Simple tag handler public void doTag() throws JspException, IOException {
  OuterTag parent = (OuterTag) getParent();
  // do something with it
}
Classic Example
 

# In a JSP
<mine:NestedLevel>
  <mine:NestedLevel>
    <mine:NestedLevel/>
  </mine:NestedLevel>
</mine:NestedLevel>

# Classic handler
package foo;

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

public class NestedLevelTag extends TagSupport {

  private int nestLevel = 0;

  public int doStartTag() throws JspException {
    nestLevel = 0;
    Tag parent = getParent();

    while (parent != null) {
      parent = parent.getParent();
      nestLevel++;
    }

    try {
      pageContext.getOut().println("<br>Tag nested level: " + nestLevel);
    } catch (IOException ex) {
        throw new JspException(IOException- " + ex.toString());
    }
    return EVAL_BODY_INCLUDE;
  }
}

Simple and Classic tags Differences

 
Simple Tags
Classic Tags
Tag Interfaces
SimpleTag (extends JspTag)
Tag (extends JspTag)
IterationTag (extends Tag)
BodyTag (extends IterationTag)
Support implementation classes
SimpleTagSupport (implements SimpleTag)
TagSuport (implements IterationTag)
BodyTagSupport (extends TagSupport, implements BodyTag)
Key lifecycle methods that you might implement
doTag()
doStartTag()
doEndTag()
doAfterBody()
(and for BodyTag - doInitBody() and setBodyContent())
How you write to the response output
getJspContext.getOut.println
(no try/catch needed because Simpletag methods declare IOException)
pageContext.getOut().println (wrapped in a try/catch because Classic tag methods do not declare the IOException)
How you access implicit variables and scoped attributes from a support implementation
with the getJspContext() method that returns a JspContext (which is usally a PageContext)
with the pageContext implicit variable NOT a method like it is with SimpleTag
How you cause the body to be processed
getJspBody().invoke(null);
eturn EVAL_BODY_INCLUE from doStart() or EVAL_BODY_BUFFERED if the class implements BodyTag
How you cause the current page evaluation to STOP
Throw a SkipPageException
etrun SKIP_PAGR from doEndag()