Creating Our Own Custom TagsS2C Home « Creating Our Own Custom Tags

In the last five lessons we have investigated the features of the JSTL which allows us to write script free JSP pages using the Core, XML, Ii8n, Databaseand Function tag libraries. But what happens if the problems we are trying to solve are not encompassed by functionality within the JSTL, do we have to go back to putting scripting into our pages? The simple answer is no; luckily for us from JSP 1.1 we were given a way to write our own custom tags which became known as classic custom tags.

Classic custom tags gave developers all the benefits you get from JavaBeans when using standard actions but also allowed access to JSP Implicit Objects and could have attributes. Classic custom tags can be created by extending a member of the javax.servlet.jsp.tagext package. Creating classic custom tags was not an easy process because of the lifecycle involved and so simple tags which have a much simpler lifecycle were introduced in JSP 2.0, along with Tag Files, to make the whole process of writing our own custom tags a lot simpler. As we are concerned with jsp onwards on this site and there is no need to write the more complex classic custom tags anymore we will not do so and all discussions concerning writing of our own custom tags are related to the simple tag model.

Simple Tag APITop

Before we look at simple tag handlers and the life cycle of this type of object lets take a quick peek at the Simple Tag API which consists of two interfaces and a support class which we would usually extend, all of which can be found in the javax.servlet.jsp.tagext package as illustrated in the diagram below:

Simple TagAPI

The javax.servlet.jsp.tagext.JspTag serves as the base type for both classic and simple tags and is mainly used for organizational and type-safety purposes. The javax.servlet.jsp.tagext.SimpleTag interface as the name implies is solely for simple tag handlers and contains the lifecycle methods for this type. The methods within the SimpleTag interface are implemented by the javax.servlet.jsp.tagext.SimpleTagSupport support class, which can be used for convenience, although you will still have to override the doTag() method in your own class as this doesn't do anything.

Simple Tag Life CycleTop

We can think of an object's lifecycle as the various stages that said object goes through during its existence. Because a simple tag handler has to run within the boundaries of whatever web container we are using, there are additional factors involved. The lifecycle of a simple tag handler begins when the thread running the servlet generated from a JSP page uses an occurrence of the simple tag in question and ends when that occurrence has been processed.

The following slideshow shows the various stages in the simple tag lifecycle. Press the button below to step through the slideshow:

stl 1 stl 2 stl 3 stl 4 stl 5 stl 6

The JSP container creates an instance of a simple tag handler by calling its no-args constructor.

The JSP container calls the setJspContext() method.

If the simple tag is nested within another tag then the JSP container calls the setParent() method.

The JSP container calls the setXxx() method for each attribute of the tag.

If the simple tag has a body the JSP container calls the setJspBody() method.

The JSP container calls the doTag() method.

Press the button below to cycle through our Simple Tag Life Cycle slideshow.

First Simple Custom TagTop

Creating our own simple tag handlers is a two step process which requires writing of the custom tag handler and the registering of it in a tag library descriptor. When compiling a tag handler both the Servlet API and JSP API packages are required and so both must appear in the build path.

In the case of Tomcat both Jar files can be located in the Tomcat lib directory, these being servlet-api.jar for the Servlet API and jsp-api.jar for the JSP API.

In the following code example we are implementing the SimpleTag interface, rather than extending the more convenient SimpleTagSupport class, so we can see the tag handler life cycle methods that are called when we run the tag handler later from a JSP.


package firstjstl;
// I/O Imports
import java.io.IOException;
// JSP Imports
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
// JSTL Imports
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTag;

public class FirstCustomTag implements SimpleTag {
    JspContext jspContext;
    
    // We need to override all five SimpleTag methods
    @Override
    public void doTag() throws IOException, JspException {
        System.out.println("Within doTag() method, assigning JspWriter");
        jspContext.getOut().print("JspWriter assigned in first custom tag.");
    }
    @Override
    public JspTag getParent() {
        System.out.println("Within getParent() method"); 
        return null;
    }
    @Override
    public void setParent(JspTag parent) {
        System.out.println("Within setParent() method"); 
    }
    @Override
    public void setJspContext(JspContext jspContext) {
        System.out.println("Within setJspContext() method"); 
        this.jspContext = jspContext;
    }
    @Override
    public void setJspBody(JspFragment body) {
        System.out.println("Within setJspBody() method"); 
    }
}

Compiling Custom TagsTop

I put the Java code in directory  c:\_JSTL\customtags\src\firstjstl

The apache-tomcat-6.0.37 path is where I downloaded the Tomcat version I used into.

Remember when compiling a tag handler both the Servlet API and JSP API jars are required and so both must appear in the build path.

We compile FirstCustomTag.java using the java compiler with the -cp and -d options as shown below

  javac -cp c:\apache-tomcat-6.0.37\lib\servlet-api.jar;c:\apache-tomcat-6.0.37\lib\jsp-api.jar -d ..\..\classes FirstCustomTag.java

The following screenshot shows that we get a clean compile and also the FirstCustomTag class now compiled into the classes\firstjcl directory.

compile FirstCustomTag class

Registering Custom TagsTop

The container and any JSP pages that want to make use of created tags need a way to locate the custom tag in question and this is achieved by registering the tag using a Tag Library Descriptor (TLD) that points to the URI where the custom tag in question resides. The container will search in several places as outlined below for TLDs so make sure that any you create are in one of the locations listed below:

  1. Directly inside the WEB-INF folder
  2. Directly inside a sub-directory of WEB-INF folder
  3. Inside the META-INF directory inside a JAR file that's inside the WEB-INF folder
  4. Inside a sub-directory of the META-INF directory, inside a JAR file that's inside the WEB-INF folder

A TLD is an XML file with the tld extension, also note the namespace used for tag libraries.

The following code example shows the basic TLD file we will use to get our first custom tag working.


<?xml version="1.0" encoding="UTF-8"?>
<taglib xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd" version="2.1"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://java.sun.com/xml/ns/j2ee">
   <description>Simple Custom Tag</description>
   <tlib-version>1.0</tlib-version>
   <short-name>Our First Simple Custom Tag</short-name>
   <tag>
      <name>firstsimpletag</name>
      <tag-class>firstjstl.FirstCustomTag</tag-class>
      <body-content>empty</body-content>
   </tag>
</taglib>

Using Custom TagsTop

When we want to use a custom tag within our JSP pages we use the taglib directive which can reference either a relative path of the context root or an absolute path that points to the location of the TLD file.

The following code example shows a JSP page that uses the custom tag we wrote above.


<%@ taglib uri="/WEB-INF/firstsimpletag.tld" prefix="first"%>
<!DOCTYPE html>
<html>
<head><title>Testing Our First Simple Custom Tag</title></head> 
<body>
<h1>Testing Our First Simple Custom Tag</p>
<first:firstsimpletag></first:firstsimpletag>
</body>
</html>

This screenshot shows file structure within Tomcat folder c:\apache-tomcat-6.0.37\webapps\firstjstl for the above entities.

tomcat jstl dep struct

The following screenshot shows the results of running the firsttagtest.jsp JSP page within the firstjstl folder within Tomcat:

run firsttagtest

The following screenshot shows the highlighted life cycle methods that ran during execution of of first custom tag:

run firsttagtest log

Simple Tag With AttributesTop

Our custom tags can also be created with attributes that are declared within the TLD. The code snippet below shows an example of specifying a couple of attributes for a tag called leagues which we will add to our existing firstsimpletag.tld file. The attributes are called division and teams, both are required and both can take either a string or an expression.


   ...
   <tag>
      <name>leagues</name>
      <tag-class>secondjstl.SecondCustomTag</tag-class>
      <body-content>empty</body-content>
      <attribute>
         <name>division</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
         <name>teams</name>
         <required>true</required>
         <rtexprvalue>true</rtexprvalue>
      </attribute>
   </tag>
</taglib>

In the following code example of a simple tag we are extending the SimpleTagSupport support class so we don't have to implement the tag handler life cycle methods. We still have to override the doTag() method of the SimpleTagSupport support class if we want our custom tags to do anything.


package secondjstl;
// I/O Imports
import java.io.IOException;
// Util Imports
import java.util.StringTokenizer;
// JSP Imports
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
// JSTL Imports
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class SecondCustomTag extends SimpleTagSupport {
    // Our attributes
    private String division;
    private String teams;
    
    public void setDivision(String division) {
        this.division = division;
    }
    public void setTeams(String teams) {
        this.teams = teams;
    }
    // We need to override doTag() to do something
    @Override
    public void doTag() throws IOException, JspException {
        System.out.println("Within doTag() method");
        JspContext jspContext = getJspContext();
        JspWriter out = jspContext.getOut();
        
        out.print("<ul><li>" + division + "<ol>");
        StringTokenizer st = new StringTokenizer(teams,",");
        
        while (st.hasMoreTokens()) {
           String token = st.nextToken();
           out.print("<li>" + token + "</li>");
        }
        out.print("</oi></li></ul>");
    }
}

Using Simple Tag AttributesTop

We are using the same taglib directive as the first example with a different prefix.

The following code example shows a JSP page that tests the custom tag above and uses its attributes.


<%@ taglib uri="/WEB-INF/firstsimpletag.tld" prefix="second"%>
<!DOCTYPE html>
<html>
<head><title>Testing Our Second Simple Custom Tag</title></head> 
<body>
<h1>Football Division</p>
<second:league division="Premier" 
   teams="West Ham,Liverpool,Chelsea,Arsenal,Brentwood,Leyton Orient,Barnsley,Reading,York">
</body>
</html>

The following screenshot shows the results of running the above JSP page:

run secondtagtest log

Simple Tag With BodyTop

Our custom tags can also bodies which we can manipulate using the JspFragment that is passed by the container. The JspFragment class expresses a piece of JSP code that we are able to invoke one or more times. When defining our fragment it can only contain template text and JSP action elements, no Java scriptlets, declarations or expressions are allowed. The JspFragment class consists of the two methods getJspContext() which returns the JspContext pertaining to this JspFragment and the invoke() method which is used to execute the fragment and sends all output to the specified Writer. If the invoke() method is passed null then the output is routed to the JspWriter that was returned by the getOut() method of the JspContext associated with this particular fragment.

In the following code example of a simple tag we are extending the SimpleTagSupport support class so we don't have to implement the tag handler life cycle methods. What we are doing is setting the body content for each breed as we loop through the array.


package thirdjstl;
// I/O Imports
import java.io.IOException;
// JSP Imports
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
// JSTL Imports
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class ThirdCustomTag extends SimpleTagSupport {
    // Our attributes
    private String[] animal = {"Cat","Dog","Bird"};
    private String[][] breed = { {"Persian","Tabby","Siamese"}, {"Labrador","Chow"}, 
            {"Budgie","Parrot","Cockatiel"} };

    // We need to override doTag() to do something
    @Override
    public void doTag() throws IOException, JspException {
        System.out.println("Within doTag() method");
        JspContext jspContext = getJspContext();
        JspWriter out = jspContext.getOut();
        
        for (int i=0; i<animal.length; i++) { 
            out.print("<table><tr><th style='background:yellow'>" + animal[i] + "</th></tr>");
            for (int j=i; j==i; j++) { 
                for (int k=0; k<breed[j].length; k++) {
                    getJspContext().setAttribute("breed", breed[j][k]);
                    getJspBody().invoke(null);
                }
            }
            out.print("</table><p> </p>");
        }    
    }
}

The code snippet below shows the TLD entry for a tag called pets which we will add to our existing firstsimpletag.tld file. Notice that because we are using a body for this tag, the value of the <body-content> tag is scriptless for this custom tag.


   ...
   <tag>
      <name>pets</name>
      <tag-class>thirdjstl.ThirdCustomTag</tag-class>
      <body-content>scriptless</body-content>
   </tag>
</taglib>

Using Simple Tag BodyTop

We are using the same taglib directive as the first couple of examples but with a different prefix.

The following code example shows a JSP page that tests the custom tag above and uses its body content.


<%@ taglib uri="/WEB-INF/firstsimpletag.tld" prefix="third"%>
<!DOCTYPE html>
<html>
<head><title>Testing Our Third Simple Custom Tag</title></head> 
<body>
<h1>Pets and Breeds</h1>
<third:pets>
   <tr><td style="background:green">${breed}</td></tr>
</third:pets>
</body>
</html>

The following screenshot shows the results of running the above JSP page:

run thirdtagtest log

Lesson 7 Complete

In this lesson we looked at creating our own custom tags for when the functionality we need is not present in the JSTL tag libraries.

What's Next?

In the next lesson we look at EL Functions.

go to home page Homepage go to top of page Top