Presented by Shireesh Thanneru, The Sycamore Group Inc.
http://www.TheSycamoreGroup.com Table of Contents If you're viewing this document online, you can click any of the topics below to link directly to that section. 1. Why Custom Tags 2 2. Tag Libraries 5 3. How Tag Libraries work 10 4. Example: HTML Encoding 12 5. Example: Simple Iteration 15 An Introduction to JSP Custom Tags Page 1 Section 1. Why Custom Tags The Problem with Straight-up Servlets In the beginning, servlets were invented, and the world saw that they were good. Dynamic web pages based on servlets executed quickly, could be moved between servers easily, and integrated well with back-end data sources. Servlets became widely accepted as a premiere platform for server-side web development. However, the commonly-used simple approach to generating HTML content, having the programer write an out.println() call per HTML line, became a serious problem for real servlet use. HTML content had to be created within code, an onerous and time consuming task for long HTML pages. In addition, content creators had to ask developers to make all content changes. People searched for a better way. Enter JSP With this new technology you could put snippets of Java code inside your HTML, and the server would automatically create a servlet from the page. This helped quite a lot. But having artists and developers working on the same file wasn't ideal, and having Java inside HTML proved almost as awkward as having HTML inside Java. It could easily create a hard to read mess. So people matured in their use of JSP and started to rely more on JavaBeans. Beans were written to contain business logic code needed by the JSP page. Much of the code inside the JSP page could be moved out of the page into the bean with only minimal hooks left behind where the page would access the bean. More recently, people have started to note that JSP pages used this way are really a "view". They're a component used to display the results of a client request. So people thought, Why submit a request directly to a "view"? What if the targetted "view" isn't the proper view for that request? After all, many requests have several possible resulting views. For example, the same request might generate a success page, a database exception error report, or a required parameter missing error report. Why must a client directly submit its request to a view? Shouldn't the client make a request to some general server component and let the server determine the JSP view to return? This belief caused many people to adopt what has been called the "Model 2" design, named after an architecture laid out in the JSP 0.92 specification and based on the model-view-controller pattern. This was another improvement -- and where many serious developers stand today. With this design, requests are submitted to a servlet "controller" that performs business logic and generates an appropriate data "model" to be displayed. This data is then passed internally to a JSP page "view" for rendering, where it appears to the JSP page like a normal embedded JavaBean. The appropriate JSP page can be selected to do the display, depending on the logic inside the controlling servlet. The JSP page becomes a nice template. Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 2 The Problems with JSP Java Code Too Tempting JSP makes it tempting to put Java code in the web page, even though that's considered bad design. Java Code Required Doing mundane things in JSP can actually demand putting Java code in the page. For example, assume the page needs to determine the locale of the current web user to create a link to the web app home page: In JSP this is best done using Java code: <a href="<%= session.getUserProfile().getLocale() %>/index.html"> Home </a> Lousy Looping Looping is overly difficult in JSP. Here's the JSP code to iterate over an ArrayList of X objects printing the name of each. <% ArrayList list = (ArrayList) request.getParameter("list"); Iterator iter = list.iterator(); while (iter.hasNext()) { %> The next name is: <%= ((X)e.next()).getName() %> <br/> <% } %> The Solutions Templating Engines By using a template engine intended for this exact use instead of the general-purpose JSP, the underlying design can be cleaner and the syntax can be clearer. Several companies have built such engines, the most popular probably being WebMacro (http://webmacro.org, from Semiotek) whose engine is free. But the templating engines have their own drawbacks: * No specification exists for how a template engine should behave. * Template engines aren't widely known. Using template engines is a relatively unknown alternative technique. Custom Tags Custom Tags offer an ideal solution for this "spaghetti code" problem. Tag libraries improve the maintainability of web applications. This improved maintainance comes from the implementation of the simple XML-based interface of the custom tag on the JSP page. Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 3 Advantages of Custom Tags Custom tags have many features that make them attractive to use from any JSP. Custom tags can * be customized via attributes passed from the calling page, either staticly or determined at runtime. * have access to all the objects available to JSP pages including request, response, and out. * modify the response generated by the calling page. * create and initialize a JavaBeans component and introduce them as scripting variables in a JSP. * be nested within one another, allowing for complex interactions within a JSP page. * encapsulate both simple and complex behaviors in an easy to use syntax and greatly simplify the readability of JSP pages. Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 4 Section 2. Tag Libraries The Components of a Tag Library A simple custom tag consists of the following elements: 1. The Tag Handler: This is the actual core of a tag. A tag handler can reference any outside material it needs (JavaBeans) and has access to all the information from a JSP page (the pageContext object). Also the JSP page passes to the tag handler all tag attributes that have been set as well as the content that resides within the tag body statement on the JSP page. When the tag handler is finished processing, it sends the output back to the JSP page. 2. The Tag Library Descriptor (tld file): This is a simple XML file which documents the properties, information and location of the tag handler file. The JSP container uses this file to map out where and how to use a called tag library. 3. The Web Site's web.xml File: This is the web application's initialization file and within this file we define what custom tag libraries we are using in a web application and which tld files are used to describe each custom tag library. 4. A Tag Library Declaration on the JSP Page: This is as simple as using a Taglib directive to declare the existence of our tags. After the tag library has been declared on the JSP page we can then use it freely within the JSP page. The challenging part isn't the coding but rather hooking the pieces together correctly. However, this layering is very important as it is one reason that tag libraries are flexible and very portable. The TLD File The TLD File A tag library descriptor (TLD) file is an XML document that describes the library. A TLD contains information about the library as a whole and about each tag contained in the library. TLDs are used by a JSP container to validate the tags. There is typically some header information followed by elements used to define the tag library. The elements are <taglib> The tag library itself. <tlibversion> The tag library's version. <jspversion> The JSP specification version the tag library depends on. <shortname> A simple default name with a mnemonic value. For example, <shortname> may be Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 5 used as the preferred prefix value in taglib directives and/or to create prefixes for IDs. <uri> A optional URI that uniquely identifies the tag library. <info> Descriptive information about the tag library. Then each tag contained in the library is described. There can be one or many tags per library. There is only one TLD element required for all tags, and that is the one used to specify a tag handler's class: <tagclass>classname</tagclass> There are various other elements used to describe tags. Which elements a tag uses will depend on how the tag is implemented in the handler. If a tag has attributes associated with it, then each attribute must be described within the <tag> element. If an attribute is required by a tag, <required> is set to "true" or "yes". To allow a runtime expression value to be used by the tag, the <rtexpvalue> is set to "true" or "yes". For each attribute of a tag, a Bean-like getter/setter method needs to be defined in the handler class. It's also possible to define scripting variables for use in tags. This is accomplished using a TagExtraInfo(TEI) class. The TEI class specifies the name of the scripting variable to be introduced, its class and its scope and also whether or not to create a new one, if one is already not defined in the JSP page. If a TagExtraInfo is to be used, the class must be defined using the <teiclass>classname<teiclass> within the tag definition. An Example TLD File A sample TLD file looks like <?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>samples</shortname> <info>Sample Tag library</info> <!-A Simple tag --> <tag> <name>hello</name> <tagclass>com.tsg.taglibs.Hello </tagclass> <!--Body content can have a value of empty: no body JSP: body that is evaluated by container, then possibly processed by the tag tagdependent: body is only processed by tag; JSP in body is not evaluated. --> <bodycontent>empty</bodycontent> <info> This is a simple hello tag. Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 6 </info> <!-- Optional attributes --> <!- personalized name --> <attribute> <name>name</name> <required>false</required> <rtexpvalue>true</rtexpvalue> </attribute> </tag> </taglib> An Example web.xml file A sample web.xml looks like <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <taglib> <taglib-uri> /tut </taglib-uri> <taglib-location> /WEB-INF/tlds/taglib.tld </taglib-location> </taglib> </web-app> The Tag Handler The Tag Handler The tag is defined in a handler class. TagSupport is generally used as the base class used for simple tags. BodyTagSupport is generally used as the base class used for tags complex tags. They can be found in the javax.servlet.jsp.tagext package. What your tag is implementing will depend on what methods could potentially be called and what needs to be implemented. TagSupport and BodyTagSupport supply default implementations of the methods listed below. If your Tag: has no attributes and no body ---implement doStartTag, doEndTag, release has attributes Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 7 ---implement doStartTag, doEndTag, set/getAttribute1...N has a body with no interaction ---implement doStartTag, doEndTag, release has a body with interaction ---implement doStartTag, doEndTag, release, doInitBody, doAfterBody An Example Tag Handler The Java code for the tag defined in the sample TLD file would look like package com.tsg.taglibs; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; /** * This is a simple tag example to show how content is added to the * output stream when a tag is encountered in a JSP page. */ public class Hello extends TagSupport { private String name=null; /** * Setter for the attribute name as defined in the tld file for this tag */ public void setName(String value) { name = value; } /** * Getter for the attribute name as defined in the tld file for this tag */ public String getName() { return(name); } /** * doStartTag is called by the JSP container when the tag is encountered */ public int doStartTag() { try { JspWriter out = pageContext.getOut(); if(name != null) out.println("Hello " + name); else out.println("Hello World "); } catch (IOException ex) { throw new JspTagException("I/O Exception " + e.getMessage()); } // Must return SKIP_BODY because we are not supporting a body for this tag. return SKIP_BODY; } } Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 8 The JSP with custom tags The JSP Once your TLD and tag handlers are created, you can begin accessing the tags in your JSP. You declare that a JSP page will use tags defined in a tag library by including a taglib directive in the page before any custom tag is used. The prefix attribute is a shortcut to referencing the library throughout the page. Sample hello.jsp: <%@ taglib uri="/tut" prefix="sample" %> <html> <head> <title>Hello World Demo</title> </head> <body> <hr/> <sample:hello name="<%= request.getParameter(\"name\") %>"/> <hr/> </body> </html> The HTML source output from this JSP would look like: <html> <head> <title>Hello World Demo</title> </head> <body> <hr/> Hello Don Leflar <hr/> </body> </html> You can see how the tag was evaluated and the contents of the tag inserted into the output stream. Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 9 Section 3. How Tag Libraries work Page Translation What happens during a JSP Page(with custom tags) Translation? Page Execution What happens during a JSP Page(with custom tags) execution? Tag Life Cycle The Life Cycle of a Simple Tag Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 10 BodyTag Life Cycle The Life Cycle of a Body Tag Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 11 Section 4. Example: HTML Encoding A Custom Tag for HTML Encoding package com.tsg.taglibs; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.IOException; import java.util.Hashtable; public class HTMLEncodeTag extends BodyTagSupport { static private Hashtable translations = makeTranslationTable(); static private Hashtable makeTranslationTable () { Hashtable table = new Hashtable(); table.put(new Character('<'), "<"); table.put(new Character('>'), ">"); table.put(new Character('&'), "&"); table.put(new Character('\n'), "<BR/>"); table.put(new Character(' '), " "); return table; } static public String getTranslation (char c) { return (String) translations.get(new Character(c)); } public int doAfterBody () throws JspException { BodyContent body = getBodyContent(); String orig = body.getString(); body.clearBody(); int length = orig.length(); StringBuffer result = new StringBuffer(Math.round(length * 1.1f)); for (int i = 0; i < length; ++i) { char c = orig.charAt(i); String translation = getTranslation(c); if (translation == null) { result.append(c); } else { result.append(translation); } } try { getPreviousOut().print(result.toString()); } catch (IOException e) { throw new JspException("unexpected IO error"); } return SKIP_BODY; } } The TLD and the web.xml file The TLD <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"> Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 12 <taglib> <tlibversion>1.0</tlibversion> <jspversion>1.1</jspversion> <shortname>HTMLFormat</shortname> <uri>HTMLFormat</uri> <info>HTML Formatting Tags </info> <tag> <name>HTMLEncode</name> <tagclass>com.tsg.taglibs.HTMLEncodeTag</tagclass> <info> Encode HTML</info> </tag> </taglib> The web.xml file <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd"> <web-app> <taglib> <taglib-uri> HTMLFormat </taglib-uri> <taglib-location> /WEB-INF/HTML_Format_Tags_1_4.tld </taglib-location> </taglib> </web-app> Using the HTML Encode Tag The JSP <%@ taglib uri="HTMLFormat" prefix="HTMLFormat" %> <html> <head> </head> <body> <pre> <HTMLFormat:HTMLEncode> < Sample HTML Encode Tag Output > </HTMLFormat:HTMLEncode> </pre> </body> </html> Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 13 The Output The input: < Sample HTML Encode Tag Output > will be output as: < Sample HTML Encode Tag Output > and hence, will be displayed as: < Sample HTML Encode Tag Output > Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 14 Section 5. Example: Simple Iteration A Custom Tag for Simple Iteration package com.tsg.taglibs; import java.io.IOException; import java.util.Iterator; import java.util.Collection; import javax.servlet.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; /** * This class is a custom action for looping through the elements * of an Iterator. The action body is evaluated once for each element. */ public class LoopTag extends BodyTagSupport { // Property variables private String name; private String loopId; private String className; // Iterator, that is used for iteration private Iterator iter; /** * Sets the name attribute, i.e. the name of the variable holding * a reference to the iterator. * * @param name the bean variable name */ public void setName(String name) { this.name = name; } /** * Sets the loopId attribute, i.e. the name of the variable to * hold the element reference in the body. * * @param property the property name */ public void setLoopId(String loopId) { this.loopId = loopId; } /** * Sets the class attribute, i.e. the name of the class for the * multi-value property elements. * * @param class the element class name */ public void setClassName(String className) { this.className = className; } /** * Extracts the Iterator and makes the first element available to * the body in a variable with the name specified by the loopId * attribute. */ public int doStartTag() throws JspException { // Extract the Iterator object Object obj = pageContext.findAttribute(name); if (obj == null) { throw new JspException("Variable " + name + " not found"); } if (Collection.class.isAssignableFrom(obj.getClass())) { Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 15 Collection col = (Collection) obj; iter = (Iterator) col.iterator(); } // Set the first loop value, if any if (iter != null && iter.hasNext()) { Object currValue = iter.next(); pageContext.setAttribute(loopId, currValue); return EVAL_BODY_TAG; } else { return SKIP_BODY; } } /** * Makes the next element available to the body in a variable * with the name specified by the loopId attribute, or returns * SKIP_BODY if all elements have been processed. */ public int doAfterBody() throws JspException { try { BodyContent bc = getBodyContent(); getPreviousOut().print(bc.getString()); bc.clearBody(); } catch (IOException e) {} if(iter.hasNext()) { Object currValue = iter.next(); pageContext.setAttribute(loopId, currValue); return EVAL_BODY_TAG; } else { return SKIP_BODY; } } /** * Releases all instance variables. */ public void release() { name = null; loopId = null; className = null; iter = null; super.release(); } } The TEI package com.tsg.taglibs; import javax.servlet.jsp.tagext.*; /** * This class provides information about the variable created by the * LoopTag to the JSP container at translation-time. */ public class LoopTagExtraInfo extends TagExtraInfo { public VariableInfo[] getVariableInfo(TagData data) { return new VariableInfo[] { new VariableInfo(data.getAttributeString("loopId"), data.getAttributeString("className"), true, Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 16 VariableInfo.NESTED) }; } } The TLD <taglib> <shortname> tut </shortname> <uri> /tut </uri> <info> A simple tab library for TSG utilities. </info> <tag> <name>loop</name> <tagclass>com.tsg.taglibs.LoopTag</tagclass> <teiclass>com.tsg.taglibs.LoopTagExtraInfo</teiclass> <bodycontent>JSP</bodycontent> <info> Evaluates the body for each element in the Iterator </info> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>loopId</name> <required>true</required> </attribute> <attribute> <name>className</name> <required>true</required> </attribute> </tag> </taglib> The JSP and the output The JSP <%@ taglib uri="/tut" prefix="tut" %> <html> <body> <% java.util.ArrayList al = new java.util.ArrayList(); al.add("VWR"); al.add("AD"); al.add("Ben Franklin"); al.add("Wyeth"); al.add("Hawthorn"); Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 17 pageContext.setAttribute("al", al); %> List of Clients: <ul> <tut:loop name="al" loopId="clientName" className="java.lang.String"> <li> <%= clientName %> </li> </tut:loop> </ul> </body> </html> The output <html> <body> List of Clients: <ul> <li> VWR </li> <li> AD </li> <li> Ben Franklin </li> <li> Wyeth </li> <li> Hawthorn </li> </ul> </body> </html> Colophon This tutorial was written entirely in XML. IBM developerWorks Toot-O-Matic tutorial generator was used to translate this XML file into multiple formats. The Toot-O-Matic tool is an XSLT stylesheet and several XSLT extension functions that convert an XML file into a number of HTML pages, a zip file, JPEG heading graphics, and two PDF files. The ability to generate multiple text and binary formats from a single source file illustrates the power and flexibility of XML. I customized this tool by editing several XSLT style-sheets to suit my needs and to give it a Sycamore look and feel. Presented by Shireesh Thanneru http://thanneru.net An Introduction to JSP Custom Tags Page 18