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
,
Database
and 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:
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:
The JSP container creates an instance of a simple tag handler by calling its no-args constructor.
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.
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:
- Directly inside the
WEB-INF
folder - Directly inside a sub-directory of
WEB-INF
folder - Inside the
META-INF
directory inside aJAR
file that's inside theWEB-INF
folder - Inside a sub-directory of the
META-INF
directory, inside aJAR
file that's inside theWEB-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.
The following screenshot shows the results of running the firsttagtest.jsp
JSP page within the firstjstl
folder within Tomcat:
The following screenshot shows the highlighted life cycle methods that ran during execution of of first custom tag:
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:
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:
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.