Custom Tag Development
If there is not a tag in the current five libraries that meet your needs, then you can write your own custom tag handler. There are three different ways to build your own tag handlers
Tag Files can invoke reusable content using a custom tag instead of the generic <jsp:include> or <c:import>. Tag Files are a kind of "tag handler lite" because they let page developers create custom tags without having to write a complicated Java tag handler class, they are just glorified includes.
There are 3 steps to take in order to use a File tag
Step One | # create the Tag File, take an include file "Header.jsp" and rename to "Header.tag" <img src="images/companyLogo.gif" > <br> |
Step Two | Put the tag file "Header.tag" in a directory named "tags" inside your applications WEB-INF directory |
Step Three | # Put a taglib directive (with a tagdir attribute) in the JSP and invoke the tag <%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %> <html><body> <myTags:Header/> </body></html> |
You invoke a Tag File with a tag, and the tags can have attributes, these attributes get sent to the Tag File and and we have to add a new attribute directive
tag attributes | # In the Tag File Note: that all tag attributes do have TAG scope, once the tag is closed, the tag attributes go out of scope |
The attributes are defined in the TLD, so how does the web developer know what attributes are available to him/her, well there is a new type of directive and its just for Tag Files, you saw one in step one above, this attribute knows how to match the TLD attribute directives. If the attribute directive is missing from the file you will get an error if the attribute is required and you are not supplying it.
attribute directive | <%@ attribute name="subTitle" required="true" rtexprvalue="true" %> |
Sometimes the value we are passing my be very large (paragraph size), we can use the <jsp:doBody/> to pass this information to the Tag File, but again without a TLD we need to declare the body-type content, and we use another new tag directive and its a bit like a page directive in JSP, however you cannot use scripting inside a <body-content> element. The tagdependent value means the body-content will be treated like plain text which means no EL, tags and scripts, the only other values are empty or scriptless (the default).
passing large values | # In the Tag File |
The container will look for tags in a number of places
![]() |
|
When you need Java, you need a custom tag handler, a handler is simply a Java class that does the work of the tag. The tag handler has access to tag attributes, the tag body and even the page context so it can get scoped attributes and request and response. Custom tag handlers come in two favors simple and classic (will discuss these later). Simple handlers with tag files should be all you will ever need but the classic option is there just in case that by any chance if simple and the tag files don't give you what you need, bear in mind there still be a lot of classic code out there in the field.
There are 3 steps to creating a simple tag handler
Step One | # Write a class that extends SimpleTagSupport import javax.servlet.jsp.tagext.*; |
Step Three | # Create a TLD for the tag (WEB-INF/tlds/simpleTags.tld) <taglib ...> <tlib-version>1.2</tlib-version> <short-name>simpleTags</short-name> <uri>simpleTags</uri> <tag> <description>simple custom tag</description> <name>simple1</name> <tag-class>foo.SimpleTagTest1</tag-class> <body-content>empty</body-content> </tag> </taglib> |
Step Four | # Write the JSP that uses the tag <%@ taglib prefix="myTags" uri="simpleTags" %> <html><body> <myTags:simple1> </html></body> |
A simple tag with a body, I have only highlighted the bits that need to change
JSP that uses the tag | <myTags:simple2> This is the body message </myTags:simple2> |
tag handler class | # Change the above java to reflect the below and recompile javac -cp d:\tomcat6\lib\jasper-api.jar;d:\tomcat6\lib\servlet-api.jar;d:\tomcat6\lib\jsp-api.jar -d classes src\SimpleTagTest2.java |
TL for the tag | <tag> <description>simple cutom tag</description> <name>simple2</name> <tag-class>foo.SimpleTagTest2</tag-class> <body-content>scriptless</body-content> </tag> Note: scriptless means the tag can have a body but the body cannot have scripting (no scriptlets, scripting expressions or declarations) |
The life of a simple tag handler is below, a JSP invokes a tag, a new instance of the tag handler class is instantiated, two or more methods are called on the handler and when the doTag() methods completes, the handler objects goes away, in other words words they are not reused.
Here are some more examples
a tag with dynamic row data | # JSP Tag } |
a simple tag with an attribute | # JSP Tag # The tag handler doTag() method # The TLD for the tag |
Sometimes you start creating a page then an exception is thrown by a Tag, you have part of the page created and you want to use this part to still appear as the response but you don't want the response to include anything still left to be processed after the tag throws an exception, this is why SkipPageException exists, it will show everything up until the exception. If the page that onvokes the tag was included from some other page, only the page that invokes the tag stops processing, the original page that did the include keeps going after the SkipPageException
SkipPageException | # tag handler |
The tag handler API is below, everything in the grey box is from the Classic tag model for custom tag handlers. The tag handler API has five interfaces and three supporting classes, there is no reason to implement the interfaces directly, so you will probably always extend a support class. One thing you should know about classic tags is that they can be pooled and reused by the Container so watch out on instance variables they need to be reset if you desire, otherwise they will have the previous values in them.
Now for a simple example of a classic tag, look at the comments to get an idea on what's going on.
Simple classic tag | # JSP Tag # The TLD for the tag # The tag handler |
The classic tag lifecycle is below, it calls doStartTag() then doAfterBodyTag() and finally the doEndTag()
There are a number of return values when you extend TagSupport
doStartTag() |
|
![]() |
doAfterBodyTag() |
|
|
doEndTag() |
|
Putting everything together we can create the following example
# JSP that invokes the tag |
If you have a peek at the MVC tutorial we had to hard code some values into out html page
hard coded values | <form method="POST" action="SelectCoffee.do"> Select coffee Type: <select name="type" size=1"> <option value="milky">Milky</option> <option value="froffy">Froffy</option> <option value="icey">Icey</option> <option value="strong">Spaced Out</option> </select> <br><br> <center> <input type="Submit"> </center> </form> |
We can now replace this with a more dynamic approach, using what we have just learned about tags. However we have added an additional part using the DynamicAttribute interface, without this interface we would have to provide a setter method for each attribute of a <select> element (id, class, style, title, etc) and thats of alot of coding, the Dynamic Attribute interface comes from the JSP API and it requires you to implement the setDynamicAttribute() method. This method needs to store the attribute name/value pairs, and a hashmap is the perfect data structure to hold this information. It can also be used in a classic tag and Tag Files as well.
HTML page | Type: optionList='${applicationScope.typeList}' <br><br> Note: the custom tag will generate the list |
Tag handler | package com.example.taglib; import jaba.io.Exception; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.DynamicAttributes; import javax.servlet.jsp.SimpleTagSupport; private class SelectTagHandler extends impleTagSupport implements DynamicAttributes { private static final String ATTR_TEMPLATE = "s='%s' "; private static final String OPTION_TEMPLATE = " <option value='%1$s'> %1$s </option>"; private List optionsList = null; public void setOptionsList (List value) { this.optionsList = value; } private String name; public void setName (String value) { this.name = value; } private MAP<String,Object> tagAttrs = new HashMap<String, Object>(); public void setDynamicAttributes(String uri, String name, Object value) { tagAttrs.put(name, value); } public void doTag() throws JspException, IOException { PageContext pageContext = (PageContext) getspContext(); JspWriter out = pageContext.getOut(); // creating the <select name="type", size="1">, using the String constants above out.print("<select "); out.print(String.format(ATTR_TEMPLATE, "name", this.name)); for ( String attrName : tagAttrs.keyset() ) { String attrDefinition = tring.format(ATTR_TEMPLATE, attrName, tagAttrs.get(attrName)); out.println(attrDefiniation); } out.print('>'); // Creating the option list for ( Object option : this.optionList ) { String optionTag = String.format(OPTION_TEMPLATE, option.toString()); out.println(optionTag); } // Finishing off the </select> out.println(" </select>"); } } |
TLD File | <?xml version="1.0" encoding"ISO-8859-1" ?> <taglib> <tlib-version>1.2</tlib-version> <jsp-version>1.2</jsp-version> <short-name>Forms TagLib</short-name> <uri>http://example.com/tags/forms</uri> <description>An example tag library of replacements for the HTML form tags</description> <tag> <name>select</name> <tag-class>com.example.taglib.SelectTagHandler</tag-class> <body-content>empty</body-content> <attribute> <name>optionsList</name> <type>java.util.List</type> <required>true</required> <rtexprvalue>true<rtexprvalue> </attribute> <attribute> <name>name</name> <required>true</required> <dynamic-attributes>true</dynamic-attributes> </attribute> </tag> </taglib> |
If you need access to the actual body contents, so that you may use it in an expression or filter or even alter in some way, then you need to extend BodyTagSupport instead of TagSupport and you will have access to the BodyTag interface methods.
Extending BodyTagSupport gives you two more lifecycle methods from the BodyTag interface - setBodyContext() and doInitBody(). You can use these two do something with the actual contents of the body of the tag used to invoke the handler. You also get one new return value for the doStartTag() EVAL_BODY_BUFFERED
Just remember that if you do NOT extend BodyTagSupport or implement BodyTag then you must not return EVAL_BODY_BUFFERED from the doStartTag().
A quick summary table to help you understand what we have discussed
doStartTag() |
||
BodyTagSupport |
TagSupport |
|
possible return values | SKIP_BODY |
SKIP_BODY EVAL_BODY_INCLUDE |
default return value from the implmenting class | EVAL_BODY_BUFFERED |
SKIP_BODY |
Number of times its can be called (per tag invocation from a JSP) | Exactly Once |
Exactly Once |
doAfterBody() |
||
possible return values | SKIP_BODY EVAL_BODY_AGAIN |
SKIP_BODY EVAL_BODY_AGAIN |
default return value from the implmenting class | SKIP_BODY |
SKIP_BODY |
Number of times its can be called (per tag invocation from a JSP) | Zero to Many |
Zero to Many |
doEndTag() |
||
possible return values | SKIP_PAGE EVAL_PAGE |
SKIP_PAGE EVAL_PAGE |
default return value from the implmenting class | EVAL_PAGE |
EVAL_PAGE |
Number of times its can be called (per tag invocation from a JSP) | Exactly Once |
Exactly Once |
doInitBody() and setBodyContent() |
||
circumstances under which they can be called and number of times per tag invocation | Exactly once and only if doStartTag() returns EVAL_BODY_BUFFERED |
NEVER |
Presume that you had a <mine:Menu> tag that builds a custom navigation bar, which means you will have nested tags.
nested tags | <mine:Menu> <mine:MenuItem itemValue="Dogs" /> <mine:MenuItem itemValue="Cats" /> <mine:MenuItem itemValue="Horses" /> </mine:Menu> |
There is a mechanism for getting information to and from outer and inner tags, regardless of the depth of nesting. Both the SimpleTag and Tag interface have a getParent() method. The Tag getparent() returns a Tag but the SimpleTag getParent() returns an instance of JspTag (which means it can access a Classic Tag parent).
One more piece of information is that by using a getParent(), a Classic tag can access Classic tag parents and a Simple tag can access either a Classic or Simple parent, also you can walk up the items but not down, basically you cannot obtain a child item.
Classic tag handler | public in doStartTag() throws JspException { OuterTag parent = (OuterTag) getParent(); // do something with it return EVAL_BODY_INCLUDE; } |
Simple tag handler | public void doTag() throws JspException, IOException { OuterTag parent = (OuterTag) getParent(); // do something with it } |
Classic Example |
|
# In a JSP # Classic handler import javax.servlet.jsp.*; |
Simple and Classic tags Differences
Simple Tags |
Classic Tags |
|
Tag Interfaces | SimpleTag (extends JspTag) |
Tag (extends JspTag) IterationTag (extends Tag) BodyTag (extends IterationTag) |
Support implementation classes | SimpleTagSupport (implements SimpleTag) |
TagSuport (implements IterationTag) BodyTagSupport (extends TagSupport, implements BodyTag) |
Key lifecycle methods that you might implement | doTag() |
doStartTag() doEndTag() doAfterBody() (and for BodyTag - doInitBody() and setBodyContent()) |
How you write to the response output | getJspContext.getOut.println (no try/catch needed because Simpletag methods declare IOException) |
pageContext.getOut().println (wrapped in a try/catch because Classic tag methods do not declare the IOException) |
How you access implicit variables and scoped attributes from a support implementation | with the getJspContext() method that returns a JspContext (which is usally a PageContext) |
with the pageContext implicit variable NOT a method like it is with SimpleTag |
How you cause the body to be processed | getJspBody().invoke(null); |
eturn EVAL_BODY_INCLUE from doStart() or EVAL_BODY_BUFFERED if the class implements BodyTag |
How you cause the current page evaluation to STOP | Throw a SkipPageException |
etrun SKIP_PAGR from doEndag() |