ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

JSP 2.0: The New Deal, Part 4
Pages: 1, 2

Processing the Custom Action Body

Just like a tag handler class written in Java, a tag file can ask the container to evaluate the custom action element body and, optionally, further process the evaluation result.



Let's make it possible to add a short description of the poll question in the action element body, like this:

<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %>
...
<html>
   ...
   <body bgcolor="white">
      ...
      <p>
        <my:poll question="Will you start using tag files?" 
          answers="${myAnswers}"
          answersMapName="myAnswers" votesMapName="myVotes" >
          JSP 2.0 introduces a new way to develop custom action
          tag handlers, called <i>tag files</i>
        </my:poll>
      </p>
      ...
   </body>
</html>

The body in this example only contains text, but it could also contain action elements and EL expressions. To evaluate the body and add the result to the response, we need to modify the poll tag file like this:

<%@ tag body-content="scriptless" %>
<%@ attribute name="question" required="true" %>
<%@ attribute name="answers" required="true" 
   type="java.lang.Object" %>
<%@ attribute name="votesMapName" required="true" %>
<%@ attribute name="answersMapName" required="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<p>
   <jsp:doBody/>
</p>
Question: ${question}<br>
<form action="result.jsp" target="result">
   <input type="hidden" name="question" value="${question}">
   <input type="hidden" name="votesMapName" value="${votesMapName}">
   <input type="hidden" name="answersMapName" value="${answersMapName}">
   <c:forEach items="${answers}" var="a">
      <input type="radio" name="vote" value="${a.key}">${a.value}<br>
   </c:forEach>
   <input type="submit" value="Vote">
</form>

First we change the tag directive body-content attribute value to scriptless. As I mentioned earlier, this means that the body can contain any kind of content except scripting elements. Next, we add a <jsp:doBody> action, which tells the container to evaluate the body and add the result to the response. Alternatively, you can use the var attribute, capture the evaluation result, and process it further.

In addition to the features I've described here, a tag file can return information back to the invoking file through variables, access undeclared attributes, and have fragment attributes (i.e., attributes holding action elements and EL expressions that can be evaluated by the tag file in a way similar to how it evaluates the action element body). You can read all about these features in Chapter 11 of my JSP book, which is the sample chapter available online.

A Simpler, Java Tag-Handler API

Being able to write a custom action tag handler as a tag file is a great new feature, especially for custom actions that generate a lot of HTML. But some things are hard to do with just JSP actions and EL expressions, so the Java tag-handler API is still needed. Prior to JSP 2.0, writing a tag handler in Java could be quite complex, due to the tricky interaction between the container and the tag handler needed to process the action element body. This complexity is required in order to support Java scripting elements in the action element body. However, if the body contains only template text, EL expressions, and action elements, a much simpler API can be designed. That's exactly what was done for JSP 2.0, and it's appropriately named the simple tag handler API.

Here's a Java tag handler for the poll custom action we implemented as a tag file earlier:

package com.mycompany.mylib;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class PollTag extends SimpleTagSupport {
   private String question;
   private Map answers;
   private String votesMapName;
   private String answersMapName;

   public void setQuestion(String question) {
      this.question = question;
   }

   public void setAnswers(Map answers) {
      this.answers = answers;
   }

   public void setVotesMapName(String votesMapName) {
      this.votesMapName = votesMapName;
   }

   public void setAnswersMapName(String answersMapName) {
      this.answersMapName = answersMapName;
   }

   public void doTag() throws JspException, IOException {
      JspWriter out = getJspContext().getOut();
      JspFragment body = getJspBody();
      if (body != null) {
         out.println("<p>");
         body.invoke(null);
         out.println("</p>");
      }
      out.print("Question:");
      out.print(question);
      out.println("<br>");
      out.println("<form action=\"result.jsp\" target=\"result\">");
      out.print("<input type=\"hidden\" name=\"question\" value=\"");
      out.print(question);
      out.println("\">");
      out.print("<input type=\"hidden\" name=\"votesMapName\" value=\"");
      out.print(votesMapName);
      out.println("\">");
      out.print("<input type=\"hidden\" name=\"answersMapName\" value=\"");
      out.print(answersMapName);
      out.println("\">");
      Iterator i = answers.keySet().iterator();
      while (i.hasNext()) {
         String key = (String) i.next();
         String value = (String) answers.get(key);
         out.print("<input type=\"radio\" name=\"vote\" value=\"");
         out.print(key);
         out.print("\">");
         out.print(value);
         out.println("<br>");
      }
      out.println("<input type=\"submit\" value=\"Vote\">");
      out.println("</form>");
   }
}

A simple tag must implement the new javax.servlet.jsp.tagext.SimpleTag interface. The example tag handler does so by extending the javax.servlet.jsp.tagext.SimpleTagSupport class, which provides default implementations of all methods. Just like a classic tag handler class, you need setter methods for each custom action attribute, but there's only one processing method to implement: the doTag() method.

In the example tag handler, the doTag() method tries to get an executable representation of the action element body (a JspFragment instance) by calling the getJspBody() method inherited from the SimpleTagSupport class. If there is a body, the tag handler invokes it with a null value as the argument to the invoke() method, which means that the evaluation result is added to the response. Just as for the <jsp:doBody> action, you can capture the result instead by passing a Writer instance to the invoke() method. The doTag() method then writes the HTML for the form with radio buttons for all alternative answers, just as the tag file implementation does.

Because there's only one method that the container calls to let the tag handler do its thing, instead of three methods in a classic tag handler that process its body (doStartTag(), doAfterBody(), and doEndTag(), each with a return value telling the container what to do next), implementing a tag handler as a SimpleTag is considerably easier than with the classic tag-handler API. In addition, simple tag-handler instances are never reused, so you don't have to worry about resetting state -- a subject that has turned out to cause a lot of problems, described in one of my earlier articles, "JSP 1.2: Great News for the JSP Community, Part 2".

You declare a simple tag handler in a TLD in exactly the same way that you declare a classic tag handler, but the <body-content> TLD element must have a value other than JSP, since scripting elements are not allowed in the element body for a custom action backed by a simple tag handler. Other than that, using a custom action implemented as a simple tag handler is no different than using one implemented as a classic tag handler.

Both simple and classic tag handlers can support undeclared attributes by implementing a new interface called javax.faces.jsp.tagext.DynamicAttributes. Attributes for both types can be of type JspFragment for attributes containing other actions or EL expressions, evaluated any number of times by the tag handler. You can read more about these features in my book JavaServer Pages, 3rd Edition.

Conclusion

In this series, I've shown you all of the new stuff that JSP 2.0 adds: the expression language, better error handling, new configuration options, improved XML page format, and two new ways to develop tag handlers. All in all, these improvements make it possible to develop JSP pages that are both easy to understand and to maintain.

If you want to try out the new JSP 2.0 features, I recommend that you use Apache Tomcat 5, one of the first JSP containers to implement the new specification. Tomcat 5 is available at the Jakarta Project site.

Hans Bergsten is the founder of Gefion Software and author of O'Reilly's JavaServer Pages, 3rd Edition.


Return to ONJava.com.