ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Programming Jakarta Struts

Getting the Most Out of the Struts Tag Libraries

by Chuck Cavaness, author of Programming Jakarta Struts and the recently released Jakarta Struts Pocket Reference
07/30/2003

The popularity of JSP Custom Tags has been rapidly growing since they were first introduced in the JSP 1.1 specification. The Struts framework, which was introduced in 2000, includes a set of Tag libraries that are instrumental in harvesting the fruits of the Struts framework. This article looks at some of the ways to get more out of those tags and helps make sense out of a few of the more complicated tasks.

Reaping the Benefits of JSP Custom Tags

The problems with straight JSP have been well documented. With JSP for instance, it's very tempting to insert Java code into the JSP. This makes maintenance of the page somewhat more challenging, even if they do have a cool sounding name like "scriptlets." And reuse for JSPs is usually nothing more than copy and paste.

We all should know by now that JSP Custom Tags were created to solve these problems and a few others. They are easier to maintain because the logic is programmed in once place and the construction of web applications is made simpler because a non-Java designer can drop them into a JSP and instantly see the results. Most popular JSP editors such as DreamWeaver and WSAD support Custom Tag syntax and the ability to step through them in a debugger.

The Tags themselves are built using Java, of course. Inserting a Tag only requires embedding an XML-like fragment into a JSP. When a JSP that contains one or more tags is invoked, the servlet produced from JSP compilation calls out to the appropriate Tag handler instance to perform its logic. All invocations of the same Tag invoke the same code. The better application's containers will create a pool of tag instances and not create a new instance of the Java class for every request. This helps to improve performance in a manner similar to how an EJB container can reuse Stateless Session bean instances.

Related Reading

Jakarta Struts Pocket Reference
By Chuck Cavaness, Brian Keeton

Overview of the Struts Tag Libraries

The Struts framework provides a set of six built-in Tag libraries that allow you to build the view part of the MVC without embedding Java code directly within your application JSPs.

The six Struts libraries are:

The Bean Tags

The Tags within the Bean Library are used for creating and accessing JavaBeans and a few other general purpose uses. Although these tags work with any standard JavaBean, they are often used with Objects that extend the Struts ActionForm class. Table 1 lists the tags within the Bean Library.

Table 1. Tags within the Struts Bean Tag Library

Tag NameDescription
cookieDefine a scripting variable based on the value(s) of the specified request cookie.
defineDefine a scripting variable based on the value(s) of the specified bean property.
headerDefine a scripting variable based on the value(s) of the specified request header.
includeLoad the response from a dynamic application request and make it available as a bean.
messageRender an internationalized message string to the response.
pageExpose a specified item from the page context as a bean.
parameterDefine a scripting variable based on the value(s) of the specified request parameter.
resourceLoad a web application resource and make it available as a bean.
sizeDefine a bean containing the number of elements in a Collection or Map.
strutsExpose a named Struts internal configuration object as a bean.
writeRender the value of the specified bean property to the current JspWriter.

Two of the most often used Tags from Table 1 are the message and write Tags.

The HTML Tags

The Tags within the Struts HTML Tag Library are used to create input forms for your application. There are also a few other useful Tags used in the creation and rendering of HTML-based user interfaces. The Tags included within the HTML Library are shown in Table 2.

Table 2. Tags within the Struts HTML Tag Library

Tag NameDescription
baseRender an HTML <base> Element
buttonRender a Button Input Field
cancelRender a Cancel Button
checkboxRender a Checkbox Input Field
errorsConditionally display a set of accumulated error messages
fileRender a File Select Input Field
formDefine an Input Form
frameRender an HTML frame element
hiddenRender a Hidden Field
htmlRender an HTML <html> Element
imageRender an input tag of type "image"
imgRender an HTML img tag
javascriptRender JavaScript validation based on the validation rules loaded by the ValidatorPlugIn
linkRender an HTML anchor or hyperlink
messagesConditionally display a set of accumulated messages
multiboxRender a Checkbox Input Field
optionRender a Select Option
optionsRender a Collection of Select Options
optionsCollectionRender a Collection of Select Options
passwordRender a Password Input Field
radioRender a Radio Button Input Field
resetRender a Reset Button Input Field
rewriteRender an URI
selectRender a Select Element
submitRender a Submit Button
textRender an Input Field of Type text
textareaRender a Textarea Field
xhtmlRender HTML tags as XHTML

Most all of the Tags within the HTML Tag library must be nested within the Struts Form Tag.

The Logic Tags

The Logic Tag Library contains tags that are helpful with iterating through collections, conditional generation of output, and application flow. Table 3 lists the Tags within the Logic Library.

Table 3. Tags within the Struts Logic Tag Library

Tag NameDescription
emptyEvaluate the nested body content of this tag if the requested variable is either null or an empty string.
equalEvaluate the nested body content of this tag if the requested variable is equal to the specified value.
forwardForward control to the page specified by the specified ActionForward entry.
greaterEqualEvaluate the nested body content of this tag if the requested variable is greater than or equal to the specified value.
greaterThanEvaluate the nested body content of this tag if the requested variable is greater than the specified value.
iterateRepeat the nested body content of this tag over a specified collection.
lessEqualEvaluate the nested body content of this tag if the requested variable is greater than or equal to the specified value.
lessThanEvaluate the nested body content of this tag if the requested variable is less than the specified value.
matchEvaluate the nested body content of this tag if the specified value is an appropriate substring of the requested variable.
messagesNotPresentGenerate the nested body content of this tag if the specified message is not present in this request.
messagesPresent Generate the nested body content of this tag if the specified message is present in this request.
notEmptyEvaluate the nested body content of this tag if the requested variable is neither null, nor an empty string, nor an empty java.util.Collection (tested by the .isEmpty() method on the java.util.Collection interface).
notEqualEvaluate the nested body content of this tag if the requested variable is not equal to the specified value.
notMatchEvaluate the nested body content of this tag if the specified value is not an appropriate substring of the requested variable.
notPresentGenerate the nested body content of this tag if the specified value is not present in this request.
presentGenerate the nested body content of this tag if the specified value is present in this request.
redirectRender an HTTP Redirect.

The Nested Tags

The Nested Tags were added to Struts during development of the 1.1 release. They extend the existing Tags functionality by allowing the Tags to relate to each other is a nested fashion. This is most useful when dealing with Object graphs.

The Nested Tags don't add any additional functionality over the Struts standard Tags other than to support the nested approach. For each Tag in the Bean, HTML, and Logic libraries, there is an equivalent nested Tag.

The Template Tags

The Template Tag Library was created to reduce the redundancy found in most web applications. In most web sites, there are sections within multiple pages that are exactly the same. The header, menus, or footers are three obvious examples. Instead of duplicating the content in each page and having to modify all pages when something like the look and feel changes, Templates allow you to have the common content in one place and insert it where necessary.

However, since the Tiles framework was introduced, the Template Tags have been deprecated and developers are encouraged to use Tiles.

Tiles Library Tags

As mentioned earlier, the Tiles framework is now integrated into the core Struts framework. Tiles is similar to the Template Tags except that it adds much more functionality and flexibility. For instance, Tiles supports inheritance between Tiles and allows you to define layouts and reuse those layouts within your site. They also support different Tiles and layouts based on I18N and channel. The Tags with the Tiles Library are shown in Table 4.

Table 4. Tags within the Struts Tiles Tag Library

Tag NameDescription
addAdd an element to the surrounding list. Equivalent to 'put', but for list element.
definitionCreate a tile/component/template definition bean.
getGets the content from request scope that was put there by a put tag.
getAsStringRender the value of the specified tile/component/template attribute to the current JspWriter.
importAttributeImport Tile's attribute in specified context.
initComponentDefinitionsInitialize Tile/Component definitions factory.
insertInsert a tiles/component/template.
putPut an attribute into tile/component/template context.
putListDeclare a list that will be pass as attribute to tile.
useAttributeUse attribute value inside page.

Collections, Maps, and the Iterate Tag

There are often times within a Struts application that you must display a collection of objects to the user. The scenario usually goes something like this: for each item in the collection, display one or more attributes from the object. For example, in a page that renders a classic shopping cart, each Object represents one item that has been added to the cart. Figure 1 shows an example of a shopping cart page.

Shopping Cart
Figure 1. A Shopping Cart page showing multiple items

Using JSP without Struts, this functionality would most likely be accomplished with some rather unattractive looking Java Scriptlets using loops. With the Struts Iterate Tag, it becomes somewhat innocuous and much easier to manage over the lifetime of the application.

The following example code snippet shows what this might look like using the Iterate Tag from the Logic library.

Example 1. Logic Iterate Code for Shopping Cart in Figure 1

<logic:iterate id="cartItem" name="UserContainer" property="cart.items">
  
  <!-This Table Row will be repeated for each item in the user's cart -->

  <tr bgcolor="#ffffff">
    <!--Delete checkbox for item -->
    <td valign="top" align="middle">
      <input 
        type="checkbox" 
        value="<bean:write name="cartItem" property="id"/>" 
        name="deleteCartItem">
    </td>
      
    <!-- name and description for item -->
    <td valign=top align=left>
      <html:link 
        page="/action/viewitemdetail" 
        paramName="cartItem" 
        paramId="id" 
        paramProperty="id">
      
        <!-- Write out the name of the Item -->
        <bean:write name="cartItem" property="name"/>
      </html:link>
  
      <br>In Stock - Usually ships in 2-3 business days<br>
             Available for pickup at most stores
    </td>
                 
    <!-- Write out a Quantity Input Field -->
    <td valign="top" noWrap align="middle">
      <input 
        type="text"
        maxlength="3" 
        size="3"
        name="qty_<bean:write name="cartItem" property="id"/>"
        value="<bean:write name="cartItem" property="quantity"/>">
    </td>
                 
    <!-- Unit price for item -->
    <td valign="top" noWrap align="middle"> 
      $<bean:write name="cartItem" format="#,##0.00" property="unitPrice"/>
    </td>
                  
    <!-- Extended price for item -->
    <td valign="top" align="right">
      <table cellspacing="2" cellpadding="3" width="100%" border="0">
       <tbody>
         <tr>
           <td align="right" bgcolor="#ffcc00">
             $<bean:write 
                name="cartItem" 
                format="#,##0.00"
                property="extendedPrice"/>
           </td>
         </tr>
       </tbody>
      </table>
    </td>       
 </tr>
</logic:iterate>

Looking at this snippet from a JSP, you might be inclined to comment on how formidable it appears. You should however quickly recognize that with the exception of a few Struts tags, most of the text is HTML; which is how it should be.

Using collections is rather straightforward. However, if your data is for some reason stored within an instance of a java.util.Map, getting at the properties in the map is a little trickier, but nonetheless solvable with the Iterate Tag. Using the Iterate Tag, we can iterate through the Map and access each element. Normally each Object that you iterate over is an element of the underlying collection, a ShoppingCartBean, for example. However, when iterating over a Map instance, the Object is of type java.util.Map.Entry.

As the Struts documentation describes, Map.Entry has two properties:

Let's say you are using a Struts ActionForm (standard or a dynamic) and it contains a property named "usermap" that is declared as a Map and instantiated as a java.util.HashMap. In this Map, the key is a unique user ID and the value is a String representing the users encrypted password. Figure 2 illustrates what this would look like.

Data Structure
Figure 2. Data Structure for Example 2

The following code fragment shows an example of iterating through this Map and writing out the two values.

Example 2. Iterate Tag used with a Map

<logic:iterate id="user" name="userForm" property="usermap">
  <tr>
    <td>User Id: <bean:write name=" user" property="key"/></td>
    <td>Password: <bean:write name=" user" property="value"/></td>   
  </tr>
</logic:iterate>

As you can see from this JSP fragment, using the Iterate Tag with Maps is not much different than with Collections. The primary distinction is that of using the key and value attributes to access the data stored within the Map. Once you have references to this data, you can use it within the Iterate block just as you would with anything else.

Formatting Errors with the Errors Tag

Let's face it, as hard as you try to make GUIs user-friendly and bulletproof, end users make mistakes. One of the most common mistakes is incorrect data entry. Your applications need to be very careful when accepting input from users in order to ensure the integrity of the datastore is maintained; Struts applications are no exception. Data entered from the browser must be validated and if errors are found, information needs to be displayed to the user describing what the problems are and what steps to take next.

The Struts framework includes the ability to catch errors and populate an Errors collection that can be accessed from within the JSPs. This is made even easier when using the Validator framework. Whether you're doing programmatic or declarative validation, the Errors collection is inserted into the request and available to a JSP. You can use either the Messages or the Errors Tag (depending on which type of validation problems occurred) to access the messages to display to the users.

Both of the Messages and Errors Tags support an "id" attribute that is used to store the current error while looping through the collection of problems. In a very simple approach you can use the following within your JSP:

   <html:messages id="error">
      <li><bean:write name="error"/></li>
   </html:messages>

This fragment would print out each message from the problem collection inside an HTML list.

There's a header and footer entry that you can pull from the resource bundle, which allows you to provide some text to the beginning and end of the errors respectively. The following fragment shows the usage of the errors.header element.

  <bean:message key="errors.header"/>
   <ul>
   <html:messages id="error">
      <li><bean:write name="error"/></li>
   </html:messages>
   </ul><hr>

The resource bundle would need to contain the errors.header entry:

errors.header=<h3><font color="red">Validation Error</font></h3>You must 
correct the following error(s) before proceeding.

If the message collection is empty however, you would still see the header text and therefore it's best to check for the presence of messages before printing anything out:

<logic:messagesPresent>
   <bean:message key="errors.header"/>
   <ul>
   <html:messages id="error">
      <li><bean:write name="error"/></li>
   </html:messages>
   </ul><hr>
</logic:messagesPresent>

There are equivalent Tags for the errors collection.

Dealing with Dates and Currencies

The Java language provides within its core libraries, the ability to deal with Dates and Currencies. One of the most sought after requirements is to format a Date or a Currency value based on a Country or Language. Java provides the java.util.Locale class, which encapsulates the country and language and makes it easier for applications to understand and deal with geographical differences.

The Struts framework makes use of this by storing the Locale for a user in the HttpSession, which is created the first time a request is received from a user. This functionality can be turned off in Struts, but it's on by default.

Once the Locale is stored in the user's session, the framework can make decisions about which localization is performed. For example, the Struts framework will chose a resource bundle that best matches the user's Locale. This makes it somewhat easier to brand a site because localized text can be rendered automatically be the framework.

Many of the Tags also support this I18N functionality. For example, the Message Tag within the Bean Library uses the Locale to automatically pull text from the proper bundle. However, there are some additional ways you can format text output. The Write Tag has a format attribute that you can use to format currency in a particular format. Suppose that I wanted to display currency in the following manner:

X,XXX.XX

Which is specific to mostly U.S. markets, then I could use the Write Tag this way:

<bean:write 
  name="cartItem" 
  format="#,##0.00" 
  property="unitPrice"/>

Although this works, there's actually a more portable way of doing this to support multiple formats. There's an attribute called formatKey on the Write Tag that will pull the format from the resource bundle. The Tag entry would look like the following:

<bean:write 
  name="cartItem" 
  format="currencyFormat" 
  property="unitPrice"/>
And the resource bundle would need to have the following entry:
currencyFormat=#,##0.00

Doing it this way allows each resource bundle to specify a currency format specific to its Locale.

Displaying Checkboxes with the Options Tag

One of the seemingly easiest things to do with HTML may be one of the hardest to do when building dynamic pages. That is rendering of checkboxes for an application; there are a few reasons why this is true.

Related Reading

Programming Jakarta Struts
By Chuck Cavaness

First, checkboxes indicate checked/unchecked on/off, or true/false, depending on how you look at it; it's a binary condition. In the Java world, you would think this needs to be represented as a primitive boolean or a class java.lang.Boolean. However, since all values are sent from the browser to the server as Strings, this complicates things a bit. That means that something needs to convert the String "on" or "true" to its Boolean equivalent.

The second non-obvious thing is how browsers treat checkboxes when updating the server. If a checkbox is checked, then a value is sent to the server like name="on", where "name" is the name given to the checkbox in the HTML form. Well, guess what is sent to the server when the checkbox isn't checked? You would think name="off" or name="false", right? Actually nothing is sent. Therefore the application can have a tough time understanding what's going on with the checkboxes on the pages.

The Struts Checkbox Tag renders an HTML checkbox and will automatically check or uncheck it depending on the state of the Boolean value of the ActionForm associated with the page. You have to remember that if the checkbox is unchecked by the user, no notification will be sent and you must take this into account. (Hint: One of the easiest ways to think about this is if you don't receive a value, then it must be unchecked!)

When you have a large number of related checkboxes, you should consider storing the values within an array and use the Multibox Tag.

Switching to the Java Standard Tag Library (JSTL)

The use of the Struts Tag libraries is a great movement forward toward smarter and faster development of web applications. However, Struts isn't cornering the market on the use of JSP Tags. Other frameworks use Tags and you don't have to use Struts to get the benefits of JSP Custom Tags.

In order to standardize some of the more useful Tags, Sun and other participants created an initiative and formalized the most commonly used Tags. This initiative was turned into JSR-52, which has now released 1.0. Java Standard Tag Library (or JSTL as it's known), provides a specification for four separate areas of concern:

The specification also describes an expression language (EL) that is designed to simplify page development. As you might imagine, there is some overlap with some of the Tags provided by the Struts library. For example, looping, error handling, and conditional logic is provided by JSTL as well as Struts. The question is "which set of tags should I use?" The answer is to use the JSTL Tags where possible. For existing code, it's probably not worth the risk to change. However, for new JSPs you should be looking at the JSTL for Tag functionality. Struts 1.1 even includes a version of the JSTL Tags based on the new Expression Language. Something definitely to keep you eye on in future versions of Struts.

Chuck Cavaness is a graduate from Georgia Tech with degrees in computer engineering and computer science. He has built Java-based enterprise systems in the healthcare, banking, and B2B sectors. He is also the author of two O'Reilly books, Programming Jakarta Struts and Jakarta Struts Pocket Reference.


O'Reilly & Associates recently released (May 2003) Jakarta Struts Pocket Reference.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.