JSP Custom Tags
JSP custom tags provide a standardized mechanism for separating the presentation and business logic in a dynamic web page, allowing page designers to focus on the presentation while application developers code the backend. They are just Java classes that implement special interfaces. Once a custom tag is developed and deployed, its actions can be called from your HTML using an XML syntax. There is a start tag and an end tag. The tag may or may not have a body. A custom tag can be expressed as:
<tagLibrary:tagName attribute="value">
body
</tagLibrary:tagName>
Benefits of Custom Tags
A very important thing to note about JSP custom tags is that they does not offer more functionality than scriptlets. They simply provide better packaging, by helping us improve the separation of the business logic and the presentation logic. Some of the benefits of a custom tag are:
- It can reduce or eliminate scriptlets in our JSP applications. Any necessary parameters to the tag can be passed as attributes or as body content, and therefore no Java code is needed to initialize or set component properties.
- It has a simpler syntax. A scriptlet is written in Java, but a custom tag can be used in an HTML-like syntax.
- It can improve the productivity of nonprogrammer content developers, by allowing them to perform tasks that cannot be done with HTML.
- It is reusable. It saves development and testing time. A scritplet is not reusable, unless we do cut-and-paste reuse.
Implementing JSP Custom Tags
There are a few steps involved in developing a custom tag. These steps can be summarized as follows:
- Write the tag handler class.
- Create the tag library descriptor (TLD).
- Make the TLD file and handler classes accessible.
- Reference the tag library.
- Use the tag in a JSP page.
Step 1. Write the tag handler class
The first thing we need to do is write the tag handler class. A tag handler is an object invoked by the JSP runtime to evaluate a custom tag during the execution of a JSP page that references the tag. The methods of the tag handler are called by the implementation class at various points during the evaluation of the tag. All tags must implement the Tag
interface. We do not need to include the body content in our example TodayTag
, because it only displays the current date in the user defined format. As such, our handler class will implement the Tag
interface (typically by extending the TagSupport
class). If we were to create a tag capable of processing body content, we would need to implement the BodyTag
interface (typically by extending the BodyTagSupport
class).
package jstl;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
public class TodayTag extends TagSupport{
private String mFormat;
public void setFormat(String pFormat) {
mFormat = pFormat;
}
public int doStartTag() throws JspException {
try {
JspWriter out = pageContext.getOut();
Date today = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat(mFormat);
out.print(dateFormatter.format(today));
} catch(IOException ioe) {
throw new JspException("Error: " + ioe.getMessage());
}
return SKIP_BODY;
}
public int doEndTag() throws JspException {
return SKIP_PAGE;
}
}
Step 2. Create the tag library descriptor (TLD)
Our next step is to define the library that will contain the mappings between our custom tag and the Java class (or classes) that will handle it. This library is defined within an XML document called a tag library descriptor (TLD). We'll call the TLD for our custom tag examples, customTag.tld.
="1.0"="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>ct</short-name>
<uri>/WEB-INF/customTag</uri>
<tag>
<name>today</name>
<tag-class>jstl.TodayTag</tag-class>
<body-content>empty</body-content>
<info>This Tag Displayes current server date in usser defined format</info>
<attribute>
<name>format</name>
<required>true</required>
<description>Provide a display format</description>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
All key information is contained within the Tag
tag where the tag name and the handler class are mapped. In more complex cases, we could use additional XML tags to provide more information about the library and about the tags. It is also typical to define multiple tags within a single library.
Step 3. Make the TLD file and handler classes accessible
The third step is to make the class or classes and TLD accessible to the web application. There are two ways of doing this. We can either package the classes and the TLD together into a JAR file and then store the JAR file in the web application's lib directory, or we can place the class files loosely in the classes subdirectory and place the TLD file somewhere beneath the web application's WEB-INF directory.
Step 4. Reference the tag library
The fourth step is to make the class or classes and TLD accessible to the web application. In order to use the tag, we have to reference it, and this can be done in three ways:
- Reference the tag library descriptor of an unpacked tag library. For example:
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
- Reference a JAR file containing a tag library. For example:
<%@ taglib uri="/WEB-INF/lib/customTag.jar" prefix="ct" %>
- Define a reference to the tag library descriptor from the web-application descriptor (web.xml), and define a short name to reference the tag library from JSP.
<taglib>
<taglib-uri>customTag</taglib-uri>
<taglib-location>/WEB-INF/customTag.tld</taglib-location>
</taglib>
Next, we add a JSP declaration to any page that will need to use the custom tag library:
<%@ taglib uri="customTag" prefix="ct" %>
Step 5. Use the tag in a JSP page
Now, we're ready to use our custom tag within a JSP page.
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSTL Custom Tag Today</title>
</head>
<body>
<h2 align="center"><ct:today format="MMMM dd, yyyy"/></h2>
</body>
</html>
The results should look similar to the figure shown below:
Parameterized Tags
There are two things that need to be mentioned to handle attributes to develop parameterized tags:
- Add a set method in the tag handler
- Add a new tag to customTag.tld
We can refer to the highlighted section of our previous example.
Tags with a body
A tag handler for a tag with a body is implemented differently depending on whether the body needs to be evaluated once or multiple times.
- Single Evaluation: If the body needs to be evaluated once, the tag handler should implement the
Tag
interface, or extend the TagSupport
abstract class; the doStartTag
method needs to return EVAL_BODY_INCLUDE
, and if it does not need to be evaluated at all, then it should return BODY_SKIP
. - Multiple Evaluation: If the body needs to be evaluated multiple times, the
BodyTag
interface should be implemented. The BodyTag
interface extends the Tag
interface, and defines additional methods (setBodyContent
, doInitBody
, and doAfterBody
) that enable a tag handler to inspect and possibly change its body. Alternatively, and similar to the TagSupport
class, we can extend the BodyTagSupport
class, which provides default implementations for the methods in the BodyTag
interface. Typically, we need to implement doInitBody
and doAfterBody
methods. The doInitBody
is called after the body content is set but before it is evaluated, and the doAfterBody
is called after the body content is evaluated.
Single Evaluation
Here is an example of a single evaluation where we extend the BodyTagSupport
class. This example reads the body content, and converts it to uppercase or lowercase depending on the attribute provided by us.
At first, we need to create the tag handler class:
package jstl;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class ChangeCaseTag extends BodyTagSupport {
private String mCase;
public void setCase(String pCase) {
mCase = pCase;
}
public int doAfterBody() throws JspException {
try {
BodyContent bc = getBodyContent();
String body = bc.getString();
JspWriter out = bc.getEnclosingWriter();
if (body != null) {
if ("upper".equalsIgnoreCase(mCase)) {
out.print(body.toUpperCase());
} else {
out.print(body.toLowerCase());
}
}
} catch (IOException ioe) {
throw new JspException("Error: " + ioe.getMessage());
}
return SKIP_BODY;
}
}
The next step is to add a tag to the tag library descriptor file, customTag.tld:
<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>ct</short-name>
<uri>/WEB-INF/customTag</uri>
<tag>
<name>changeCase</name>
<tag-class>jstl.ChangeCaseTag</tag-class>
<body-content>JSP</body-content>
<info>This Tag Displayes current server date in user defined format</info>
<attribute>
<name>case</name>
<required>true</required>
<description>Provide the case upper or lower</description>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
Note that when you write a tag with a body, the <body-content>
tag's value must be either JSP or jspcontent.
Now, a test driver for this example is shown below:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Upper or Lower Case</title>
</head>
<body>
<h2>
Upper Case :
<ct:changeCase case="upper">
Hello World
</ct:changeCase>
</h2>
<h2>
Lower Case :
<ct:changeCase case="lower">
Hello World
</ct:changeCase>
</h2>
</body>
</html>
The results should look similar to the figure shown below:
Multiple Evaluation
Let's now see an example of a body tag evaluated multiple times. The example tag accepts an integer as its attribute, and prints its body HTML as many times as indicated in the JSP code.
Once again, create a tag handler class first:
package jstl;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class LoopTextTag extends BodyTagSupport {
private int mTimes = 0;
private BodyContent mBodyContent;
public void setTimes(int pTimes) {
mTimes = pTimes;
}
public void setBodyContent(BodyContent pBodyContent) {
mBodyContent = pBodyContent;
}
public int doStartTag() throws JspException {
if (mTimes > 1) {
return EVAL_BODY_TAG;
} else {
return SKIP_BODY;
}
}
public int doAfterBody() throws JspException {
if (mTimes > 1) {
mTimes--;
return EVAL_BODY_TAG;
} else {
return SKIP_BODY;
}
}
public int doEndTag() throws JspException {
try {
if (mBodyContent != null) {
mBodyContent.writeOut(mBodyContent.getEnclosingWriter());
}
} catch (IOException pIOEx) {
throw new JspException("Error: " + pIOEx.getMessage());
}
return EVAL_PAGE;
}
}
The methods of this tag perform the following roles:
- The
doStartTag
method gets called at the start of the tag. It checks if the loop needs to be performed. - The
setBodyContent
is called by the JSP container to check for more than one loop. - The
doAfterBody
method is called after each evaluation; the number of times the loop needs to be performed is decreased by one, then it returns SKIP_BODY
when the number of times is not greater than one. - The
doEndTag
method is called at the end of the tag, and the content (if any) is written to the enclosing writer.
Similar to the previous examples, the next step is to add a new tag descriptor to customTag.tld:
="1.0"="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>ct</short-name>
<uri>/WEB-INF/customTag</uri>
<tag>
<name>loopText</name>
<tag-class>jstl.LoopTextTag</tag-class>
<body-content>JSP</body-content>
<info>This Tag Displayes given text multiple times</info>
<attribute>
<name>times</name>
<required>true</required>
<description>Provide number of times the text will be repeated</description>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
Note that the <rtexprvalue>
tag is true
, which specifies that evaluations will be performed at runtime.
Finally, we write a sample JSP page to test the <loopText>
tag.
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/WEB-INF/customTag.tld" prefix="ct" %>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>JSTL Custom Tag Loop Text</title>
</head>
<body>
<center>
<ct:loopText times="5">
<h3>Loop Text!</h3>
</ct:loopText>
</center>
</body>
</html>
The output should look similar to the figure shown below:
Conclusion
Custom tags help us improve the separation of program logic and presentation. The various examples in this article show how to develop and deploy simple and advanced custom tags. Also, you may wish to look at what the Jakarta Taglibs Project has to offer for various custom tag libraries. For more information, you can refer to the following websites: