Thursday, February 24, 2005

JSP Custom Tags

Presumably I've been looking in the wrong places, but finding a simple example of creating a custom JSP tag eluded me. So, after much wrangling, here is my simple example:

test.jsp
<%@ taglib uri="/WEB-INF/my.tld" prefix="my" %>
<my:foo value="${some JSTL expression}"/>


my.tld
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>My Tags</short-name>
<tag>
<name>foo</name>
<tag-class>my.Foo</tag-class>
<attribute>
<name>value</name>
</attribute>
</tag>
</taglib>


Foo.java
package my;

import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

public class Foo
extends TagSupport
{
String _value;
public String getValue() { return _value; }
public void setValue(String value) { _value = value; }

public int doStartTag()
throws JspException
{
return SKIP_BODY;
}

public int doEndTag()
throws JspException
{
try
{
String result = (String) ExpressionEvaluatorManager.evaluate
(
"value", _value, String.class, this, pageContext
);
pageContext.getOut().print("[" + result + "]");
}
catch (IOException ex)
{
throw new JspException("problem generating output", ex);
}

return EVAL_PAGE;
}
}


Note that even this is not the simplest possible example in that I've added JSTL expression evaluation, and done so in a non-portable manner. It strikes me as such a useful capability that I've included it anyway. The gotchas are that in addition to the obvious (the requirement to be using Tomcat, the need to import the Apache-specific implementation class and the need to call evaluate()), you need to remember to include JSTL's standard.jar in your classpath when compiling Foo.java and, if you're using Jasper at compile time (i.e. deploying .class files made from .jsp files ahead of time instead of deploying .jsp files and letting your container compile them on demand), you'll need to make certain that Foo.class is in Jasper's classpath so that it can perform its validation of your custom tags.