Return to index

logo

 

JSP - Part 2
Custom Tags
and Tag Handlers

 

This document introduces the use of JSP version 2.0 custom tags and their associated tag handlers. It is at this point that JSP really takes off and the ability to separate business logic (java code) and presentation (html) becomes much greater.

 

Introduction

Java Server Pages (JSP) technology makes it easy to embed bits of Java code (or scriptlets) within HTML documents. This solution, however, may not be suitable for all HTML content developers, perhaps because they do not know Java and they do not care to learn its syntax. While JavaBeans can be used to encapsulate much of the Java code, using them in JSP pages still requires content developers to have some knowledge of Java syntax.

JSP technology allows you to introduce new custom tags. As a Java developer, you can enhance JSP pages by introducing custom tags that can be deployed and used in an HTML-like syntax. Custom tags also allow you to provide better packaging - by improving the separation between business logic and presentation. Furthermore, some tags (i.e. tag handlers) may be reusable.

 

Overview of Tags

If you have experience with HTML, you already know about the types of tags that can be used. Basically there are two types of tags, and both can have attributes (information about how the tag should do its job).


 Bodyless Tags

A bodyless tag is a tag that has a start tag but does not have a matching end tag. It has the syntax:

<tagName attributeName="value" anotherAttributeName="anotherValue"/>

Bodyless tags are used to represent certain functions, such as presenting an input field, or displaying an image. Here is an example of a bodyless tag in HTML:

<img src="/developer/technicalArticles/xml/WebAppDev3/fig10.gif">

Notice that bodyless tags in HTML - because they are 'old technology' - do not follow ideal syntax : they do not end with '/>'.


 Tags with a Body

A tag with a body has a start tag and a matching end tag, with a 'body' inbetween. It has the syntax:

<tagName attributeName="value" anotherAttributeName="anotherValue">
      tag body
</tagName>

Tags with a body are used to perform operations on the body content, such as formatting. Here is an example of a tag with a body in HTML ('p' is shorthand for paragraph):

<p align="center">
      body text
</p> 

 

JSP Custom Tags

JSP custom tags are merely Java classes that implement special interfaces. Once they are developed and deployed, their actions can be called from your HTML using an XML-style syntax. They have a start tag and an end tag. They may or may not have a body. The body-less tag has '/>' at the end - and this is because it follows the more consistent XML format.

A bodyless tag can be expressed as:

<tagLibrary:tagName/>

A tag with a body can be expressed as:

<tagLibrary:tagName>
      body
</tagLibrary:tagName>


The Benefits of Custom Tags

A very important thing to note about JSP custom tags is that they do not offer more functionality than scriptlets - they simply provide better packaging, by helping you improve the separation of business logic and presentation. Some of the benefits of custom tags are:

  • They can reduce or eliminate scriptlets in your JSP applications. Any necessary parameters to the tag can be passed as attributes or body content, and therefore no Java code is needed to initialize or set component properties.
  • They have a simpler syntax. Scriptlets are written in Java, but custom tags use an HTML-like syntax.
    They can improve the productivity of non-programmer content developers, by allowing them to perform tasks that cannot be done with HTML.
  • They can be reusable and thereby save development and testing time. Scriptlets are not reusable, unless you call cut-and-paste reuse.

In short, you can use custom tags to accomplish complex tasks the same way you use HTML tags to create a complex presentation.

 

The SimpleTag Interface

Prior to JSP version 2.0, there existed functionality for custom tags that in certain respects works differently from that specified in JSP version 2. Although both now co-exist, TJI will, for now at least, only support the later, improved custom tag API. The easy way to spot the difference in tag handlers is that the older handlers implemented the Tag interface with two key methods - doStartTag() and doEndTag(). The latter handlers implement the SimpleTag interface and involve only one key method - doTag().

The complete interface definition of SimpleTag is shown below:

public interface SimpleTag extends JspTag {
public void doTag()throws JspException, java.io.IOException;
public void setParent(JspTag parent);
public JspTag getParent();
public void setJspContext(JspContext pc);
public void setJspBody(JspFragment jspBody);
} 

The doTag() method is called only once for any given tag invocation. This means that all code (including tag logic, iteration, or body evaluations) related to this tag is contained in just one method.

Class SimpleTagSupport provides a default implementation for all the methods in interface SimpleTag, and it is normally much easier for a custom tag handler class to extend class SimpleTagSupport than itself implement all the methods in interface SimpleTag.

The useful methods getParent() and getJspContext() are provided by class SimpleTagSupport.

The method getParent() can be called to get the parent tag handler instance in the case of nested tag handlers. Method setParent() will have been called by the server container if the handler is nested. If the handler is not nested, getParent() will return null.

The setJspContext() method is called by the server container and provides access to the same variables as the predefined variables available to scriptlets - but via method calls. For example, to get the output stream : getJspContext().getWriter() returns the current JspWriter instance.

The following items are valid in a SimpleTag body :

  • HTML markup
  • other standard and custom tags
  • <jsp:include .../> and <jsp:forward .../>

The setJspBody() method is provided to support body content and is called by the server container. The container invokes the setJspBody() method with a JspFragment object encapsulating the body of the tag. The tag handler implementation can call invoke() on that fragment to process the body - that is, to output the static HTML, call child tag handlers, etc. The SimpleTagSupport convenience class provides the method getJspBody().

 

The SimpleTagSupport Class

The javax.servlet.jsp.tagext.SimpleTagSupport class provides a default implementation of all the methods in interface SimpleTag. When developing tag handlers, the best approach in most cases is to extend this class (or your own extension of this class). In most cases the only thing to do next is overide the doTag() method; the default implementation does nothing.

The following lifecycle events take place for the simple tag handler (in the same order):

1. A new tag handler instance is created each time the tag is encountered by the server container. This is done by calling the zero argument constructor on the corresponding implementation class. It is important to remember that a new instance will be created for each tag invocation.

2. The setJspContext() and setParent() methods are invoked on the tag handler; setParent() only if the tag does have a parent tag. Method getParent() will return null if the tag has no parent.

3. The container calls the setters for each attribute defined for this tag in the order in which they appear in the JSP page.

4. The setJspBody() method is called by the container to set the body of this tag, as a JspFragment object, if the tag has a body.

5. The doTag() method is called by the container (server). All tag logic, iteration, body evaluations, etc. occur in this method.

6. All variables are synchronized after the doTag() method returns.

 

Some Simple Examples

A very simple SimpleTag :

The JSP tag :

<mytags:SayHello/>

The Java Tag Handler :

package mytags;
public class SayHello extends SimpleTagSupport {
     public void doTag() {
          getJspContext().getOut().print("Hello !");
     }
}


 A tag with a body :

The JSP tag :

<mytags:LoopTwice>
       Hello (again) !
</mytags:LoopTwice>

The Java Tag Handler :

package mytags;
public class LoopTwice extends SimpleTagSupport {
     public void doTag() {
          Writer out=getJspContext().getOut();
          JspFragment f=getJspBody();
          for (int i=0; i<2; i++) {
               f.invoke(out);
          }
     }
}


As in this simple example, 'wrapping' a tag around a body is a way to keep presentation in the JSP page. It is also a way to allow tag handlers to communicate - by use of the getParent() method. The Duke's Book Store example project makes a great deal of use of this way for tag handlers to communicate when arranged in a tree-like stucture. The thing to remember is that a tag handler is 'alive' between the start tag and end tag - i.e. all the time that its body (and tag handler 'children') is being processed.

 

 A Tag with a parameter :

The JSP tag :

<mytags:Loop numLoops="5">
      Hello again !
</mytags:Loop>

The Java Tag Handler :

package mytags;
public class Loop extends SimpleTagSupport {
     private int n=1;

 

     /* Called, bean-style, by server container         to set the specified parameter : */
     public void setNumLoops(String num) {
          n=(new Integer(num)).intValue();
     }

 

     public void doTag() {
          Writer out=getJspContext().getOut();
          JspFragment f=getJspBody();
          for (int i=0; i<n; i++) {
               f.invoke(out);
          }
     }
}


The server container will call method setNumLoops() with a value of '5' and then method doTag().

Note that the 'setter' methods in tag handlers used to receive tag attributes can have parameter types other than String - the primitive types int, boolean and float are also allowed. This is true for dynamic parameters too.

 

 A Tag with a dynamic parameter :

One can use a JSP expression as a dynamic parameter that is calculated at run-time. For example:

<mytags:Loop numLoops="<%= request.getParameter("num") %>" >
       Hello again !
</mytags:Loop>

 

 A nested Tag :

The JSP tag :

<mytags:RequestParam id="num">
      <mytags:Loop>
            Hello again !
      </mytags:Loop>
</mytags:RequestParam>


The Java Tag Handlers :

Loop is almost the same as before - but it will call its parent (tag handler RequestParam) to find out how many times to loop. RequestParam gets a specified parameter from the HttpServletRequest and makes it available to child tag handlers through the method getParam().

The URL calling the JSP page might be something like
http://host/Example.jsp?num=3

package mytags;
public class RequestParam extends SimpleTagSupport {
     private String id="", param="";
     /* Can be called by child tag handlers : */
     public String getParam() {return param;}

 

     /* Get request parameter and run body
        which will include the child tag handlers
        that can 'call back' to getParam() : */
     public void doTag() {
          PageContext app=(PageContext)getJspContext();
          HttpServletRequest request=(HttpServletRequest)app.getRequest();
          param=request.getParameter(id);
          JspFragment f=getJspBody();
          f.invoke(out);
     }

 

     /* Called by server to set specified parameter : */
     public void setId(String s) {id=s;}
}

and

package mytags;
public class Loop extends SimpleTagSupport {
     private int n=1;

 

     /* First task in doTag is to get the number of
        of loops required from tag handler GetParam */
     public void doTag() {
          String num=((GetParam)getParent()).getParam();
          n=(new Integer(num)).intValue();
          Writer out=getJspContext().getOut();
          JspFragment f=getJspBody();
          for (int i=0; i<n; i++) {
               f.invoke(out);
          }
     }
}


The two class casts in tag handler RequestParam are required because SimpleTag is based on javax.servlet and not javax.servlet.http - so when we need to use functionality specific to http type servlets, we need to make such class casts.

Of course, the above functionality could much more easily be achieved by using the standard tag library (see JSP Part 3) - but it has demonstrated how custom tags can communicate.

 

 A Tag in place of a scriptlet :

The possibilities here are many! See the custom tag based version of Duke's Book Store example project available from our web site to see some of the many ways this can be done.

NOTE : The full way to write a tag handler class, including the required imports and the exceptions it throws, is :

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
public class MyTagHandler extends SimpleTagSupport {
      public void doTag() throws JspException, IOException {
            ...
      }
} 

You may also need to import javax.servlet.http.*;

 

Notes on developing JSP in TJI

TJI simplifies greatly the development and setup of custom tags. Although some of the more advanced features are lost in this approach, it is ideal for less expert JSP developers or those who just want to learn about the core features of JSP.

<tagsetname:tagname> is, in TJI, directly 'translated' to both a full class name of tagsetname.tagname such that tagsetname is the package name - and to a specific directory:

<homepath>/web/servlets/tagsetname

In deployment to a server outside of TJI, there are sets of files to be created that specify these mappings. One of these - the Tag Library Descriptor (TLD) - also provides for some extra functionality that TJI does not currently support (for example, whether a particular parameter is 'required'). However, as can be seen from the Duke's Book Store example projects, it is still possible to do a lot with JSP in TJI. And if you run a separate server, such as TomCat, you can still do the bulk of your development in TJI.

The Duke's Book Store example using custom tags comes as two projects:

  • one for the JSP files and the non-tag java code (such as the book database)
  • and the other for the tag handlers as these are in a separate package (called 'tags' but it could be called something else, such as 'myTags').

So, although both the servlets generated from the JSP files and the tag handlers share the same base directory - <homePath>/web/servlets - they are in different directories; the tag handlers will be in directory <homepath>/web/servlets/tags because tags is their package name.

Hence, when a tag handler requires access to a class such as BookDB, the database java class (which will be in the servlets directory, the base directory for java code), it needs to be imported by the tag handler - not because it has a different base directory but because it is in a different package. (There is no reason to add to the project classpath - the tag handler classes will be found automatically because they share the same base directory as the auto-generated servlets.)

The project type to use for tag handler projects is 'Tag Handler'. A project type of 'JSP' is used for the JSP files, other resources such as HTML and gif image files - and for non-tag handler java files, such as Java Beans.

This is the preferred way to develop JSP applications in TJI - the tag handlers are in a separate project, which can be open at the same time. When a tag handler is recompiled you can simply reload the JSP page associated with it - because tag handlers (the newer JSP version 2.0 ones) are instantiated when they are called.

When a JSP page is changed, you can also simply reload the page because TJI will know that the page needs to be recompiled. When any JSP file is reloaded by TJI's web browser, all the application's servlets will be reloaded to ensure consistency. The session will also be reset. So if cookies are enabled, you should select 'Clear Cookies' from the 'Options' menu, if these are used to maintain session state, if you wish to start the application run with a new session.

The Duke's Book Store example project that uses custom tags could be written in many different ways - the one available for download is one person's 'first cut' notion of how it can be done. But other possibilities may be better. Perhaps by incorporating the use of Java Beans.

Although not all features of JSP are yet supported by TJI, this does not preclude the development of any task - the features so far implemented allow you to do anything! It's just that JSP files you might wish to use from your past development, or from elsewhere, might need to be adapted - or run using a separate JSP 'container' (server).

Example Project 1 (JSP) - available from our website - illustrates very simply all the main features of JSP - scriptlets, expressions, beans and custom tags (including nested and with dynamic parameters) - and is a good place to start for those new to JSP.

When there is a compile-time error or run-time exception involving the java in a JSP page, TJI will show the error by loading the auto-generated java servlet (in read-only mode). Once the problem is identified, you can alter the JSP file or associated tag handler as appropriate and then re-run the project.

The auto-generated servlets are named as the associated JSP file plus an underscore. So, for example, Login.jsp will be translated to Login_.java. This is to avoid a name conflict with any hand-coded servlets in the servlets directory.

You can set a .jsp file as the project's main 'HTML' file so that simply pressing the 'Run' button will launch the JSP project in the in-built Web Browser ('Web' tab), starting with that page.

Finally, note that a difference exists in how TJI translates and runs JSP from most other JSP enabled servers - it is far less pre-emptive in indicating parse errors (structural problems) - but when such problems exist, you will not get the result you would expect - and from what does happen you can easily locate and then solve the problem. This approach is not a design goal - rather, it allows TJI to incorporate a JSP engine that is far smaller than it would otherwise need to be.

 

Return to index