Easy Custom Tags with Tag Files, Part 2 Blog

Version 2


    The new feature of JSP 2.0, tag files, makes your life as a JSP programmer much easier, as you learned i n the first part of this series. In the second part, we'll cover more advanced features of the tag files, such as the variable directive, thejsp:doBody, and jsp:invoke actions, as well as how to package tag files into a JAR so they can be easily deployed.

    The variable Directive

    It is sometimes useful to expose values in a tag file to the calling JSP page. This is possible through the use of thevariable directive in the tag file. In a tag file, thevariable directive is analogous to thevariable element in a tag library descriptor and defines the details of a variable in the tag handler that is accessible from the calling JSP page. Because a tag file can have multiple variable directives, it is possible to provide multiple values to the calling JSP page. Comparevariable with the attribute directive that you use to pass a value from the JSP page to the tag file.

    The syntax of the variable directive is as follows:

    <%@ variable (attribute="value")* %>

    The syntax can be expressed in the following more informal form:

    <%@ variable attribute1="value1" attribute2="value2" ... %>

    The list of attributes for the variable directive is given in Table 1.

    Table 1. The variable directive's attributes

    name-givenThe variable name that will be available to the scripting language or the EL expressions in the calling JSP page. If thename-from-attribute is used, thename-given attribute must not be present, and vice versa. The value for name-given must not be the same as any of the attributes in this tag file.
    name-from-attributeThis attribute is similar to the name-givenattribute. However, the value for this attribute is the name of an attribute whose value at the start of the tag invocation will give the name of the variable. A translation error will result if both or neither of the name-given andname-from-attribute attributes is specified.
    aliasA locally scoped attribute to hold the value of this variable.
    variable-classThe type of this variable. The default isjava.lang.String.
    declareIndicates whether the variable is declared in the calling page or tag file after this tag invocation. The default is true.
    scopeThe scope of the scripting variable defined. The possible values are AT_BEGIN, AT_END, andNESTED (default).
    descriptionThe description of this variable.

    You may ask why you need the variable directive at all if you can just output the processing result to theJspWriter of the calling JSP page. This is true, but simply sending output to the JspWriter deprives the calling JSP page of some flexibility in using processing results. As an example, the firstTag.tag file in Part 1 of this article outputs the server's current date in long format. Suppose you want to also provide the server's current date in short format, then you must write another tag file. Having two tag files for similar functionality creates unnecessary maintenance headaches. Alternatively, you can expose two variables in the tag file,longDate and shortDate. The following tag file varDemo.tag provides the server's current date in two forms: long and short. It has two variables calledlongDate and shortDate.

    <%@ tag import="java.util.Date" import="java.text.DateFormat"%> <%@ variable name-given="longDate" %> <%@ variable name-given="shortDate" %> <%   Date now = new Date(System.currentTimeMillis());   DateFormat longFormat = DateFormat.getDateInstance(DateFormat.LONG);   DateFormat shortFormat = DateFormat.getDateInstance(DateFormat.SHORT);   jspContext.setAttribute( "longDate", longFormat.format(now));   jspContext.setAttribute( "shortDate", shortFormat.format(now)); %> <jsp:doBody/>

    Notice that you set a variable using thesetAttribute method on the JspContextobject of the tag. The jspContext implicit object represents this object. The set tag in the JSP Standard Tag Library (JSTL) encapsulates this functionality. If you are familiar with JSTL, you can use this tag instead of thesetAttribute method.

    Also note that you must use the jsp:doBody standard action to invoke the tag body. For more information, see the sections jsp:doBody and jsp:invoke below. To test the varDemo.tag file, use thevarDemoTest.jsp page:

    <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> Today's date: <br/> <tags:varDemo> In long format: ${longDate} <br/> In short format: ${shortDate} </tags:varDemo>

    You can invoke the varDemoTest.jsp page using the following URL (assuming, as before, that you're running Tomcat on port 8080):


    Figure 1 shows the result.

    The result of varDemoTest.jsp
    Figure 1. The result of varDemoTest.jsp

    There are many cases where you will want to use variables. As another example, suppose you want to write a custom action that fetches the details of a product from the database given the product identifier. Here is what you can do. You can use an attribute to pass the product identifier. For each piece of information you provide a variable to store that information. That gives you variables like name, price,description, imageUrl, and so on.


    The jsp:doBody standard action can be used only from inside a tag file. You use it to invoke the body of a tag. You've seen jsp:doBody in action in the tag file invarDemo.tag. In this section you'll learnjsp:doBody in more detail.

    The jsp:doBody action can have attributes. You use these attributes if you want to direct the output of the tag invocation to a variable. If used without an attribute, thejsp:doBody standard action writes the output to theJspWriter object of the calling JSP page. The list of the jsp:doBody standard action's attributes are given in Table 2.

    Note: All the attributes of jsp:doBody are optional.

    Table 2. The attributes of jsp:doBody

    varThe name of a scoped attribute to store the output of the tag body invocation. The value is stored as a java.lang.String object. Only one of the var or varReader attribute can be present.
    varReaderThe name of a scoped attribute to store the output of the tag body invocation. The value is stored as a java.io.Reader object. Only one of the var or varReader attribute can be present.
    scopeThe scope for the resulting variable.

    The following example shows how to use jsp:doBodyto invoke the tag body and store the output in a session-scoped variable called referer. The scenario is like this: you have a web site that sells toys and you advertised your web site heavily in many search engines. Of course, you want to know which search engine is the most effective in generating sales. You do this by recording the referer header of the main page of your web application. You use a tag file to store the value of referer as a session attribute. Later, when the user purchases a product, you can obtain the session attribute value and insert it into a database table.

    The example consists of one HTML file (searchEngine.html), two JSP pages (main.jsp and viewReferer.jsp), and one tag file (doBodyDemo.tag). The main.jsppage is the main page of the web site. It uses thedoBodyDemo custom tag to store thereferer header. To view the refererheader value, you use the viewReferer.jsp page. If you invoke the main.jsp page by directly typing in its URL, the referer header will be null. Therefore, you use the searchEngine.html file to go to themain.jsp page.

    The doBodyDemo.tag file is as follows:

    <jsp:doBody var="referer" scope="session"/>

    That's right. The doBodyDemo.tag file only consists of one line: a jsp:doBody standard action. What it does is invoke the body of the tag and store the output in a session attribute called referer.

    The main.jsp page is given below:

    <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> Your referer header: ${header.referer} <br/> <tags:doBodyDemo>   ${header.referer} </tags:doBodyDemo> <a href="viewReferer.jsp">View</a> the referer as a Session attribute.

    The main.jsp page prints the value of thereferer header, using text and an EL expression:

    Your referer header: ${header.referer} <br/>

    It then uses the doBodyDemo tag, passing thereferer header as the body.

    <tags:doBodyDemo>   ${header.referer} </tags:doBodyDemo>

    It then prints a link to the viewReferer.jsp page for your convenience:

    <a href="viewReferer.jsp">View</a> the referer as a Session attribute.

    The viewReferer.jsp page is shown below.

    The Referer header of the previous page is ${sessionScope.referer}

    The viewReferer.jsp page prints the value of a session attribute called referer, using an EL expression.

    Last, the searchEngine.html looks like this:

    Please click <a href="main.jsp">here</a>

    To test the example, first invoke thesearchEngine.html file using the following URL:


    You'll see the page in Figure 2.

    The searchEngine.html page
    Figure 2. The searchEngine.html page

    Now, click the link to go to the main.jsp page. Thereferer header of the main.jsp page will be the URL of the searchEngine.html. Figure 3 shows the main.jsp page.

    The main.jsp page
    Figure 3. The main.jsp page

    The main.jsp page invokes thedoBodyDemo custom action that stores thereferer session attribute. Now, click the view link in the main.jsp page to see the session attribute value. You'll see something similar to Figure 4.

    The viewReferer.jsp page
    Figure 4. The viewReferer.jsp page


    The jsp:invoke standard action is similar tojsp:doBody, and, like jsp:doBody, you can use jsp:invoke only in a tag file. You use thejsp:invoke standard action to invoke afragment attribute. Recall that an attribute can have the fragment attribute whose value is either true or false. When the fragment attribute value is true, the attribute is a fragment attribute, which you can invoke as many times as you want from a tag file. The jsp:invoke can have attributes too. The list of attributes is presented in Table 3. Note that only the fragment attribute is required.

    Table 3. The attributes of jsp:invoke

    fragmentThe name used to identify this fragment during this tag invocation.
    varThe name of a scoped attribute to store the output of the tag body invocation. The value is stored as ajava.lang.String object. Only one of thevar or varReader attribute can be present.
    varReaderThe name of a scoped attribute to store the output of the tag body invocation. The value is stored as ajava.io.Reader object. Only one of thevar or varReader attribute can be present.
    scopeThe scope for the resulting variable.

    As an example, consider the invokeDemo.tagfile:

    <%@ attribute name="productDetails" fragment="true" %> <%@ variable name-given="productName" %> <%@ variable name-given="description" %> <%@ variable name-given="price" %> <% jspContext.setAttribute( "productName", "Pelesonic DVD Player"); jspContext.setAttribute( "description", "Dolby Digital output through coaxial digital-audio jack," + " 500 lines horizontal resolution-image digest viewing"); jspContext.setAttribute("price", "65"); %> <jsp:invoke fragment="productDetails"/>

    The tag file defines the attribute productDetailswhose fragment attribute is true. It also defines three variables and sets the value for those variables. The last line of the tag file invokes the fragmentproductDetails. Because there is no varor varReader attribute in the jsp:invokestandard action, the result of tag invocation will be directed to the JspWriter object of the calling JSP page.

    To test the tag file, use this invokeTest.jsp page:

    <%@ taglib prefix="easy" tagdir="/WEB-INF/tags" %> <html> <head> <title>Product Details</title> </head> <body> <easy:invokeDemo> <jsp:attribute name="productDetails"> <table width="220" border="1"> <tr> <td><b>Product Name</b></td> <td>${productName}</td> </tr> <tr> <td><b>Description</b></td> <td>${description}</td> </tr> <tr> <td><b>Price</b></td> <td>${price}</td> </tr> </table> </jsp:attribute> </easy:invokeDemo> </body> </html>

    You can use the following URL to call theinvokeTest.jsp page.


    The result is shown in Figure 5.

    Using fragment attribute
    Figure 5. Using fragment attribute

    Packaging Tag Files

    Just like the traditional custom actions, you can package tag-file-based custom actions into a JAR file. To be accessible, tag files bundled in a JAR file need a tag library descriptor. For each tag, you must use a tag-file element, as opposed to the tag element for traditional tag handlers. Eachtag-file element needs two sub-elements:name and path. The nameelement defines the tag name and must be unique throughout all tag names in the application. The path element specifies the full path of the tag file and must begin with/META-INF/tags.

    As an example, here is how you bundle a tag handler based on theencode.tag file. You need the following tag library descriptor, which for this example, I callmyTLD2.tld:

    <?xml version="1.0" encoding="UTF-8" ?> <taglib 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" version="2.0"> <description>JSP 2.0 tag files</description> <tlib-version>1.0</tlib-version> <short-name>My Tag Files</short-name> <uri>http://www.brainysoftware.com/tagfiles</uri> <tag-file> <name>encode</name> <path>/META-INF/tags/encode.tag</path> </tag-file> </taglib>

    Note that there is one tag-file element with a nameand a path subelements. There is also theuri element that serves as the unique identifier for this tag library descriptor. The value of the urielement can be anything. In the example, I use my domain name to guarantee uniqueness. However, if you use a domain name, the domain name does not have to exist.

    Then you have to create a working directory, which in this example I call deploy. Under deploy, you'll have a META-INF directory with a tags subdirectory. You copy your tag file(s) to deploy underMETA-INF/tags and the tag library descriptor underMETA-INF. Figure 6 show the directory structure for this example.

    The directory structure of the tag files to be packaged
    Figure 6. The directory structure of the tag files to be packaged

    Now, change directories to the deploy directory and type in the following:

    jar cvf mytagfiles.jar *

    where mytagfiles.jar is the name of the resulting JAR.

    To use the custom actions in the JAR file, just copy the JAR file to the WEB-INF/lib directory of your application and start/restart your Web container. You can then use the tag(s) from a JSP page. The following is a JSP page that shows how you can use the encode tag from your application.

    <%@ taglib prefix="easy" uri="http://www.brainysoftware.com/tagfiles" %> <easy:encode input="<br/> means changing line"/>

    Note that in the JSP page you use the uri attribute in your taglib directive, instead of the tagdirattribute.

    Note also that you can package customs tags based on tag handlers into the same JAR as the custom tags that are based on tag files.

    Be warned that the tag files in your JAR file remain as tag files; that is, anyone with a text editor can see the contents of your code.


    In this article you have learned the new feature in JSP 2.0 that makes the process of writing tag extensions much simpler: the tag file. With tag files, you don't need a tag library descriptor. You don't even need to compile the tag handler. You have also seen how to use the jsp:invoke and jsp:doBodystandard actions and package tag handlers based on tag files.