Java Enterpise Platform - Tag Support
Motivation
JSP custom tags are great! Sure, custom tags reduce the time required to create a new web page, but I really love them for the increased maintainability and readability of the resulting JSP page. Sun has done a great job with the JSP API, making it super easy to create a new custom tag by extending the TagSupport and BodyTagSupport classes.
Still, though, I can't help but grimace when I notice that my beautiful JSPs came at a serious cost. While my custom tags often increase the readability of my JSPs, the code that implements the custom tag can often be a mess of concerns: java logic, HTML content and HTML formatting characters. Heavy sigh!
Well, there are several strategies for making JSP custom tag implementations more readable. For large tags, string templates are a good solution. For smaller tags, I provide a quick solution below that improves the readability of JSP custom tag implementations.
The Code
BigOhTagSupport is an abstract class that extends javax.servlet.jsp.tagext.TagSupport, providing simple utility methods to make child JSP tag classes easier to read and maintain.
/*
* Created on Mar 10, 2008 by davidwingate
*/
package com.bigohsoftware.common.web.tags;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;
import javax.servlet.jsp.tagext.TryCatchFinally;
public abstract class BigOhTagSupport extends TagSupport implements TryCatchFinally, Serializable
{
public BigOhTagSupport()
{
doFinally();
}
/**
* Print to JspWriter, handling the IOException.
* @param str
* @throws JspException
*/
protected void print(String str) throws JspException
{
try
{
pageContext.getOut().write(str);
}
catch (IOException ioe)
{
throw new JspTagException(ioe);
}
}
/**
* Convenience method that auto-formats an HTML line by pre-pending numTabs tab characters and post-pending a new line character.
* @param line
* @param numTabs
* @throws JspException
*/
protected void printLine(String line, int numTabs) throws JspException
{
for (int i = 0; i < numTabs; i++)
{
print("\t");
}
print(line);
print("\n");
}
/**
* Convenience method for obtaining the context path of the current web application.
* @return Returns the context path for the current web application.
*/
protected String getContextPath()
{
if (pageContext.getRequest() instanceof HttpServletRequest)
{
return ((HttpServletRequest) pageContext.getRequest()).getContextPath();
}
else
{
return null;
}
}
}
Discussion
The BigOhTagSupport class provides several convenient features, but the class' printing methods are of chief import in this tutorial. Whereas the print(String) method supports the printing of unformatted text to the page's JspWriter, the printLine(String, int) method also formats the input String to include some standard HTML formatting characters (\t and \n).
An Example: HeadTag
To see why extending BigOhTagSupport can result in more readable code. Consider a simple HeadTag, implemented both with and without BigOhTagSupport. First let's see what a HeadTag implementation might look like without BigOhTagSupport.
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import javax.servlet.jsp.tagext.TryCatchFinally;
public class HeadTag extends TagSupport implements TryCatchFinally, Serializable
{
private String title;
public HeadTag()
{
doFinally();
}
@Override
public int doStartTag() throws JspException
{
try
{
pageContext.getOut().write("<head>\n");
pageContext.getOut().write("\t<meta http-equiv='content-type' content='text/html; charset=utf-8' />\n");
pageContext.getOut().write("\t<title>" + title + "</title>\n");
}
catch (IOException ioe)
{
throw new JspException(ioe);
}
return EVAL_BODY_INCLUDE;
}
public void doCatch(Throwable arg0) throws Throwable
{
}
public void doFinally()
{
title = null;
}
@Override
public int doEndTag() throws JspException
{
try
{
pageContext.getOut().write("</head>\n");
}
catch (IOException ioe)
{
throw new JspException(ioe);
}
return EVAL_PAGE;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
}
Look at all of the \t and \n characters mingled in with the HTML content to be printed ... ick! Here's a cleaner version of the same functionality, extending BigOhTagSupport this time.
import javax.servlet.jsp.JspException;
public class HeadTag extends BigOhTagSupport
{
private String title;
public HeadTag()
{
doFinally();
}
@Override
public int doStartTag() throws JspException
{
printLine("<head>", 0);
printLine("<meta http-equiv='content-type' content='text/html; charset=utf-8' />", 1);
printLine("<title>" + title + "</title>", 1);
return EVAL_BODY_INCLUDE;
}
public void doCatch(Throwable arg0) throws Throwable
{
}
public void doFinally()
{
title = null;
}
@Override
public int doEndTag() throws JspException
{
printLine("</head>", 0);
return EVAL_PAGE;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
}
As you can see from the example above, extending BigOhTagSupport will allow you to separate out two concerns:
- What HTML content do I want to generate.
- How should I format that content (for a graphic designer, say).