Tuesday, November 10, 2009

Keep Your Complexity Out of My JSP View - JSP Functions



The JSTL library comes with built in functions that are very useful. Hopefully these functions meet your needs, but sometimes you need to write your own. Sometimes you might want to expose some static util functions to your page. Well with JSP 2.0 you can write you own.

In my example I'm going to write a debug tag to be able to print out session and request variables. It also gives you a convenient point to set a debug point so you can inspect context variables.

The first thing to do is to write a tld file. This is just like we do for old JSP tags. I put the tld under WEB-INF/tld/ and I will reference that path when I declare it in the JSP page.

Here is my tld file
The main parts are under the function node. There is the name that you will use to call the function, the function-class is the java class that contains the function and the function-signature is the signature of the function. We need to use fully qualified names for the return and parameters.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<jsp-version>2.1</jsp-version>
<short-name>fnt</short-name>
<uri>http://daneking.blogspot.com</uri>
<description>A debug function</description>
<function>
<name>debugPage</name>
<function-class>
viewhelper.JSPMethodCall
</function-class>
<function-signature>
java.lang.String callFactoryMethod(javax.servlet.jsp.PageContext)
</function-signature>
</function>
</taglib>



Now here is the java code to create a string of the session and request variables. I bolded the call to the implementation of the my method.

package viewhelper;

import java.util.Enumeration;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.PageContext;

public class JSPMethodCall {

private static final String CR = "<br/>";

private static String printSessionAttributes(HttpSession s,String header){
StringBuffer sb=new StringBuffer(header);
sb.append(CR);
Enumeration e=s.getAttributeNames();
while(e.hasMoreElements()){
String var=(String)e.nextElement();
sb.append(var);
sb.append(" = " + s.getAttribute(var));
sb.append(CR);
}
return sb.toString();

}
private static String printRequestAttributes(ServletRequest r,String header){
StringBuffer sb=new StringBuffer(header);
sb.append(CR);
Enumeration e=r.getAttributeNames();
while(e.hasMoreElements()){
String var=(String)e.nextElement();
sb.append(var);
sb.append(" = " + r.getAttribute(var));
sb.append(CR);
}
return sb.toString();

}
public static String callFactoryMethod(PageContext ctx){
HttpSession session =ctx.getSession();

StringBuffer sb=new StringBuffer(CR + "--- Debug Values ---" + CR);
sb.append(printSessionAttributes(session,"---Session ---"));
ServletRequest servletRequest = ctx.getRequest();
sb.append(printRequestAttributes(servletRequest,"---Request ---"));
return sb.toString();
}

}


And the finally the call from the JSP page. I've bolded the important parts where we call the function and the declaration of the tld page where we defined our function.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="/WEB-INF/tlds/fntags.tld" prefix="fnt" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Calling Functions</title>
</head>
<body>
<c:set scope="session" var="sessionVar" value="sessionValue"/>
<c:set scope="request" var="requestVar" value="requestValue"/>
This is my function .....
${fnt:debugPage(pageContext)}

</body>
</html>


Many times we get so caught up in the latest widgets and technologies that we don't look at some of the basic things we can do to refactor out som complexity out of our views. Hopefully this is another tool to help you do that.