Java Servlet Programming, 2nd Edition > 18. JavaServer Pages > 18.8 Custom Tag Libraries |
18.8 Custom Tag LibrariesTo conclude, let's look at the most interesting aspect of JavaServer Pages: the support for custom tag libraries. Custom tags (aka custom actions) let a JSP page contain XML tags that, although they look like HTML, actually cause specific Java code to be executed when a tag is encountered. The possible uses for custom tag libraries are quite exciting. For example, Live Software (creators of JRun) used JSP custom tags to implement a 100% portable version of the Allaire Cold Fusion tags called CF_Anywhere. (Shortly thereafter, Live Software was purchased by Allaire.) The details on how to write a custom tag library are fairly involved and extend beyond the scope of this chapter. Fortunately, several open source tag libraries are under development and already useful. The two most prominent are Apache Taglibs and Apache Struts, both from the Apache Jakarta project at http://jakarta.apache.org. Apache Taglibs holds general-purpose tag libraries; Apache Struts holds a tag libary intended to support a Model 2-style architecture, but many of the tags are generally useful. For a list of JSP tag libraries see http://jsptags.com. There's also an effort to create a standard tag library as a formal Java specification request process as JSR-052. For more information on JSR-052 see http://java.sun.com/aboutJava/communityprocess/jsr/jsr_052_jsptaglib.html. 18.8.1 Using Custom Tag LibrariesCustom actions allow the embedding of application logic into JSP pages using HTML-like tags. With the right set of tags, logic that previously had to be written using scriptlets can instead be written using tags. For example, the Apache Struts project has an <iterate> tag we can use to replace a for loop. The <iterate> tag repeats the body of the tag once for each element of a collection (List, Set, Vector, Map, array, etc.). During each iteration it puts the element into page scope for use within the body.[6] Struts also has a <property> tag that operates like <jsp:getProperty> with the extra enhancement that it filters the content for HTML display by converting all HTML special characters into their appropriate character entities. For example, < becomes %lt;.
Using <iterate> and <property> together we can write the following page snippet to display all the cookies sent in the request with all special characters properly filtered: <%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %> <struts:iterate collection="<%= request.getCookies() %>"> <struts:property name="cookie" property="name" /> = <struts:property name="cookie" property="value" /> <BR> </struts:iterate> To install and use a tag library like Struts you must do several things:
18.8.2 A Tool Application Using Custom Tag LibrariesUsing the <iterate> and <property> tags from Struts we can simplify our toolview.jsp page. We'll also take this opportunity to demonstrate the servlet-driven Model 2 architecture in which a servlet receives the request, adds attributes to the request object, then dispatches the request to a JSP that acts like a template. Example 18-12 shows the controller servlet. Example 18-12. A Model 2 Servlet Controllerimport java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class ToolServlet extends HttpServlet { Tool[] tools = null; public void init() throws ServletException { // Load the tool data in our init for simplicity String toolsFile = getServletContext().getInitParameter("toolsFile"); // from web.xml if (toolsFile == null) { throw new ServletException("A tools data file must be specified as " + "the toolsFile context init parameter"); } log("Loading tools from " + toolsFile); try { tools = Tool.loadTools(toolsFile); if (tools.length == 0) { log("No tools found in " + toolsFile); } else { log(tools.length + " tools found in " + toolsFile); } } catch (Exception e) { throw new ServletException(e); } } public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { Tool[] tools = null; // Place an appropriate "tools" attribute in the request String state = req.getParameter("state"); if (state == null) { req.setAttribute("tools", getTools()); } else { req.setAttribute("tools", getTools(state)); } // Send the request to the JSP for processing RequestDispatcher disp = req.getRequestDispatcher("/toolview-tag.jsp"); disp.forward(req, res); } public Tool[] getTools() { return tools; } public Tool[] getTools(String state) { List list = new LinkedList(); for (int i = 0; i < tools.length; i++) { if (tools[i].getStateFlag().equalsIgnoreCase(state)) { list.add(tools[i]); } } return (Tool[]) list.toArray(new Tool[0]); } } This servlet behaves similarly to the ToolApp in Tea and ToolServlet in WebMacro. It loads the tool information in its init( ) method, and on each request places an appropriate subset of tools into the request object. The servlet forwards each request to a JSP for actual rendering, and the JSP can gain access to the tools in the request object using the <jsp:useBean> tag. Example 18-13 demonstrates the JSP named toolview-tag.jsp . Example 18-13. The Tool Application Rewritten Using Tag Libraries<%-- toolview-tag.jsp --%> <%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %> <% String title = "Tool Listing"; String deck = "A list of content creation tools"; String desc = "Without tools, people are nothing more than animals. And " + "pretty weak ones at that. Here's a list of servlet-based " + "content creation tools you can use so you won't be a " + "servlet weakling."; %> <%@ include file="/header.jsp" %> <%@ page session="false" %> <%@ page errorPage="/errorTaker.jsp" %> <%-- Fetch the tools array as a request attribute --%> <jsp:useBean scope="request"/> <struts:iterate collection="<%= tools %>"> <HR SIZE=2 ALIGN=LEFT> <H3> <%-- Automatically HTML-escapes values --%> <struts:property name="tool" property="name" /> <% if (((Tool)tool).isNewWithin(45)) { %> <FONT COLOR=#FF0000><B> (New!) </B></FONT> <% } else if (((Tool)tool).isUpdatedWithin(45)) { %> <FONT COLOR=#FF0000><B> (Updated!) </B></FONT> <% } %> </H3> <A HREF="<struts:property name="tool" property="homeURL"/>"> <struts:property name="tool" property="homeURL"/></A><BR> <%-- Assume don't want HTML in comments --%> <struts:property name="tool" property="comments" /> </struts:iterate> <%@ include file="/footer.jsp" %> This page first uses the taglib directive to load the Struts custom tag library. Don't forget this step! If you do forget, you won't get a direct error message but things won't work. The reason is that without the taglib directive the <struts:iterate> and <struts:property> tags look to the server like any other HTML tags and they're treated like page content to be output instead of page logic to be executed! The body of the page uses the <struts:iterate> tag to loop over the array of tools and the <struts:property> tag to display each tool's name, URL, and comments. If the comments are allowed to contain HTML markup then the proper tag to use would be <jsp:getProperty> so the HTML markup isn't filtered away. Using the common tags from Apache Struts removes some more code from the HMTL page, but a few scriptlets remain. To remove all the scriptlets would require creating a custom tag to perform the timestamp comparison. Unfortunately, because of the many details involved in writing custom tags, we can't include a tutorial here. If you're interested in learning more on creating custom tags, see Hans Bergsten's book JavaServer Pages (O'Reilly).
|