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

advertisement

AddThis Social Bookmark Button

Developing Custom Tag Libraries as Tag Files
Pages: 1, 2, 3

Processing the Action Body

So far, the tag files we've looked at ignore (or actually forbid) the body of the custom action element used to invoke them, but the body is often an important part of the equation. One example is a conditional custom action, such as a variation of the <c:if> JSTL action. It needs to process the body if the condition is true. Another example is a custom action that transforms the body in some way or simply uses it as input.



Let's develop a custom action that transforms its body content. It first converts all characters that have special meaning in HTML and XML to the corresponding character entity codes (e.g., < to &lt;), and then converts special proprietary codes into HTML elements. A custom action like this can be used to process user input in an online forum to protect it against cross-site scripting attacks while still allowing for limited formatting of the messages. Here's how you can use this custom action in a JSP page:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<%-- Create test data --%> 
<c:set var="message"> 
This is just a lot of text that the browser will format to 
fit the browser window. Attempts to <blink> add HTML elements 
are dealt with by conversion to character entities. 
[code] 
This part I want the browser to leave alone, so that 
all my indentations are left intact: 
public class Foo { 
public String getBar( ) { 
return bar; 
} 
} 
[/code] 
And then some regular text again. 
</c:set> 
<html> 
<head> 
<title>Online Forum</title> 
</head> 
<body bgcolor="white"> 
<h1>Online Forum</h1> 
Here's a formatted message: 
<p> 
<my:htmlFormat> 
${message} 
</my:htmlFormat> 
</p> 
</body> 
</html> 

This page first saves test data containing text, an HTML element, and the proprietary formatting codes in a variable named message. In a real application, the text would likely come from a database or some other external source. It then processes the text with the <my:htmlFormat> custom action.

The result is shown in Figure 11-1. Note how the <blink> HTML element is displayed instead of causing most of the text to blink, and how the formatting is preserved for all text between the proprietary [code] and [/code] tags.


Figure 11-1. Result of text processing with a custom action

Example 11-4 shows the tag file that implements the <my:htmlFormat> custom action.

Example 11-4. Processing the body (htmlFormat.tag)

<%@ tag body-content="scriptless" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%-- Capture the body evaluation result in a variable --%>
<jsp:doBody var="bodyRes" />

<%-- Convert special characters to character entities --%>
<c:set var="escapedBody" value="${fn:escapeXml(bodyRes)}" />

<%-- Replace "[code]/[/code]" with "
/
" --%> <c:set var="convBody" value="${fn:replace(escapedBody, '[code]', '
')}" />
<c:set var="convBody" 
  value="${fn:replace(convBody, '[/code]', '
')}" /> <%-- Output the result --%> ${convBody}

Note that the tag directive in Example 11-4 sets the body-content attribute to scriptless. As I mentioned earlier, this means that the page author is allowed to put template text, standard actions and custom actions, in the body but not scripting elements (i.e., Java code).

It's after the directive elements that this example gets interesting; here's a standard action that we have not discussed before: <jsp:doBody>. This action can only be used in tag files. It evaluates the body of the custom action element, meaning that all action elements (if any) in the body are called and the output they produce is mixed with the template text (if any). The result is saved in a variable, using the var attribute to name the variable. This attribute is optional, as shown in Table 11-1, and you can use the varReader attribute as an alternative. If you don't specify any of these attributes, the result is added to the page invoking the custom action.

Table 11-1. Attributes for <jsp:doBody>

Attribute name Java type Dynamic value accepted Description
var String No Optional. The name of the variable to hold the body evaluation result as a String.
varReader String No Optional. The name of the variable to hold the body evaluation result as a java.io.Reader.
scope String No Optional. The variable scope; one of page, request, session, or application. Default is page.

The difference between the var and varReader attributes is the type of Java object used for capturing the result. The var attribute captures it as a String and is sufficient for most cases. When the varReader attribute is used, the result is captured as a java.io.Reader object instead. For large results, this can be slightly more efficient when combined with an action or function for the transformation that reads its input from a Reader. Along with one of var or varReader, you can also specify the scope for the variable with the scope attribute.

The rest of the tag file in Example 11-4 transforms the captured body. First it uses the JSTL fn:escapeXml( ) function to convert all special characters to character entity codes, and then it replaces all occurrences of [code] and [/code] with the HTML <pre> and </pre> tags using the JSTL fn:replace( ) function, to preserve formatting in these sections. Finally, the converted body evaluation result is added to the calling page with a simple EL expression.

Processing Fragment Attributes

Processing the custom action body is easy and powerful as you can see, but wait, there's more! The custom action body is actually just a special case of what's called a JSP fragment in the JSP specification. A JSP fragment is an executable representation of a set of dynamic elements (actions and EL expressions), optionally mixed with template text. When the tag file invokes the fragment, all the dynamic elements in the fragment are executed. Since the elements have access to the current values of all scoped variables, the result typically differs from invocation to invocation, and the tag file can invoke it any number of times (e.g., once or none for a conditional action or multiple times for an iteration action).

In Example 11-4, the <jsp:doBody> action invokes the special fragment representing a custom action element body, but named fragments can also be provided as custom action attributes and be invoked by the tag file. Such fragments are invoked with the <jsp:invoke> action, described in Table 11-2.

Table 11-2. Attributes for <jsp:invoke>

Attribute name Java type Dynamic value accepted Description
fragment String No Mandatory. The name of the fragment to invoke.
var String No Optional. The name of the variable to hold the body evaluation result as a String.
varReader String No Optional. The name of the variable to hold the body evaluation result as a java.io.Reader.
scope String No Optional. The variable scope; one of page, request, session, or application. Default is page.

Let's develop a variant of the JSTL <c:forEach> action to illustrate how you can use named fragments. Say you want to loop through all the elements in a collection to generate an HTML table, and you want to render even rows one way and odd rows another. Here's a page that solves this problem by using a custom action with separate fragment attributes for even and odd rows:

<%@ page contentType="text/html" %> 
<%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<html> 
<head> 
<title>Even and Odd Rows</title> 
</head> 
<body bgcolor="white"> 
<h1>Even and Odd Rows</h1> 
<table> 
<my:forEvenAndOdd items="a,b,c,d,e"> 
<jsp:attribute name="even"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="red"><td>${counter}: Even Row</td></tr> 
</jsp:attribute> 
<jsp:attribute name="odd"> 
<c:set var="counter" value="${counter + 1}" /> 
<tr bgcolor="blue"><td>${counter}: Odd Row</td></tr> 
</jsp:attribute> 
</my:forEvenAndOdd> 
</table> 
</body> 
</html>

A fragment attribute value is defined using the <jsp:attribute> action introduced earlier. The body of this action element makes up the content of the fragment. In the page shown here, each fragment attribute values contain a JSTL <c:set> action for incrementing a counter and HTML table row and cell elements for showing the counter's value plus the static text "Even Row" and "Odd Row", respectively. The fragments also set different row background colors to make the differences clear. The result of processing this page is shown in Figure 11-2.

Note how the current value of the counter page scope variable is used for each new row, and how the rows alternate between the even and odd fragments. Example 11-5 shows the tag file for the <my:forEvenAndOdd> custom action.


Figure 11-2. Representing even and odd rows as fragments

Example 11-5. Using fragment attributes (forEvenAndOdd.tag)

<%@ tag body-content="empty" %> 
<%@ attribute name="items" rtexprvalue="true" required="true" %> 
<%@ attribute name="even" fragment="true" required="true" %> 
<%@ attribute name="odd" fragment="true" required="true" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<c:forEach items="${items}" varStatus="status"> 
<c:choose> 
<c:when test="${status.count % 2 == 0}"> 
<jsp:invoke fragment="even" /> 
</c:when> 
<c:otherwise> 
<jsp:invoke fragment="odd" /> 
</c:otherwise> 
</c:choose> 
</c:forEach>  

The tag directive specifies that the body must be empty; in this example, it must only contain the <jsp:attribute> elements (no template text of other elements), and they are considered alternatives to regular element attributes, not body content.

To tell the container to use an executable fragment as the attribute value, the attribute must be declared as such. Note that the attribute directive's fragment attribute is set to true for both the even and odd attributes. Otherwise the container evaluates the <jsp:attribute> body once and sets the attribute to the resulting value, as described in Chapter 6.

After the directives in Example 11-5, JSTL actions are used to loop through the list of items, and decide whether it's an even or odd row. The <jsp:invoke> action then invokes the appropriate fragment. The result is what you see in Figure 11-2.

Pages: 1, 2, 3

Next Pagearrow