# Tags That Interact - JSP

Tags that share data are referred to in the JSP specification as cooperating actions.There are several different ways that tags can communicate with each other, although the general idea is that a custom action can create an object, such as a JavaBean, for use by another action. For example, you might want to cache a result-set from a database query and have another action process it. One way would be for the first action to store the object as an attribute in the pageContext by using the setAttribute() method.When you do this you must be very careful that the name that you bound the object to is not already in use, or you will erase the reference to the existing object. Such a shared object is visible to scriptlet code through the pageContext.getAttribute() method as long as you know the name to which the object is bound, and the type to which you want to cast the retrieved reference.

Earlier in this chapter, in the example that used an empty tag with attributes (Listing ), the tag handler implementation contained some hard-coded HTML.This can be avoided by instead using a tag that exposes data so that the page author can display it with other tags in any way that she sees fit. For example, the handler in Listing 9.5 could have exposed an array of values.Then, the page author could iterate through the array with a c:forEach action, displaying each element with any formatting she chooses.

Two main approaches can be taken to help tags cooperate: scripting variables and nested scope.

Scripting Variables

One way that you can share objects between actions is with the use of scripting variables. A tag handler can export a JavaBean, or any other object, with the pageContext.setAttribute() method. Another tag can then access the JavaBean through the pageContext.getAttribute() method.

You can describe the exported object by writing a subclass of TagExtraInfo, although for simple variables you can use a block within the TLD file. For example, consider the JSP in Listing . It uses a custom action called defineObjects, which is the action name recommended by the JSP specification for an action that creates objects for use on the page. In this case, the defineObjects action exports an array of random numbers.The size of the array is set by the value of the howMany attribute.

Listing A JSP That Uses an Action That Exports a Scripting Variable

<%@ taglib uri=”/chapter09TagLib” prefix=”chapter” %><%@ taglib uri=”http://java.sun.com/jstl-el/core” prefix=”c” %> <html><head><title>Chapter- A custom tag: scripting variable</title></head><body>

This page uses a custom tag that has no body content, but takes an attribute called “howMany” that specifies how many random numbers to generate. The random numbers are exported through an array whose name is set by the “name” attribute. Here is its output:

<chapter:defineObjects howMany=”5”name=”numbers” /><ul><c:forEach items=”${numbers}”var=”currentNumber”><li> <c:out value=”${currentNumber}” /></li></c:forEach></ul></body></html>

The line that generates the array of random numbers and exports it under the name numbers is <chapter09:defineObjects howMany=”5” name=”numbers” />

If you have read the rest of the listing already, you will have seen that the JSTL c:foreach action uses the numbers scripting variable and exposes each element of the array as a variable called currentNumber:

<c:forEach items=”${numbers}”var=”currentNumber”> Each number from the array is then displayed in a list with the JSTL c:out action:<li><c:out value=”${currentNumber}”/></li>

Before you see the Java code, and the TLD file, take a look at the screenshot in Figure

A JSP that uses a custom action that exports a scripting variable.

The Java code that implements the tag handler for the custom action is in ListingListing Tag Handler Code for thedefineObjects Custom Action

The name attribute of the defineObjects action is implemented in the usual way, except the value is stored in an instance variable called exportedArrayName.This is just to show that the attribute name of the custom action does not have to match the name of a field in the tag handler implementation.

The important part is at the end of the doStartTag() method:

pageContext.setAttribute(exportedArrayName, outputArray);

The question is, “So what is different between manually exporting a variable by using pageContext.setAttribute()from a scriptlet, and this approach?”The answer is that you can make an addition to the tag library’s TLD that saves the page author from having to call pageContext.getAttribute() to access the exported variable.That is, the variable is implicitlyavailable.The requisite changes look like this:

Child Elements of the variable Element

Hierarchical Tag Structures

Apart from using scripting variables, yet another way that actions can communicate is through the use of nested scoping.The way that it works is that an enclosing, or parent, action can make data and JavaBeans available to nested, or child, actions. For page authors, this is a logical approach to sharing data because they tend to be familiar with using certain tags within other tags. For example, <li> is used within <ul> to create an unordered list of items. Even for software developers, the switch statement in Java has case and default constructs, which is mirrored in JSTL’s c:choose, c:when, and c:otherwise actions.

The data that is exposed by the enclosing action is made available when the enclosing action’s doStartTag() method is called, and removed when the doEndTag() method is invoked.To understand how the nested action accesses the exposed data, remember that a tag handler has its setParent() method invoked early in its lifecycle to allow it to store a reference to its enclosing tag.The getParent() method is used by the following method that is declared by the TagSupport class:

public static final Tag findAncestorWithClass(Tag from, java.lang.Class class)

You can use this method to find an enclosing tag handler of a certain type.The first parameter is the tag to begin searching from, and the second parameter is the tag handler type.

Let’s put all this together with an example.This example uses an enclosing action that computes the average of two numbers supplied through attributes, and exposes the result as a nested scoped variable. A nested action retrieves the average value and displays it. The JSP for this example is in Listing Listing A JSP That Uses Nested Actions(averager.jsp)

The enclosing action is called averager, and the two numbers to compute the average of are set by the number1 and number2 attributes.The nested action is displayResult, and is the tag responsible for retrieving the average value from its enclosing action and displaying it.The Java code for the tag handler for the enclosing action is in Listing ListingThe Tag Handler for theaverager Action (Averager.java)

the vast majority of this tag handler is for storing the two attributes (number1 and number2) and the computed average value (averageValue).The average value is calculated in the doStartTag() method, which returns EVAL_BODY_INCLUDE so that the body content is evaluated and passed to the output.The doEndTag() method is not overridden because the inherited version from the parent TagSupport class returns EVAL_PAGE to indicate that the rest of the page should be evaluated, which is exactly what we want.

The tag handler for the nested action, displayResult, can be found in Listing Listing The Tag Handler Implementation for thedisplayResult Action

There are three important lines of code in Listing 9.15, the first being the most important of all:

Averager parentTag =(Averager)findAncestorWithClass(this,Averager.class);

This line of code is the one that uses the getParent() method to find the enclosing tag that is of type Averager. In this example, it is the immediately enclosing action.However, in more complex examples, the findAncestorWithClass() method searches all the way up to the top-level action until the required action is found. If the specified action cannot be located, a value of null is returned instead.

After the correct enclosing action has been located, the other two important lines of code are

double averageValue =parentTag.getAverageValue(); which retrieves the average value, and: pageContext.getOut().print(“The average value is “+averageValue);

which writes it out. For completeness, here are the lines that must be added to the TLD file: