Encapsulate reusable functionality in JSP tags

Build your own custom JSP tag with Tomcat

Like HTML, JavaServer Pages (JSP) use the concept of tags as their building blocks. A handful of tags are available to you as a JSP developer, allowing you to embed Java code, include other documents, and even make use of JavaBeans components. Although those tags are all you need to build Websites with dynamic content, you will probably find yourself duplicating small bits of functionality into your JSP pages after a while. If that is the case, tag libraries may be the answer.

What are tag libraries?

Tag libraries, or

taglibs,

are a feature of JSP 1.1 that enables you to build libraries of reusable JSP tags. That means you can encapsulate common behavior in your own JSP tag and use it across the JSP pages in your Web apps. The ability to extract common functionality from a JSP page and easily reuse it in other pages and Web applications can be very powerful. But isn't that what JavaBeans were designed for?

JavaBeans are reusable software components. It's true that there are JSP tags that let you use JavaBeans within your pages. The only ability those tags allow you, however, is to bind named, scoped instances, and then get/set those instances' properties. If you need to call methods on your JavaBeans, you need to embed the appropriate Java code inside a scriptlet.

Tags or JavaBeans?

I've established that you can use JavaBeans or JSP tags to encapsulate common functionality. That in essence labels both JavaBeans and tags as reusable software components and raises the question of when one is more appropriate to use over the other. While no hard and fast rules explain that, a couple of factors can be taken into account.

Server-side JavaBeans are nonvisual components, and I generally use them for maintaining session state and business information. A good example would be using a bean to maintain the user's current state in a Web-based mail application.

Tags on the other hand can represent actions on a page, and I tend to use those to hide common functionality that generates dynamic HTML or controls the page in some way.

One of the key differences between JavaBeans and JSP tags is that tags are much more aware of the environment in which they are running. That includes the page context (containing the request, response, and so on) and the servlet context of the Web application in which the tag is running. JSP tags can use those contexts to access the HTTP session information and to also make use of JavaBeans that contain session and/or business state.

An example tag

That's enough about what tags are and how you can use them. The next questions I'll address are what they look like and how to write them. First, I'll present an example, showing how to write a simple JSP tag; then I'll show you how to deploy and use it in your JSP pages.

To keep things simple, I'll use the current reference implementation of the Servlet/JSP specification -- Tomcat from the Apache Group. Tomcat supports the new J2EE Web applications and, to save creating your own, you can use Tomcat's example Web application. That can be found under your Tomcat installation directory (TOMCAT_HOME) in webapps/examples. Further information about Tomcat, including details on where you can download it, can be found in the Resources section.

Displaying a file size

To set the scene, imagine that your Web application lets the user download a file via a hyperlink. Your JSP page could get the filename (or URI) from a JavaBean and generate the hyperlink dynamically. Let's say you realize that some of those files may be very large, and you'd like to display the file size so that the user can make an informed decision as to whether he or she wishes to download the file.

Obviously, a few options are open to you, one being to use the JavaBean to store the file size. Of course that isn't a good option if the contents of the file change frequently. Instead, you would use a custom tag that, given a file's URI, uses its environment to find out how large the file is.

Design and describe the tag

First, you'll need to describe your tag, giving it a name and specifying any attributes it may have. You do that by creating a tag library descriptor (.tld) file -- an XML file that describes your tag library.

For the purpose of that example, I'll create a file called taglib.tld and place it under the web-inf directory. The contents of the file as are follows.

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
                           "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
    <tlibversion>1.0</tlibversion>
    <jspversion>1.1</jspversion>
    <shortname>examples</shortname>
    <uri>http://www.mycompany.com/taglib</uri>
    <info>An example tag library</info>
    <tag>
        <name>size</name>
        <tagclass>examples.SizeTag</tagclass>
        <info>Works out how large a file, pointed to by a URI, is in bytes</info>
        <attribute> 
            <name>uri</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>
</taglib>

I'll skim over the top of the file and concentrate on the tag library that I am defining. A tag library has a short name, a brief description, and a URI. That doesn't need to be a real URI as it's just an identifier for the library. A tag library can have one or more tags, each tag having a name, some brief commentary about its purpose, and a fully qualified class name. That class name is the name of the Java class that contains the functionality provided by your custom tag. I'll write that class shortly.

My tag has one mandatory (required) attribute called uri, allowing me to specify the URI of the file whose size I want. When using custom tags on a JSP page, you can provide a Java scriptlet as the value of an attribute. The value between the <rtexprvalue> tags indicates that such may be the case and that the JSP engine should evaluate the runtime expression beforehand. For this simple example, I'll just pass a hard-coded URI, so I've set that to false for now.

Build the tag

Once you have described your tag and its attributes, you need to write the class that will provide the functionality. All JSP tags are required to implement the

javax.servlet.jsp.tagext.Tag

interface. When a tag is used on a page, the JSP engine instantiates the class and begins by calling the various life cycle methods that the interface defines. That includes telling the tag about its environment by referring to a

PageContext

object.

The following code shows the SizeTag class. There are two places where supporting classes can go but, for the purpose of this example, I'll place the tag under the web-inf/classes directory. There should already be an examples package in which I can place the tag.

 
package examples;
import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
/**
 * Given a URI, uses the servlet context to find out how large the
 * "real" file is in bytes.
 *
 * @author      Simon Brown
 */
public class SizeTag extends javax.servlet.jsp.tagext.TagSupport {
        /** the URI of the file */
        private String uri;                       
        /**
         * Performs the processing of this tag.
         */
        public int doStartTag() throws JspException {
            StringBuffer html = new StringBuffer();
            // ask the container (via the servlet context) for the
            // real path of a file pointed to by a URI
            String realPath = pageContext.getServletContext().getRealPath(uri);
            // we can now find out how large the file is
            File f = new File(realPath);
            long fileLength = f.length();
            // build up the HTML piece by piece ...
            html.append(fileLength);
            html.append(" bytes");
                       
            // ... and write it
            try {
                pageContext.getOut().write(html.toString());
            } catch (IOException ioe) {
                throw new JspException(ioe.getMessage());
            }
            return EVAL_BODY_INCLUDE;
        }
        /**
         * Standard JavaBeans style property setter for the URI.
         *
         * @param s        a String representing the URI
         */
        public void setUri(String s) {
            this.uri = s;
        }
}                       

You may have noticed that instead of implementing the Tag interface, I'm actually extending a class called TagSupport. That is provided as part of the standard Servlet/JSP distribution and contains a default implementation of the methods defined in the Tag interface.

There are only two methods of interest in this tag, doStartTag() and setUri(). When I defined the tag in the tag library descriptor, I said that it would have an attribute called uri. Attribute values are passed to tags via the appropriate set method. The Java code that performs that is generated by the JSP engine, which uses Java's reflection mechanism to check that the appropriate method does exist on the tag. For that reason, tags must define a set method for each of its defined attributes. The name of the method should follow the JavaBeans convention of changing the first character of the property name to uppercase. The setUri() method is defined as taking a String, although you can use any object.

Custom tags on a JSP use the XML notion of an opening and closing tag, reflected by the doStartTag() and doEndTag() methods defined in the Tag interface. As my tag is a very simple example, I've chosen only to implement doStartTag(), which gets called when the JSP engine encounters the opening (or starting) tag. My implementation uses the tag's page context to find out a file's local location, to which the specified URI points. Once the tag has that information, it uses a standard java.io.File object to work out the file's length and generates a small, dynamic HTML string. That generated HTML is then sent back to the JSP engine, using the output stream associated with the page context. The return value tells the JSP engine how the tag processing should continue. In that case, you're telling the engine to continue as normal and evaluate any body content.

You should now compile the above class, ensuring that you have the Servlet/JSP classes in your classpath. Those can be found in TOMCAT_HOME/lib/servlets.jar.

Registering the tag library

Now that you've defined your tag and written its supporting class, you now need to tell the Web application where to find the tags by registering the taglib with it. The Web application configuration file (web.xml) is stored under the

web-inf

directory. Open up the web.xml file and insert the following XML fragment. The ordering of XML elements in the file is important, so the fragment below should be placed before or after the existing example taglib.

    <taglib>
        <taglib-uri>
            http://www.mycompany.com/taglib
        <taglib-uri>
        <taglib-location>
           /WEB-INF/taglib.tld
        <taglib-location>
    </taglib>

That tells the Web application where to find the taglib descriptor and the URI that refers to the taglib. Again, that doesn't have to be a real URI, but it should be the same as the one specified in the taglib.tld file that you created above.

Using the tag

The final step in this example is to actually use the tag in a JSP page. To do that, you'll write a very simple JSP page as illustrated below. It should be saved with the normal .jsp extension and can be placed in the examples root directory (

TOMCAT_HOME/webapps/examples

).

<html>
<head>
<%@ taglib uri="http://www.mycompany.com/taglib" prefix="examples" %>
</head>
<body>
The file is <examples:size uri="/jsp/index.html"/>.
</body>
</html>

The taglib directive tells the JSP engine that your JSP wishes to use a taglib referred to by the specified URI. The taglib directive also takes a prefix that tags on the page will use. For the sake of this example, I'll be using the examples prefix.

On the JSP, your tag consists of the prefix (like an XML namespace), the tag name, and any attributes. It's written by using normal XML notation, and the example above uses the shorthand for combining the opening and closing tags. The URI that you pass your tag just points to an HTML file provided with the JSP examples.

All you need now is to start up Tomcat and request the JSP from your browser. What you should notice is that your custom tag has been completely processed and substituted with the HTML that it generated dynamically. If you take a look at the HTML source via the browser, you should also notice that no trace of your tag remains.

Summary

I hope this article has given you an introduction and overview of JSP tag libraries. I've covered some of the tag basics, illustrated when you would use custom tags over JavaBeans, and presented a short example of how to build and deploy a tag.

Although there's a lot more to taglibs than has been presented here, the article aimed to give you a good starting point from which to explore further and begin to develop your own taglibs. Custom tags are a very powerful addition to the current JSP specification and a great mechanism for wrapping up reusable functionality on your JSPs.

Simon Brown is a distributed systems designer and developer with more than three years of commercial Java experience. He has considerable knowledge of large-scale distributed developments for major clients, having designed and implemented a number of important Java systems, largely in the banking and media sectors. Considered one of Synamic's more senior Java specialists, he acts as technical lead and mentor to other team members and presents at Java events, including a BOF session at JavaOne 2000. Simon is a Sun-certified enterprise architect for J2EE and developer for Java 2.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies