Struts-The Complete Reference
Struts-The Complete Reference
- An Introduction to Struts - Building a Simple Struts Application - The Model Layer - The View Layer - The Controller Layer - Validator - Tiles - Declarative Exception Handling - Struts Modules
Chapter 11 - The HTML Tag Library Chapter 12 - The Bean Tag Library Chapter 13 - The Logic Tag Library Chapter 14 - The Nested Tag Library Chapter 15 - Using JSTL with Struts
Part III - Struts Configuration Files
Chapter 16 - The Struts Configuration File Chapter 17 - The Tiles Configuration File Chapter 18 - The Validator Configuration Files
Part IV - Applying Struts
Appendix Index
Hyperlinks were used to connect the content together. Although hyperlinks enabled the user to move from page to page, the contents of each page was still a static document that did not support other forms of user interaction. It wasnt long, though, before businesses wanted to be able to offer dynamic content that offered the user a richer, more interactive experience. Before continuing, it will be helpful to explain precisely what is meant by dynamic content. In short, dynamic content is data that is specifically targeted for a particular user. For example, a user may want to check the price and availability of some item in an online store. The user enters the item name and the server supplies the response. The response is generated on-the-fly based on the request, and is thus dynamic content. To fill the dynamic-content void, Web server software began to support the use of CGI scripts for creating applications that could run on a Web server and generate dynamic content back to a browser. CGI, or Common Gateway Interface, allowed Web servers to accept a request and execute a server-side program that would perform some action and then generate output on standard out. Web server software would then read that output and send it back to the requesting browser. Initially, many of these CGI scripts were written in Perl or other Unix-based scripting languages. Over time, though, as the applications being built to run as CGI scripts grew in complexity, more application-oriented languages like C and C++ were being used to create larger, more robust applications. With the advent of HTML forms, CGI scripts also were able to receive data from the browser and process it. As most readers know, HTML forms allow data entry on a Web page. That data could be sent to a CGI script on the server and then manipulated, stored, or otherwise processed. Around the same time that CGI-based application development was becoming popular on the server side, the Java programming language was introduced, with an initial focus on applets. Applets gave the Web developer the ability to add rich, dynamic functionality to Web pages. Because Java offered the promise of write once and run anywhere programs, any browser that supported Java could run the applets. For the first time, developers could easily include dynamic content on a Web page. For the same reasons that Java began to blossom on the client side with applets, Java also began to make inroads on the server side with the advent of servlet technology in 1997. Servlets solved many of the shortcomings of CGI, such as portability and efficiency, and offered a Javabased solution for the Web application paradigm. Servlets are portable across operating systems and can run on any server that has a Java Virtual Machine (JVM). Thus, they also benefit from Javas write once, run anywhere philosophy. Servlets have a more efficient execution model than CGIs because they are multithreaded instead of requiring a new process for each request. Servlets also have access to Javas vast libraries, including the JDBC APIs. After servlets were introduced, Sun released the JavaServer Pages (JSP) technology as an extension to the servlet technology. JSPs take the reverse approach from servlets to building Web applications by having Java code intermingled in an HTML-based page. When a request is made to the server for a JSP, the Java server container checks if the JSP has already been compiled into a servlet. If it has, it proceeds to execute the servlet. If the JSP has not yet been compiled into a servlet, the server container converts the JSP code into a Java source file and then compiles that source so that subsequent requests to the JSP will find the servlet already compiled and ready to execute. The nice thing about this approach is that changes to the JSP HTML can be made without having to manually recompile the code. The server container manages the compilation and will recognize that the HTML in the JSP has changed and recompile the JSP into a servlet for you. JSPs solve the problem of presentation code (HTML) being embedded in servlets, which made development cumbersome because HTML authors had to wade through Java code to edit HTML (not a good separation of responsibilities). In contrast, HTML developers can work on JSPs directly without interfering with Java code.
As the preceding discussion shows, many of the changes in Web-based development that have occurred over the past few years have been driven by the desire to efficiently include dynamic content in a Web page. Streamlining the use of dynamic content has been, and remains, one of the more important issues associated with the Internet and the applications that use it. As you will see, Struts is part of the solution to the dynamic-content problem.
Model Components
In the MVC architecture, model components provide an interface to the data and/or services used by an application. This way, controller components don't unnecessarily embed code for manipulating an application's data. Instead, they communicate with the model components that perform the data access and manipulation. Thus, the model component provides the business logic. Model components come in many different forms and can be as simple as a basic Java bean or as intricate as Enterprise JavaBeans (EJBs) or Web services.
View Components
View components are used in the MVC architecture to generate the response to the browser. Thus, a view component provides what the user sees. Often times the view components are simple JSPs or HTML pages. However, you can just as easily use WML or another view technology for this part of the architecture. This is one of the main design advantages of MVC. You can use any view technology that you'd like without impacting the Model (or business) layer of your application.
Controller Components
At the core of the MVC architecture are the controller components. The Controller is typically a servlet that receives requests for the application and manages the flow of data between the Model layer and the View layer. Thus, it controls the way that the Model and View layers interact. The Controller often uses helper classes for delegating control over specific requests or processes.
Enter Struts
Although the Model-View-Controller architecture is a powerful means of organizing code, developing such code can be a painstaking process. This is where Struts comes in. Struts is a Web application framework that streamlines the building of Web applications based on the MVC design principles. But what does that mean? Is Struts an MVC Web application that you just add on to or extend? Is Struts just some libraries? Actually, Struts is a little bit of both. Struts provides the foundation, or framework, for building an MVC-oriented application along with libraries and utilities for making MVC development faster and easier. You could create a new Controller servlet every time you wanted to use the MVC design pattern in your Web application. Additionally, you'd need to create the management/flow logic for getting data to and from the Model and then routing requests to the View. You'd also need to define interfaces for interacting with your Model objects and all the utility code that goes along with using the MVC design pattern. However, instead of going through this process each time you create a new application, you can use Struts. Struts provides the basic structure and outline for building that application, freeing you to concentrate on building the business logic in the application and not the 'plumbing.' To better understand the benefits of Struts, consider the following analogy. If you were to create a GUI application in Java, you wouldn't write a textfield widget and a dropdown widget yourself. You would use Java's Swing API that already has standardized, fully functional code that provides these controls. Not only are the Swing controls ready-to-use, but they are also understood by all Java programmers. Struts provides the same type of advantages: Struts supplies a standard way of implementing an MVC application, the Struts code is tried and true, and the techniques required to use Struts are well known and documented. In addition to providing the foundation for MVC applications, Struts provides rich extension points so that your application can be customized as you see fit. This extensibility has led to several third-party add-ons being made available for Struts, such as libraries for handling application workflow, libraries for working with view technologies other than JSP, and so on.
Support for Struts comes in three forms. First is the API documentation that comes with Struts. Second, Struts has a very active mailing list where you can get support for virtually any question. Third, several third-party consulting companies specialize in Struts support and development.
Base Framework
The base framework provides the core MVC functionality and is comprised of the building blocks for your application. At the foundation of the base framework is the Controller servlet: ActionServlet. The rest of the base framework is comprised of base classes that your application will extend and several utility classes. Most prominent among the base classes are the Action and ActionForm classes. These two classes are used extensively in all Struts applications. Action classes are used by ActionServlet to process specific requests. ActionForm classes are used to capture data from HTML forms and to be a conduit of data back to the View layer for page generation.
HTML Used to generate HTML forms that interact with the Struts APIs. Bean Used to work with Java bean objects in JSPs, such as accessing bean values. Logic Used to cleanly implement simple conditional logic in JSPs. Nested Used to allow arbitrary levels of nesting of the HTML, Bean, and Logic tags that otherwise do not work.
Tiles Plugin
Struts comes packaged with the Tiles subframework. Tiles is a rich JSP templating framework that facilitates the reuse of presentation (HTML) code. With Tiles, JSP pages
can be broken up into individual 'tiles' or pieces and then glued together to create one cohesive page. Similar to the design principles that the core Struts framework is built on, Tiles provides excellent reuse of View code. As of Struts 1.1, Tiles is part of and packaged with the core Struts download. Prior to Struts 1.1, Tiles was a third-party addon, but has since been contributed to the project and is now more tightly integrated.
Validator Plugin
Struts comes packaged, as of version 1.1, with the Validator subframework for performing data validation. Validator provides a rich framework for performing data validation on both the server side and client side (browser). Each validation is configured in an outside XML file so that validations can easily be added to and removed from an application declaratively versus being hard-coded into the application. Similar to Tiles, prior to Struts 1.1, Validator was a third-party add-on, but has since been included in the project and is more tightly integrated.
Acquiring Struts
Struts is available free of charge and can be downloaded from the Apache Jakarta site at: http://jakarta.apache.org/struts/ Because Struts is open source, you have a couple of options when downloading the Struts framework software. You can download the software in binary, precompiled form or you can download the source code for compiling on your own. For most cases, the binary distribution will suffice; however, if you want to make changes to the Struts source code, the source distribution is available. If you choose to download a binary distribution of Struts, you have a couple of options. You can download a released version of the code, which has been rigorously tested and certified as being of good quality, or you can download a nightly build of the code, which is less stable and not intended for production use. Opting to use a nightly build allows you to get access to the latest enhancements and bug fixes that have been made to the Struts framework ahead of an official release. However, its important to point out again that nightly builds have no guarantee on quality because adding a new feature to Struts could potentially break another feature that has been stable for some time. Similar to downloading a binary distribution of Struts, if you choose to download a source distribution, you have a couple of options. You can download the source for an officially released version of Struts or you can choose to get the latest and greatest version of the Struts source code directly from the Struts CVS source control repository. Just as with the binary distribution, choosing to download the latest Struts source code can get you the newest enhancements and bug fixes to the software, but it may also be laden with new bugs.
(TLD) files. Additionally, Struts comes with several sample Web applications that illustrate how to use the Struts framework. One of the sample Web applications, struts-blank.war, is typically used for new Struts applications because it provides a basic template for a Struts application, including all the necessary .jar files, and so on. Struts distributions also come witha sample Web application, struts-example.war, that illustrates the basic structure of a Struts application.
As you will see, a Struts application is a composite of several interrelated parts. The goal of this chapter is to give you a general understanding of these parts and show how they work together to form a complete program. To accomplish that goal, this chapter develops a simple application that highlights each Struts component. In the process, several key elements of Struts are introduced. Once you understand how this simple Struts application works, you will be able to easily understand other Struts programs because all share a common architecture. Subsequent chapters discuss in detail the many concepts introduced here. This chapter also describes the steps necessary to compile, package, and run the application. You will use the same general procedure with the other examples in this book.
Application Overview
The sample application in this chapter deviates from the stereotypical 'Hello World' program found in most programming books. Instead, a bit more sophisticated example is needed to illustrate the components of Struts and the process required to build a Strutsbased application. The example that we will use is a simple human resources (HR) application. Creating a full-blown HR application is a large undertaking that requires several pieces of functionality, from employee management to benefits management, so the sample application in this chapter will support only one common subset of functionality: Employee Search. The sample application is called Mini HR and it will have a basic opening page that links to an Employee Search page. From the Employee Search page, users can search for employees by name or social security number. After executing the search, the Search page will be redisplayed with a list of employees that match the search criteria. Although quite simple, and limited in scope, this example illustrates the key features common to any Struts-based Web application.
File index.jsp
Description Contains the JSP that is used as a gateway page for the Mini HR application and provides a link to the Employee Search page. Contains the JSP that is used for performing employee searches and displaying the search results. Contains the class that captures and transfers data to and from the Search page. This is a View class. Contains the class code that processes requests from the Search page. This is a Controller class. Contains the class that encapsulates the business logic and data access involved in searching for employees. This is a Model class. Contains the class that represents an employee and encapsulates all of an employee's data. This is a Model class. Contains the XML that is used to configure the Java Web application properties for the Mini HR application. Contains the XML that is used to configure the Struts framework for this application.
Employee.java
web.xml struts-config.xml
ApplicationResources.properties Contains properties that are used to externalize application strings, labels, and messages so that they can be changed without having to recompile the application. This file is also used for internationalizing the application. The following sections examine each of the Mini HR application files in detail, and in many cases line by line. First, though, it's necessary to explain where each file should be placed in a directory hierarchy. Because this application (and all other Struts applications) will be deployed to a J2EE servlet container, the application files have to be arranged in the standard J2EE Web Archive (.war) format, which is simply a Java Archive (.jar) file with a different extension (.war). The Web Archive format also specifies a few key requirements for the .jar file:
There must be a directory at the root level of the archive named WEB-INF. At run time this is a protected directory and thus any files beneath it will be inaccessible to browsers. There must be a Web application deployment descriptor file named web.xml beneath the WEB-INF directory. This file will be explained later in this chapter in the 'web.xml' section. Any libraries needed by the application should be under a directory called lib located beneath the WEB-INF directory.
Any class files needed by the application, which are not already packaged in a .jar file, should be under a directory called classes located beneath the WEB-INF directory.
For the Mini HR application, you will create a directory called MiniHR. In principle, you can place this directory anywhere, but to follow along with this example, put it at c:\java. You'll use the c:\java\MiniHR directory as the root of your Web application so that you can easily create a Web Archive file later. Following is the layout of the c:\java\MiniHR directory, shown in Figure 2-1, and the location of each file examined in this section. You will need to place the files in this exact structure.
index.jsp
The index.jsp file, shown here, is a very simple JSP that is used to render Mini HR's opening screen:
<%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> <html> <head> <title>ABC, Inc. Human Resources Portal</title> </head>
<body> <font size="+1">ABC, Inc. Human Resources Portal</font><br> <hr width="100%" noshade="true"> • Add an Employee<br> • <html:link forward="search">Search for Employees</html:link><br> </body> </html>
You'll notice that index.jsp is comprised mostly of standard HTML, with the exception of the JSP tag library definition at the top of the file and the 'Search for Employees' link. The index.jsp file uses Struts' HTML Tag Library to render the Search link. Before you can use the HTML Tag Library, you have to 'import' it into the JSP with the following line at the top of the JSP:
<%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %>
This line associates the tag library located at /WEB-INF/tlds/struts-html.tld with a prefix, or 'handle,' of html. That way, any time a tag from the Struts HTML Tag Library is used, it will be prefixed with html. In index.jsp's case, the link tag is used with the following line:
<html:link forward="search">Search for Employees</html:link>
Of course, if you wanted to use another prefix for the tag library, you could do so by updating the prefix attribute of the tag library import on the first line of the file. The HTML Tag Library's link tag is used for rendering an HTML link, such as http://www.jamesholmes.com/. The link tag goes beyond basic HTML, though, by allowing you to access link, or forward definitions, from the Struts configuration file, struts-config.xml, which is covered later in this chapter in the 'struts-config.xml' section. In this case, the tag looks for a forward definition named 'search' defined in the strutsconfig.xml file to use for the link being generated. If you skip ahead to the 'strutsconfig.xml' section of this chapter, you'll see that the forward definition is as follows:
<!-- Global Forwards Configuration --> <global-forwards> <forward name="search" path="/search.jsp"/> </global-forwards>
Forward definitions allow you to declaratively configure the location to which a link points instead of hard-coding that information into your JSP or application. As you'll see in Chapter 5, forward definitions are used throughout Struts to direct the flow of an application from the Struts configuration file. The following is the source code generated after index.jsp has been requested in the browser. Notice that the Search page link has been converted into a standard HTML link.
<html> <head> <title>ABC, Inc. Human Resources Portal</title> </head> <body> <font size="+1">ABC, Inc. Human Resources Portal</font><br> <hr width="100%" noshade="true"> • Add an Employee<br> • <a href="/MiniHR/search.jsp">Search for Employees</a><br> </body> </html>
search.jsp
The search.jsp file is responsible for the bulk of the Employee Search functionality in the Mini HR application. When the Employee Search link is selected from the index.jsp page, search.jsp is executed. This initial request for search.jsp renders the basic Employee Search screen shown here:
Each time a search is performed, Struts' Controller servlet is executed and eventually search.jsp is executed to handle the rendering of the Employee Search screen, with the search results, as shown here:
Similarly, if there are any errors with the search criteria when the search is submitted, search.jsp is executed to report the errors, as shown here:
<logic:present name="searchForm" property="results"> <hr width="100%" size="1" noshade="true"> <bean:size id="size" name="searchForm" property="results"/> <logic:equal name="size" value="0"> <center><font color="red"><b>No Employees Found</b></font></center> </logic:equal> <logic:greaterThan name="size" value="0"> <table border="1"> <tr> <th>Name</th> <th>Social Security Number</th> </tr> <logic:iterate id="result" name="searchForm" property="results"> <tr> <td><bean:write name="result" property="name"/></td> <td><bean:write name="result" property="ssNum"/></td> </tr> </logic:iterate> </table> </logic:greaterThan> </logic:present> </body> </html>
Because of its size and importance, we will examine it closely, line by line. Similar to index.jsp, search.jsp begins by declaring the JSP tag libraries that will be used by the JSP:
<%@ taglib uri="/WEB-INF/tlds/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/tlds/struts-logic.tld" prefix="logic" %>
In addition to the HTML Tag Library used by index.jsp, search.jsp uses Struts' Bean and Logic libraries. These additional tag libraries contain utility tags for working with Java beans and using conditional logic in a page, respectively. The next several lines are comprised of basic HTML tags:
<html> <head> <title>ABC, Inc. Human Resources Portal - Employee Search</title> </head> <body> <font size="+1"> ABC, Inc. Human Resources Portal - Employee Search </font><br> <hr width="100%" noshade="true">
<html:errors/>
Recall that search.jsp is used to render any errors that occur while validating that the search criteria are sound. The HTML Tag Library's errors tag will emit any errors that are passed to the JSP from the SearchForm object. This is covered in more detail in the 'SearchForm.java' section in this chapter. The next several lines of search.jsp are responsible for rendering the HTML for the search form:
<html:form action="/search"> <table> <tr> <td align="right"><bean:message key="label.search.name"/>:</td> <td><html:text property="name"/></td> </tr> <tr> <td></td> <td>-- or --</td> </tr> <tr> <td align="right"><bean:message key="label.search.ssNum"/>:</td> <td><html:text property="ssNum"/> (xxx-xx-xxxx)</td> </tr> <tr> <td></td> <td><html:submit/></td> </tr> </table> </html:form>
Before discussing the specifics of the search form, let's review the use of the Bean Tag Library in this snippet. This snippet uses the library's message tag, as shown here:
<td align="right"><bean:message key="label.search.name"/>:</td>
The message tag allows externalized messages from the ApplicationResources .properties file to be inserted into the JSP at run time. The message tag simply looks up the key passed to it in ApplicationResources.properties and returns the corresponding message from the file. This feature is especially useful to internationalize a page and to allow easy updating of messages outside the JSP. Internationalization is the process of providing content specific to a language, locale, or region. For instance, internationalization would be to create both English and Spanish versions of the same JSP. Note The acronym I18N is sometimes used in place of the word internationalization, because it is such a long word to type. I18N represents the first letter i, followed by 18 characters, and then the final letter n. The I18N acronym is used occasionally in this book.
Now, it's time to examine the form. Struts' HTML Tag Library has a tag for each of the standard HTML form tags, such as <form> <input type=''> and so on. Instead of using the standard HTML tags, you'll use the HTML Tag Library's equivalent tag, which ties the form to Struts. For example, the text tag (<html:text>) renders an <input type='text' > tag. The text tag goes one step further, though, by allowing a property to be associated with the tag, as shown here:
<td><html:text property="name"/></td>
The property 'name' here corresponds to the field named name in the SearchForm object. That way, when the tag is executed, it places the value of the name field in the HTML at run time. Thus, if the name field had a value of 'James Holmes' at run time, the output from the tag would look like this:
<td><input type="text" name="name" value="James Holmes"></td>
At the beginning of this next snippet, the HTML Tag Library's form tag is used to render a standard HTML <form> tag. Notice, however, that it specifies an action parameter of '/search' as shown here:
<html:form action="/search">
The action parameter associates an Action object mapping from the strutsconfig.xml file with the form. That way, when the form is submitted, the processing will be handled by the specified Action object. The final section of the search.jsp file contains the logic and tags for rendering search results:
<logic:present name="searchForm" property="results"> <hr width="100%" size="1" noshade="true"> <bean:size id="size" name="searchForm" property="results"/> <logic:equal name="size" value="0"> <center><font color="red"><b>No Employees Found</b></font></center> </logic:equal> <logic:greaterThan name="size" value="0"> <table border="1"> <tr> <th>Name</th> <th>Social Security Number</th> </tr> <logic:iterate id="result" name="searchForm" property="results"> <tr> <td><bean:write name="result" property="name"/></td> <td><bean:write name="result" property="ssNum"/></td> </tr>
The beginning of this snippet uses Struts' Logic Tag Library for using conditional logic in a JSP. The Logic Library's present tag checks an object to see if a particular property is present. In this case, the logic tag checks to see if the results field of the SearchForm has been set. If so, then all of the HTML and JSP tags inside the <logic:present > tag will be executed. Otherwise, they will be ignored. The rest of the tags in this snippet are responsible for rendering the search results. First, the Bean Library's size tag gets the size of the results ArrayList from the SearchForm object. Next, the size is checked to see if it is 0 using the Logic Library's equal tag. If the size is in fact 0, then a 'No Employees Found' message will be rendered. Otherwise, each of the employees returned from the search will be displayed. The Logic Library's iterate tag is used to iterate over each of the search results. Each search result is assigned to a variable named result by the iterate tag. Inside the iterate tag the Bean Library's write tag is used to access the result variable's name and ssNum fields.
SearchForm.java
The SearchForm class, shown next, is a View class that is used to capture and transfer data to and from the Employee Search page. When the HTML form on the Search page is submitted, Struts' ActionServlet will populate this class with the data from the form. Notice that there will be a one-to-one mapping between fields on the page and fields in the class with getter and setter methods. Struts uses encapsulation and Java's reflection mechanism to call the method corresponding to each field from a page. Additionally, when SearchAction (the Controller class for the Search page) executes, it will populate this object with the search results so that they can be transferred back to the Search page.
package com.jamesholmes.struts; import java.util.List; import javax.servlet.http.HttpServletRequest; import import import import org.apache.struts.action.ActionError; org.apache.struts.action.ActionErrors; org.apache.struts.action.ActionForm; org.apache.struts.action.ActionMapping;
public class SearchForm extends ActionForm { private String name = null; private String ssNum = null; private List results = null; public void setName(String name) {
this.name = name; } public String getName() { return name; } public void setSsNum(String ssNum) { this.ssNum = ssNum; } public String getSsNum() { return ssNum; } public void setResults(List results) { this.results = results; } public List getResults() { return results; } // Reset form fields. public void reset(ActionMapping request) { name = null; ssNum = null; results = null; }
mapping,
HttpServletRequest
// Validate form data. public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); boolean nameEntered = false; boolean ssNumEntered = false; // Determine if name has been entered. if (name != null && name.length() > 0) { nameEntered = true; } // Determine if social security number has been entered. if (ssNum != null && ssNum.length() > 0) { ssNumEntered = true; } /* Validate that either name or social security number has been entered. */ if (!nameEntered && !ssNumEntered) { errors.add(null, new ActionError("error.search.criteria.missing")); } /* Validate format of social security number if it has been entered. */ if (ssNumEntered && !isValidSsNum(ssNum.trim())) {
errors.add("ssNum", new ActionError("error.search.ssNum.invalid")); } return errors; } // Validate format of social security number. private static boolean isValidSsNum(String ssNum) { if (ssNum.length() < 11) { return false; } for (int i = 0; i < 11; i++) { if (i == 3 || i == 6) { if (ssNum.charAt(i) != '-') { return false; } } else if ("0123456789".indexOf(ssNum.charAt(i)) == -1) { return false; } } return true; } }
ActionForm subclasses, including SearchForm, are basic Java beans with a couple of extra Struts-specific methods: reset( ) and validate( ). The reset( ) method is used to clear out, or 'reset,' an ActionForm's data after it has been used for a request. Because Struts reuses ActionForms instead of creating new ones for each request, this method is necessary to ensure that data from different requests is not mixed. Typically, this method is used to just set class fields back to their initial states, as is the case with SearchForm. However, as you'll see in Chapter 4, this method can be used to perform other necessary logic for resetting an ActionForm object. The validate( ) method of ActionForm is called to perform basic validations on the data being transferred from an HTML form. In SearchForm's case, the validate( ) method first confirms that a name and social security number have been entered. If a social security number has been entered, SearchForm goes one step further and validates the format of the social security number with the isValidSsNum( ) method. The isValidSsNum( ) method simply ensures that an 11-character string was entered and that it conforms to the following format: three digits, hyphen, two digits, hyphen, four digits (e.g., 111-22-3333). Note that business-level validations, such as looking up a social security number in a database to make sure it is valid, are considered business logic and should be in a Model layer class. The validations in an ActionForm are meant to be very basic, such as just confirming that data was entered, and should not be used for performing any real business logic. You'll notice that the validate( ) method returns an ActionErrors object and the validations inside the method populate an ActionErrors object if any validations fail. The ActionErrors object is used to transfer validation error messages to the screen. Remember from the discussion of search.jsp that the HTML Tag Library's errors tag will emit any errors in a JSP if they are present. Following is the snippet from search.jsp:
<html:errors/>
Here in the ActionForm class, you simply place the keys for messages into the ActionErrors object, such as 'error.search.criteria.missing'. The errors tag will use these keys to load the appropriate messages from the ApplicationResources.properties file, discussed in the section of the same name later in this chapter. Note that the validate( ) method will be invoked only if the validate parameter of the <action> tag is set to 'true' in the struts-config.xml file.
SearchAction.java
The SearchAction class, shown next, is a Controller class that processes requests from the Search page:
package com.jamesholmes.struts; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import org.apache.struts.action.Action; org.apache.struts.action.ActionForm; org.apache.struts.action.ActionForward; org.apache.struts.action.ActionMapping;
public final class SearchAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { EmployeeSearchService service = new EmployeeSearchService(); ArrayList results; SearchForm searchForm = (SearchForm) form; // Perform employee search based on what criteria was entered. String name = searchForm.getName(); if (name != null && name.trim().length() > 0) { results = service.searchByName(name); } else { results = service.searchBySsNum(searchForm.getSsNum().trim()); } // Place search results in SearchForm for access by JSP. searchForm.setResults(results); // Forward control to this Action's input page. return mapping.getInputForward(); } }
Remember from the discussion of search.jsp that the HTML form on the page is set to post its data to the '/search' action. The strut-config.xml file maps the search action to this class so that when ActionServlet (Controller) receives a post from the Search page, it delegates processing for the post to this Action subclass. This mapping is shown here:
<!-- Action Mappings Configuration --> <action-mappings> <action path="/search" type="com.jamesholmes.struts.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> <forward name="results" path="/results.jsp"/> </action> </action-mappings>
Struts' Action subclasses manage the processing of specific requests. You can think of them as mini servlets assigned to manage discreet Controller tasks. For instance, in the preceding example, SearchAction is responsible for processing employee search requests and acts as a liaison between the Model (EmployeeSearchService) and the View (search.jsp). SearchAction begins by overriding Struts' Action class execute( ) method. The execute( ) method is the single point of entry for an Action class by Struts' ActionServlet. You'll notice that this method takes an HttpServletRequest object and an HttpServletResponse object as parameters, similar to a servlet's service( ), doGet( ), and doPost( ) methods. Additionally, execute( ) takes a reference to the ActionForm associated with this Action and an ActionMapping object reference. The ActionForm reference passed to this Action will be an instance of SearchForm, as discussed in the previous section, 'SearchForm.java.' The ActionMapping reference passed to this Action will contain all of the configuration settings from the struts-config.xml file for this Action. The execute( ) method begins by instantiating a few objects, and then the real work gets underway with a check to see what search criteria was entered by the user. Notice that the ActionForm object passed in is cast to its native type: SearchForm. Casting the object allows you to access SearchForm's methods for retrieving the search criteria. Based on the criteria entered, one of EmployeeSearchService's methods will be invoked to perform the employee search. If an employee name was entered, the searchByName( ) method will be invoked. Otherwise, the searchBySsNum( ) method will be invoked. Both search methods return an ArrayList containing the search results. This results ArrayList is then added to the SearchForm instance so that search.jsp (View) can access the data. The execute( ) method concludes by forwarding control to SearchAction's input page: search.jsp. The input page for an action is declared in the struts-config.xml file, as shown here for SearchAction, and is used to allow an action to determine from which page it was called:
<action path="/search"
EmployeeSearchService.java
EmployeeSearchService is a Model class that encapsulates the business logic and data access routines involved in searching for employees. The SearchAction Controller class uses this class to perform an employee search and then shuttles the resulting data to the View layer of the Mini HR application. EmployeeSearchService is shown here:
package com.jamesholmes.struts; import java.util.ArrayList; public class EmployeeSearchService { /* Hard-coded sample data. Normally this would come from a real data source such as a database. */ private static Employee[] employees = { new Employee("Bob Davidson", "123-45-6789"), new Employee("Mary Williams", "987-65-4321"), new Employee("Jim Smith", "111-11-1111"), new Employee("Beverly Harris", "222-22-2222"), new Employee("Thomas Frank", "333-33-3333"), new Employee("Jim Davidson", "444-44-4444") }; // Search for employees by name. public ArrayList searchByName(String name) { ArrayList resultList = new ArrayList(); for (int i = 0; i < employees.length; i++) { if(employees[i].getName().toUpperCase().indexOf(name.toUpperCase ()) != -1) { resultList.add(employees[i]); } } return resultList; } // Search for employee by social security number. public ArrayList searchBySsNum(String ssNum) { ArrayList resultList = new ArrayList(); for (int i = 0; i < employees.length; i++) { if (employees[i].getSsNum().equals(ssNum)) { resultList.add(employees[i]); } }
return resultList; } }
In order to simplify Mini HR, the EmployeeSearchService class will not actually communicate with a real data source, such as a database, to query employee data. Instead, EmployeeSearchService has some sample Employee data hard-coded at the top of the class, as shown here:
/* Hard-coded sample data. Normally this would come from a real data source such as a database. */ private static Employee[] employees = { new Employee("Bob Davidson", "123-45-6789"), new Employee("Mary Williams", "987-65-4321"), new Employee("Jim Smith", "111-11-1111"), new Employee("Beverly Harris", "222-22-2222"), new Employee("Thomas Frank", "333-33-3333"), new Employee("Jim Davidson", "444-44-4444") };
The sample data is comprised of a few Employee objects. As you'll see in the next section, the Employee class is a simple class for encapsulating employee data. The searchByName( ) and searchBySsNum( ) methods use the hard-coded data when performing a search. The searchByName( ) method loops through each of the Employee objects in the employees array looking for any employees that match the name specified. If a match is found, it is added to the return ArrayList that will eventually be used by search.jsp to display the results. Note that the name search is case insensitive by virtue of uppercasing the Strings before comparison. You should also note that the use of String's indexOf( ) method allows for partial matches instead of only exact matches. Similar to the searchByName( ) method, searchBySsNum( ) loops through the hardcoded employee list looking for any employees that match the specified social security number. Note that searchBySsNum( ) will capture only exact matches. Because social security numbers are unique to an individual, only one match should ever be returned for a social security number-based search.
Employee.java
The Employee class, shown next, is a simple bean for encapsulating the data for an employee. The class is straightforward, comprised simply of setters and getters for the Employee class data.
package com.jamesholmes.struts; public class Employee { private String name; private String ssNum; public Employee(String name, String ssNum) {
this.name = name; this.ssNum = ssNum; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setSsNum(String ssNum) { this.ssNum = ssNum; } public String getSsNum() { return ssNum; } }
This class is used by EmployeeSearchService for transferring employee search results data from the Model (EmployeeSearchService) to the View (search.jsp). Oftentimes, this 'transfer' object is referred to as a Data Transfer Object (DTO) or Value Object (VO) and has the simple responsibility of being a data container and abstracting the Model from the View.
web.xml
The web.xml file, shown next, is a standard Web Archive deployment descriptor used to configure the Mini HR application. Because the file contains several configuration details, it will be reviewed section by section.
<?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servletclass> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
<!-- The Welcome File List --> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>/WEB-INF/tlds/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/tlds/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/tlds/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-logic.tld</taglib-location> </taglib> </web-app>
The following is the first section of the web.xml file. It declares the Struts Controller servlet, ActionServlet, and configures it.
<!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servletclass> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
This declaration starts by assigning a name to the servlet that will be used in the next section for mapping the servlet to specific application requests. After defining the servlet's name and class, the config initialization parameter is defined. This parameter tells Strut's ActionServlet where to find its central configuration file: strutsconfig.xml. Finally, the <load-on-startup> tag is used to instruct the servlet engine how many instances of the servlet should be instantiated when the server starts. The second section of the web.xml file causes ActionServlet to respond to certain URLs:
<!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
Notice that the <servlet-name> tag references the same name declared in the previous section. This associates the previous servlet declaration with this mapping. Next, the <url-pattern> tag is used to declare the URLs that ActionServlet will respond to. In
this case, it is saying that ActionServlet should process any requests for pages that end in .do. So, for example, a request to http://localhost:8080/MiniHR/page.do or a request to http://localhost:8080/MiniHR/dir1/dir2/page2.do will be routed to Struts' ActionServlet for processing. The next section of the web.xml file declares the Welcome File list that the Mini HR application will use:
<!-- The Welcome File List --> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list>
The Welcome File list is a list of files that the Web server will attempt to respond with when a given request to the Web application goes unfulfilled. For example, in Mini HR's case, you can enter a URL of http://localhost:8080/MiniHR/ and index.jsp will be executed, because no page has been specified in the URL. The servlet engine detects this and references the Welcome File list for pages that should be tried to respond to the request. In this case, the servlet engine will try to respond with a page at /index.jsp. If that page is unavailable, an error will be returned. Note that the Welcome File list can span several pages. In that case, the servlet engine will iterate through the list until a file is found that can be served for the request. The final section of the web.xml file declares the JSP tag libraries that should be available to JSPs in the Mini HR application:
<!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>/WEB-INF/tlds/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/tlds/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/tlds/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-logic.tld</taglib-location> </taglib>
A tag library definition associates a URI (or simple identifier) with the actual location of a *.tld file beneath a Web application, so essentially they are aliases. Using these aliases allows JSPs to reference an alias for a Tag Library Descriptor instead of the actual descriptor location. That way, the actual location of the tag library definitions can change without each JSP having to be changed as long as the aliases stay consistent.
Notice in the web.xml file that the URI (alias) and the actual location of the TLD files are the same. This is done for simplicity's sake and is a common practice. Note, despite the fact that the URL and location are the same, the definitions in web.xml are necessary for JSPs to access the tag libraries.
struts-config.xml
The struts-config.xml file, shown next, is the central location for all of a Struts application's configuration settings. Remember from the previous description of the web.xml file that the struts-config.xml file is used by ActionServlet to configure the application. The basic configuration information is covered here, but a complete description will have to wait until you know more about Struts. (A complete discussion of configuration is found in Chapter 16.)
<?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- Form Beans Configuration --> <form-beans> <form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/> </form-beans> <!-- Global Forwards Configuration --> <global-forwards> <forward name="search" path="/search.jsp"/> </global-forwards> <!-- Action Mappings Configuration --> <action-mappings> <action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> </action> </action-mappings> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> </struts-config>
Struts configuration files are XML-based and should conform to the Struts Configuration Document Type Definition (DTD). The struts-config.xml file just shown begins by declaring its use of the Struts Configuration DTD:
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
Next, is the Form Beans Configuration section, which is used to specify all of the ActionForm objects used in your Struts application. In this case, you're only using one Form Bean: SearchForm. The definition of the Form Bean, shown here, allows you to associate a name or alias of 'searchForm' with the SearchForm object:
<form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/>
That way, the application code (i.e., JSPs, Action objects, and so on) will reference 'searchForm' and not 'com.jamesholmes.minihr.SearchForm'. This allows the class definition to change without causing the code that uses the definition to change. The next section of the file, Global Forwards Configuration, lists the forward definitions that your application will have. Forward definitions are a mechanism for assigning a name to the location of a page. For example, for the Mini HR application, you assign the name 'search' to the 'search.jsp' page:
<forward name="search" path="/search.jsp"/>
Similar to Form Beans, the use of forward definitions allows application code to reference an alias and not the location of pages. Note that this section of the file is dedicated to 'Global' forwards, which are made available to the entire Struts application. You can also specify action-specific forwards that are nested in an <action> tag in the config file:
<action ...> <forward .../> </action>
The issue of action-specific forward definitions is examined later in this book. After the Global Forwards Configuration section comes the Action Mappings Configuration section of the file. This section is used to define the Action classes used in your Struts application. Remember from the previous section on SearchAction.java that Action objects are used to handle discreet Controller tasks. Because the SearchAction mapping, shown here, has many settings, each is examined in detail.
<action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> </action>
The first part of the Action Mappings Configuration section defines the path associated with this action. This path corresponds to the URL used to access your Struts application. Remember from the 'web.xml' section that your application is configured to have any URLs ending in .do be handled by ActionServlet. Setting the path to '/search' for this action essentially says that a request to '/search.do' should be handled by SearchAction. Struts removes the .do from the URL (resulting in
'/search') and then looks in the struts-config.xml settings for an Action Mapping that corresponds to the URL. The next <action> attribute, type, specifies the Action class that should be executed when the path is requested. The name attribute corresponds to the name of a Form Bean defined in the struts-config.xml file. In this case, 'searchForm' corresponds to the Form Bean you set up earlier. Using the name attribute tells Struts to populate the specified Form Bean with data from the incoming request. The Action object will then have access to the Form Bean to access the request data. The next two attributes, scope and validate, are related to the Form Bean defined with the name attribute. The scope attribute sets the scope for the Form Bean associated with this action. For example, use 'request' for request scope or 'session' for session scope. The validate attribute is used to specify whether or not the Form Bean defined with the name attribute should have its validate( ) method called after it has been populated with request data. The final <action> attribute, input, is used to inform the Action object what page is being used to 'input' data to (or execute) the action; in this case, it is 'search.jsp'. The last section of the file, Message Resources Configuration, is used to define the location of the ApplicationResources.properties file. Notice that the file is specified using Java's package mechanism: package.package.class (i.e., 'com.jamesholmes.minihr .ApplicationResources'). This allows ActionServlet to load the properties file from the same place that classes are loaded.
ApplicationResources.properties
The ApplicationResources.properties file, shown next, is based on Java's Resource Bundle functionality for externalizing and internationalizing application strings, messages, and labels.
# Label Resources label.search.name=Name label.search.ssNum=Social Security Number # Error Resources error.search.criteria.missing=<li>Search Criteria Missing</li> error.search.ssNum.invalid=<li>Invalid Social Security Number</li> errors.header=<font color="red"><b>Validation Error(s)</b></font><ul> errors.footer=</ul><hr width="100%" size="1" noshade="true">
You'll notice that this file is simply comprised of name-value pairs, where the name is a key and the value is a message corresponding to the key. Each of the name-value pairs is then used by your Struts application whenever a string, message, or label needs to be displayed. Externalizing these strings in a separate file instead of embedding them in your application allows the strings to be changed without having to recompile the application (separation of concerns). Externalizing the strings also allows your application to support internationalization so that it can be tailored to different locales. As you'll see in Chapter 10, I18N with Struts is straightforward and easy with the use of properties files for strings, messages, and labels.
c:\java\jakarta-struts-1.1\lib\struts-html.tld c:\java\jakarta-struts-1.1\lib\struts-logic.tld Assuming that you have installed Struts at c:\java\jakarta-struts-1.1, installed Tomcat at c:\java\jakarta-tomcat-4.1.27, and placed the Mini HR application files at c:\java\MiniHR, the following command line will compile the Mini HR application when run from the c:\java\MiniHR directory: javac -classpath WEB-INF\lib\commons-beanutils.jar; WEB-INF\lib\commons-collections.jar; WEB-INF\lib\commons-lang.jar; WEB-INF\lib\commons-logging.jar; WEB-INF\lib\commons-validator.jar; WEB-INF\lib\digester.jar; WEB-INF\lib\fileupload.jar; WEB-INF\lib\jakarta-oro.jar; WEB-INF\lib\struts.jar; C:\java\jakarta-tomcat-4.1.27\common\lib\servlet.jar WEB-INF\src\com\jamesholmes\minihr\*.java -d WEB-INF\classes Notice that you must specify the path to each .jar file explicitly. Of course, if you update CLASSPATH, this explicit specification is not needed. You should also notice that the compiled code will be placed into the WEB-INF\classes directory, as specified by the -d WEB-INF\classes section of the command line. Remember from the earlier discussion that this is the standard Web Archive directory that J2EE servlet containers will look in for compiled Web application code.
When you first access the http://localhost:8080/MiniHR/ URL, index.jsp will be run, because it was specified as the Welcome File in the web.xml deployment descriptor. From the opening page, select the Search for Employees link. The Search page allows you to search for employees by name or social security number. If you do not enter any search criteria, an error message will be shown on the page. Similarly, if you enter an invalid social security number, an error message will be shown on the page after you click the Search button. Figures 2-2 through 2-5 show Mini HR in action.
SearchAction (Controller) interfaces with the EmployeeSearchService object (Model) to perform the employee search. EmployeeSearchService returns an ArrayList of Employee objects (Model). 5. SearchAction (Controller) forwards control to search.jsp (View). 6. search.jsp (View) uses the ArrayList of Employee objects (Model) to generate a response to the browser. The flow of execution for Mini HR can be generalized for any Struts application as shown here. Figure 2-6 shows the flow execution in graphic form. 1. The browser makes a request to the Struts application that is processed by ActionServlet (Controller). 2. ActionServlet (Controller) populates the ActionForm (View) object with HTML form data and invokes its validate( ) method. 3. ActionServlet (Controller) executes the Action object (Controller). 4. Action (Controller) interfaces with model components and prepares data for view. 5. Action (Controller) forwards control to the JSP (View). 6. JSP (View) uses model data to generate a response to the browser.
4.
Figure 2-6: Flow of execution Remember, the same basic pattern of execution applies to any Struts application. Now that you understand the basic structure of a Struts application and how its components work together, its time to move on to an in-depth examination of Struts. As mentioned at the start, all of the topics presented in this chapter are examined in detail in the chapters that follow.
As you know, Struts is a framework that is used to build applications based on the Model-View-Controller (MVC) architecture. Because the MVC organization is at the foundation of Struts, it is not possible to fully utilize Struts without a clear understanding of each part of the MVC architecture. Therefore, this and the following two chapters examine in depth the Model, View, and Controller portions of a Struts application, beginning in this chapter with the Model.
Figure 3-1: Model layer breakdown Each sublayer does not necessarily represent a separate set of classes, but rather the Models set of responsibilities. You may choose to house a specific functions code for all layers in one large class, or you may break down each sublayer into finegrained objects. The level of object granularity is up to you and whats best and/or
necessary really depends on the size and complexity of your application. The following are the three sublayers:
External interface Composed of code that provides an interface that external code uses to interact with the Model. Business logic Encompasses the bulk of the Model code and provides the business functionality for an application. Data access Composed of code for communicating with an applications data sources such as a database.
package com.jamesholmes.struts; import java.util.ArrayList; public class EmployeeSearchService { /* Hard-coded sample data. Normally this would come from a real data source such as a database. */ private static Employee[] employees =
{ new Employee("Bob Davidson", "123-45-6789"), new Employee("Mary Williams", "987-65-4321"), new Employee("Jim Smith", "111-11-1111"), new Employee("Beverly Harris", "222-22-2222"), new Employee("Thomas Frank", "333-33-3333"), new Employee("Jim Davidson", "444-44-4444") }; // Search for employees by name. public ArrayList searchByName(String name) { ArrayList resultList = new ArrayList(); for (int i = 0; i < employees.length; i++) { if(employees[i].getName().toUpperCase().indexOf(name.toUpperCase ()) != -1) { resultList.add(employees[i]); } } return resultList; } // Search for employee by social security number. public ArrayList searchBySsNum(String ssNum) { ArrayList resultList = new ArrayList(); for (int i = 0; i < employees.length; i++) { if (employees[i].getSsNum().equals(ssNum)) { resultList.add(employees[i]); } } return resultList; } } EmployeeSearchService fulfills all three of the models sublayers: external interface, business logic, and data access. The external interface is defined by the methods searchByName( ) and searchBySsNum( ). The business logic is contained in the implementation to those methods, which finds an employee based on either his or her name or social security number. Data access occurs each time the hard-coded Employee array is used.
In a small, sample application such as Mini HR, there is nothing wrong with implementing the entire Model within EmployeeSearchService. However, in a more complicated application, a class such as this would normally be used as only the external interface to the Model. In this approach, it would house only skeletal searchByName( ) and searchBySsNum( ) methods, which would pass through (or delegate) requests to the business logic sublayer where their actual implementation would exist. For example, the business logic sublayer code could be implemented in a class named EmployeeSearchImpl. Furthermore, EmployeeSearchImpl would then communicate with data access sublayer classes to actually query employee data from a database or such, rather than containing the data access code itself. The Employee class, shown next, is used as a conduit for transferring data to and from the Model. As such, it can be thought of as being part of the Model layer. package com.jamesholmes.struts; public class Employee { private String name; private String ssNum; public Employee(String name, String ssNum) { this.name = name; this.ssNum = ssNum; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setSsNum(String ssNum) { this.ssNum = ssNum; } public String getSsNum() { return ssNum; } }
Typically, an instance of this type is referred to as a Data Transfer Object (DTO) or Value Object (VO) and is part of the external interface sublayer. The Model uses DTOs to send data back through its external interface and to accept data through its external interface. You can think of these classes as interfaces themselves because they specify the format and packaging of the data expected by the Model.
does not contain business logic, such as calculating interest for a banking application or storing items in a shopping cart for an online catalog. The View layer also does not contain any code for persisting data to or retrieving data from a data source. Rather, it is the Model layer that manages business logic and data access. The View layer simply concentrates on the interface. Keeping the Model and View layers separate from one another allows an applications interface to change independent of the Model layer and vice versa. This separation also allows the application to have multiple interfaces (or views). For instance, an application could have a Web interface and a wireless interface. In this case, each interface is separate, but both use the same Model layer code without the Model layer being tied to either interface or either interface having to know about the other interface.
Each of these components is examined in detail in this chapter, but first it is helpful to understand how they fit together in the View layer. JSP pages are at the center of the View components; they contain the HTML that is sent to browsers for users to see and they contain JSP library tags. The library tags are used to retrieve data from Form Beans and to generate HTML forms that, when submitted, will populate Form Beans. Additionally, library tags are used to retrieve content from resource bundles. Together, all of Struts View layer components are used to generate HTML that browsers render. This is what the user sees. On the back side, the View layer populates Form Beans with data coming from the HTML interface. The Controller layer then takes the Form Beans and manages getting their data and putting it into the Model layer. Additionally, the Controller layer takes data from the Model layer and populates Form Beans so that the data can be presented in the View layer.
The following sections explain each of these major View components in detail.
JSP Pages
JSPs are the centerpiece of the Struts View layer. They contain the static HTML and JSP library tags that generate dynamic HTML. Together the static and dynamically generated HTML gets sent to the users browser for rendering. That is, the JSPs contain the code for the user interface with which a user interacts. JSPs in Struts applications are like JSPs in any other Java-based Web application. However, to adhere to the MVC paradigm, the JSPs should not contain any code for performing business logic or code for directly accessing data sources. Instead, the JSPs are intended to be used solely for displaying data and capturing data. Struts provides a set of tag libraries that supports displaying data and creating HTML forms that capture data. Additionally, the tags support displaying content stored in resource bundles. Therefore, JSPs (coupled with Form Beans) provide the bulk of the Struts View layer. The JSP tag libraries glue those two together and the resource bundles provide a means of content management.
Form Beans
Form Beans provide the conduit for transferring data between the View and Controller layers of Struts applications. When HTML forms are submitted to a Struts application, Struts takes the incoming form data and uses it to populate the forms corresponding Form Bean. The Struts Controller layer then uses the Form Beans to access data that must be sent to the Model layer. On the flip side, the Controller layer populates Form Beans with Model layer data so that it can be displayed with the View layer. Essentially, Form Beans are simple data containers. They either contain data from an HTML form that is headed to the Model via the Controller or contain data from the Model headed to the View via the Controller. Form Beans are basic Java beans with getter and setter methods for each of their properties, allowing their data to be set and retrieved easily. The org.apache.struts .action.ActionForm class is the base abstract class that all Form Beans must subclass. Because Form Beans are simple data containers, they are principally comprised of fields, and setter and getter methods for those fields. Business logic and data access code should not be placed in these classes. That code goes in Model layer classes. The only other methods that should be in these classes are helper methods or methods that override ActionForms base reset( ) and validate( ) methods. The ActionForm class has a reset( ) method and a validate( ) method that are intended to be overridden by subclasses where necessary. The reset( ) method is a hook that Struts calls before the Form Bean is populated from an HTML form submission. The validate( ) method is a hook that Struts calls after the Form Bean has been populated from an HTML form submission. Both of these methods are described in detail later in this section. Following is an example Form Bean:
import org.apache.struts.action.ActionForm;
public class EmployeeForm extends ActionForm { private String firstName; private String lastName; private String department; public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getLastName() { return lastName; } public void setDepartment(String department) { this.department = department; } public String getDepartment() { return department; } }
Form Bean properties can be of any object type, be it a built-in class like String or a complex application-specific class such as an Address object that has fields for street address, city, state, and ZIP. Struts uses reflection to populate the Form Beans and can traverse object hierarchies to any level so long as the getter and setter methods are public. For example, if your Form Bean had an Address object field named address, to access the city field on the Address object the Form Bean would need a public getAddress( ) method that returned an Address object. The Address object would need a public getCity( ) method that would return a String. Often, its best to have Form Bean fields be Strings instead of other types. For example, instead of having an Integer-type field for storing a number, its best to use a String-type field. This is because all HTML form data comes in the form of strings. If a letter rather than a number is entered in a numeric field, its better to store the value in a String so that the original data can be returned to the form for correcting. If instead the data is stored in a Long, when Struts attempts to convert the string value to a number, it will throw a NumberFormatException if the value is a letter. Then, when the form is redisplayed showing the invalid data, it will show 0 instead of the originally entered value, because letters cannot be stored in numeric-type fields. Configuring Form Beans To use Form Beans, you have to configure them in the Struts configuration file. Following is a basic Form Bean definition:
<!-- Form Beans Configuration --> <form-beans> <form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/> </form-beans>
Form Bean definitions specify a logical name and the class type for a Form Bean. Once defined, Form Beans are associated with actions by action mapping definitions, as shown next:
<!-- Action Mappings Configuration --> <action-mappings> <action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> </action> </action-mappings>
Actions specify their associated Form Bean with the name attribute of the action tag, as shown in the preceding snippet. The value specified for the name attribute is the logical name of a Form Bean defined with the form-bean tag. The action tag also has a scope attribute to specify the scope that the Form Bean will be stored in and a validate attribute to specify whether or not the Form Beans validate( ) method should be invoked after the Form Bean is populated. The reset( ) Method As previously stated, the abstract ActionForm class has a reset( ) method that subclasses can override. The reset( ) method is a hook that gets called before a Form Bean is populated with request data from an HTML form. This method hook was designed to account for a shortcoming in the way browsers handle check boxes. Browsers send the value of a check box only if it is checked when the HTML form is submitted. For example, consider an HTML form with a check box for whether or not a file is read-only:
<input type="checkbox" name="readonly" value="true">
When the form containing this check box is submitted, the value of true is sent to the server only if the check box is checked. If the check box is not checked, no value is sent. For most cases, this behavior is fine; however, it is problematic when Form Bean boolean properties have a default value of true. For example, consider the read-only file scenario again. If your application has a Form Bean with a read-only property set to true and the Form Bean is used to populate a form with default settings, the readonly property will set the read-only check boxs state to checked when it is rendered. If a user decides to uncheck the check box and then submits the form, no value will be sent to the server to indicate that the check box has been unchecked (i.e., set to false). By using the reset( ) method, this can be solved by setting all properties tied to check boxes to false before the Form Bean is populated. Following is an example
implementation of a Form Bean with a reset( ) method that accounts for unchecked check boxes:
import org.apache.struts.action.ActionForm; public class FileForm extends ActionForm { private boolean readOnly; public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } public boolean getReadOnly() { return readOnly; } public void reset() { readOnly = false; } }
The reset( ) method in this example class ensures that the readOnly property is set to false before the form is populated. Having the reset( ) method hook is equivalent to having the HTML form actually send a value for unchecked check boxes. A side benefit of the reset( ) method hook is that it offers a convenient place to reset data between requests when using Form Beans that are stored in session scope. When Form Beans are stored in session scope, they persist across multiple requests. This solution is most often used for wizard-style process flows. Sometimes its necessary to reset data between requests, and the reset( ) method provides a convenient hook for doing this. The validate( ) Method In addition to the reset( ) method hook, the ActionForm class provides a validate( ) method hook that can be overridden by subclasses to perform validations on incoming form data. The validate( ) method hook gets called after a Form Bean has been populated with incoming form data. Following is the method signature for the validate( ) method:
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request)
Notice that the validate( ) method has a return type of ActionErrors. The org.apache.struts.action.ActionErrors class is a Struts class that is used for storing validation errors that have occurred in the validate( ) method. If all validations in the validate( ) method pass, a return value of null indicates to Struts that no errors occurred. Note Form Bean data validations could be performed in action classes; however, having them in Form Beans allows them to be reused across multiple actions where more than one action uses the same Form Bean. Having the validation
code in each action would be redundant. Following is an example Form Bean with a validate( ) method:
import javax.servlet.http.HttpServletRequest; import import import import org.apache.struts.action.ActionError; org.apache.struts.action.ActionErrors; org.apache.struts.action.ActionForm; org.apache.struts.action.ActionMapping;
public class NameForm extends ActionForm { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { if (name == null || name.length() < 1) { ActionErrors errors = new ActionErrors(); errors.add("name", new ActionError("error.name.required")); return errors; } return null; } }
This example Form Bean has one field, name, that is validated in the validate( ) method. The validate( ) method checks whether or not the name field is empty. If it is empty, it returns an error indicating that fact. The ActionErrors object is basically a collection class for storing org.apache.struts.action.ActionError instances. Each validation inside the validate( ) method creates an ActionError instance that gets stored in the ActionErrors object. The ActionError class takes a key to an error message stored in the Struts resource bundle. Struts uses the key to look up the corresponding error message. The ActionError class also has constructors that take additional arguments that contain replacement values for the error message associated with the specified key. Struts also has a built-in Validator framework that greatly simplifies performing data validations. The Validator framework allows you to declaratively configure in an XML file the validations that should be applied to Form Beans. For more information on the Validator framework, see Chapter 6. Note The validate( ) method will be called only if the Form Bean has been configured for validation in the Struts configuration file.
The Lifecycle of Form Beans Form Beans have a defined lifecycle in Struts applications. To fully understand how Form Beans work, its necessary to understand this lifecycle. The Form Bean lifecycle is shown in Figure 4-1.
Figure 4-1: The Form Bean lifecycle Following is an explanation of the Form Bean lifecycle. When a request is received by the Struts controller servlet, Struts maps the request to an action class that is delegated to process the request. If the action being delegated to has an associated Form Bean, Struts attempts to look up the specified Form Bean in request or session scope, based on how the action is configured in the Struts configuration file. If an instance of the Form Bean is not found in the specified scope, an instance is created and placed in the specified scope. Next, Struts calls the reset( ) method on the Form Bean so that any processing is executed that needs to occur before the Form Bean is populated. After that, Struts populates the Form Bean with data from the incoming request. Next, the Form Beans validate( ) method is called. The next step in the process is based on the return value from the validate( ) method. If the validate( ) method records any errors and subsequently returns a non-null ActionErrors object, Struts forwards back to the actions input page. If, however, the return value from the validate( ) method is null, Struts continues processing the request by calling the actions execute( ) method. DynaActionForms A useful addition to the 1.1 release of Struts was the introduction of Dynamic Form Beans. Dynamic Form Beans are an extension of Form Beans that allows you to specify their properties inside the Struts configuration file instead of having to create a concrete class, with a getter and setter method for each property. The concept of Dynamic Form Beans originated because many developers found it tedious to create for every page a Form Bean that had a getter and setter method for each of the fields on the pages HTML form. Using Dynamic Form Beans allows the properties to be specified in a Struts configuration file. To change a property, simply update the configuration file. No code has to be recompiled.
The following snippet illustrates how Dynamic Form Beans are configured in the Struts configuration file:
<!-- Form Beans Configuration --> <form-beans> <form-bean name="employeeForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="firstName" type="java.lang.String"/> <form-property name="lastName" type="java.lang.String"/> <form-property name="department" type="java.lang.String"/> </form-bean> </form-beans>
Dynamic Form Beans are declared in the same way as standard Form Beans, by using the form-bean tag. The difference is that the type of the Form Bean specified with the form-bean tags type attribute must be org.apache.struts.action.DynaActionForm or a subclass thereof. Additionally, the properties for Dynamic Form Beans are specified by nesting form-property tags beneath the form-bean tag. Each property specifies its name and class type. Additionally, an initial value for the property can be specified using the form-property tags initial attribute, as shown next:
<form-property name="department" type="java.lang.String" initial="Engineering"/>
If an initial value is not supplied for a property, Struts sets the initial value using Javas initialization conventions. That is, numbers are set to zero and objects are set to null. Because you declare Dynamic Form Beans in the Struts configuration file instead of creating concrete classes that extend ActionForm, you do not define reset( ) or validate( ) methods for the Dynamic Form Beans. The reset( ) method is no longer necessary for setting default values because the initial attribute on the form-property tag achieves the same effect. The DynaActionForm classs implementation of the reset( ) method resets all properties to their initial value when it is called. You can either code the functionality of the validate( ) method inside action classes or use the Validator framework for validation. These two options eliminate the need to create a validate( ) method on the Form Bean. If, however, you have a special case where you need to have an implementation of the reset( ) and/or validate( ) method for your Dynamic Form Bean, you can subclass DynaActionForm and create the methods there. Simply specify your DynaActionForm subclass as the type of the Form Bean in the Struts configuration file to use it.
things such as conditional logic, iterating over collections, and so on. With the advent of the JSP Standard Tag Library (JSTL), many of the utility tags have been superceded. (Using JSTL with Struts is covered in Chapter 15.) Following is a list of the Struts tag libraries and their purpose:
HTML Used to generate HTML forms that interact with the Struts APIs. Bean Used to work with Java bean objects in JSPs, such as to access bean values. Logic Used to cleanly implement simple conditional logic in JSPs. Nested Used to allow arbitrary levels of nesting of the HTML, Bean, and Logic tags that otherwise do not work.
Later in this book, each of these libraries has an entire chapter dedicated to its use, but this section provides a brief introduction to using the tag libraries, focusing on Struts core JSP tag library, the HTML Tag Library, as an example. This library is used to generate HTML forms that, when submitted, populate Form Beans. Additionally, the HTML Tag Library tags can create HTML forms populated with data from Form Beans. To use the HTML Tag Library in a Struts application, you need to include a snippet like the following in your applications Web Archive (.war) deployment descriptor, web.xml:
<taglib> <taglib-uri>/WEB-INF/tlds/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-html.tld</taglib-location> </taglib>
This snippet sets up the HTML Tag Library. For information on setting up the other tag libraries, see their respective chapters. Recall from the overview of the web.xml file in Chapter 2 that the <taglib-uri> tag is used to declare the URI (or alias) that will be referenced in each of your JSPs with a taglib directive. The <taglib-location> tag is used to declare the actual location of the Tag Library Descriptor (.tld) file in your Web Archive. The following snippet illustrates how your JSPs declare their use of the HTML Tag Library with a JSP taglib directive:
<%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %>
Notice that the uri attribute specified here is the same as that declared with the <taglib-uri> tag in the web.xml file. Also, notice that the prefix attribute is set to html. This attribute can be set to whatever you want; however, html is the accepted default for the HTML Tag Library. The prefix attribute declares the prefix that each tag must have when it is used in the JSP, as shown here:
<html:form action="/Search">
Because html is defined as the prefix, the form tag is used as shown. However, if you were to choose to use a prefix of struts-html, the tag would be used as follows:
<struts-html:insert attribute="header"/>
Resource Bundles
Resource bundles allow Java applications to be easily internationalized by having application content placed into bundles. This content can then be read by the application at run time. Therefore, instead of having content hard-coded in the application, the application reads its content from the bundle. A side benefit of using resource bundles to store application content (whether for internationalization or not) is that the content can be changed without having to recompile the application. Additionally, bundles serve as a central repository for content that is common to multiple uses (i.e., multiple applications). Having content in a central repository reduces unnecessary duplication. Struts has built-in support for working with Javas resource bundle mechanism. Having this support allows the Struts framework to seamlessly support application internationalization as well as have a mechanism for externalizing content so that it can be easily changed without having to modify JSPs or application code. Struts uses resource bundle resources throughout the framework. For example, resource bundle resources can be accessed from JSPs to populate them with content. Similarly, action objects can access content stored in resource bundles to do such things as generate error or informational messages that get displayed onscreen. Struts Form Bean validation mechanism is also tied to resource bundles for managing error messages. Actually, there are several uses for resource bundles throughout Struts. The rest of this section explains how to create a resource bundle properties file and configure Struts to use it. An example of accessing resource bundle content from a JSP is also shown. Later chapters provide specific information about how to use resource bundles in the context of those chapters topics. Using resource bundles in Struts is as easy as creating a properties file to store the resources in, and then configuring Struts to use the properties file. Once this is done, accessing the resources is straightforward. Following is a very simple resource bundle properties file containing a few properties:
page.title=Employee Search link.employeeSearch=Search for Employees link.addEmployee=Add a New Employee
Resource bundle properties files simply contain key/value pairs. The resources are accessed by their key. The standard name for the resource bundle properties file in Struts is ApplicationResources.properties. In order for Struts to be able to load this file, it must be stored on your applications classpath somewhere. The following snippet configures the resource bundle with Struts:
<!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/>
The parameter attribute of the message-resources tag specifies the fully qualified classpath for the resource bundle properties file minus the .properties file extension. Once a properties file has been created and configured in the Struts configuration file, the resources in the bundle can be accessed from several places in the Struts framework. The most common place is in JSPs. The following snippet illustrates how to use the Bean Tag Librarys message tag to load a message from the resource bundle:
<%@ taglib uri="/WEB-INF/tlds/struts-bean.tld" prefix="bean" %> <html> <head> <title><bean:message key="page.title"/></title> </head> <body>
The value specified with the message tags key attribute is the key for a message in the resource bundle. At run time, Struts retrieves the message and places it in the JSP. Note Detailed information on internationalizing Struts applications is found in Chapter 10.
private List results = null; public void setName(String name) { this.name = name; } public String getName() { return name; } public void setSsNum(String ssNum) { this.ssNum = ssNum; } public String getSsNum() { return ssNum; } public void setResults(List results) { this.results = results; } public List getResults() { return results; } // Reset form fields. public request) { name = null; ssNum = null; results = null; } // Validate form data. public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); boolean nameEntered = false; boolean ssNumEntered = false; void reset(ActionMapping mapping, HttpServletRequest
// Determine if name has been entered. if (name != null && name.length() > 0) { nameEntered = true; } // Determine if social security number has been entered. if (ssNum != null && ssNum.length() > 0) { ssNumEntered = true; } /* Validate that either name or social security number has been entered. */ if (!nameEntered && !ssNumEntered) { errors.add(null, new ActionError("error.search.criteria.missing")); } /* Validate format of social security number if it has been entered. */ if (ssNumEntered && !isValidSsNum(ssNum.trim())) { errors.add("ssNum", new ActionError("error.search.ssNum.invalid")); } return errors; } // Validate format of social security number. private static boolean isValidSsNum(String ssNum) { if (ssNum.length() < 11) { return false; } for (int i = 0; i < 11; i++) { if (i == 3 || i == 6) { if (ssNum.charAt(i) != '-') { return false; } } else if ("0123456789".indexOf(ssNum.charAt(i)) == -1) { return false; } }
return true; } } The SearchForm class is a basic Form Bean with a few properties and implementations for the reset( ) and validate( ) method hooks. Mini HR uses this Form Bean to capture search criteria from the search page using the name and ssNum fields. The results field is used to transfer search results back to the search page after a search has been performed. The index.jsp page, shown next, is a simple JSP used as an example menu page for linking to Mini HRs functions: <%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> <html> <head> <title>ABC, Inc. Human Resources Portal</title> </head> <body> <font size="+1">ABC, Inc. Human Resources Portal</font><br> <hr width="100%" noshade="true"> • Add an Employee<br> • <html:link Employees</html:link><br> </body> </html> This page is the opening page for the Mini HR application and provides a link to the Mini HR search page. The search.jsp page shown here provides the core interface to the Mini HR search functionality. It serves as the search criteria page as well as the search results page. <%@ taglib uri="/WEB-INF/tlds/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/tlds/struts-logic.tld" prefix="logic" %> <html> <head> <title>ABC, Inc. Human Resources Portal - Employee Search</title> </head> <body> <font size="+1"> ABC, Inc. Human Resources Portal - Employee Search </font><br> forward="search">Search for
<hr width="100%" noshade="true"> <html:errors/> <html:form action="/search"> <table> <tr> <td align="right"><bean:message key="label.search.name"/>:</td> <td><html:text property="name"/></td> </tr> <tr> <td></td> <td>-- or --</td> </tr> <tr> <td align="right"><bean:message key="label.search.ssNum"/>:</td> <td><html:text property="ssNum"/> (xxx-xx-xxxx)</td> </tr> <tr> <td></td> <td><html:submit/></td> </tr> </table> </html:form> <logic:present name="searchForm" property="results"> <hr width="100%" size="1" noshade="true"> <bean:size id="size" name="searchForm" property="results"/> <logic:equal name="size" value="0"> <center><font color="red"><b>No Employees Found</b></font></center> </logic:equal> <logic:greaterThan name="size" value="0"> <table border="1"> <tr> <th>Name</th> <th>Social Security Number</th> </tr> <logic:iterate id="result" name="searchForm" property="results">
<tr> <td><bean:write name="result" property="name"/></td> <td><bean:write name="result" property="ssNum"/></td> </tr> </logic:iterate> </table> </logic:greaterThan> </logic:present> </body> </html> This page uses Struts tag library tags to determine whether or not it is being executed before or after a search has been submitted. If the page is being displayed before a search, it simply displays the search criteria form. However, if the page is being displayed after a search, it displays the search criteria form in addition to the search results. The ApplicationResources.properties resource bundle file, shown next, is used by the SearchForm Form Bean and the JSPs to retrieve externalized content from a central repository: # Label Resources label.search.name=Name label.search.ssNum=Social Security Number # Error Resources error.search.criteria.missing=<li>Search Criteria Missing</li> error.search.ssNum.invalid=<li>Invalid Social Security Number</li> errors.header=<font color="red"><b>Validation Error(s)</b></font><ul> errors.footer=</ul><hr width="100%" size="1" noshade="true"> The SearchForm Form Bean uses this file to store validation error messages. The JSPs use this file to store field labels. Together they are able to leverage Struts built-in resource bundle mechanism for externalizing content. Doing so allows for easy updates to the content and provides a simple interface for internationalizing the content if necessary.
Technology stxx
Description and URL Struts for transforming XML with XSL (stxx) is a third-party extension of the Struts framework that supports using XSLT (XML Style Language Templates) for the View layer.http://stxx.sourceforge.net/ Java's Swing library can be used to create rich GUI front ends for Struts applications. The following article illustrates how to do this.http://javaboutique.internet.com/tutorials/Swing/ The Jakarta Velocity project is a Java-based templating engine that can be used as an alternative to JSPs. VelocityStruts is a Velocity subproject that integrates Velocity with the Struts framework.http://jakarta.apache.org/velocity/tools/struts/
Swing
Velocity
Upon receiving a request, ActionServlet delegates its processing to the RequestProcessor class. The RequestProcessor class processes all aspects of the request, including selecting the Form Bean associated with the request, populating the Form Bean with data, validating the Form Bean, and then selecting the correct Action class to execute for the request. The Action class is where the Struts framework ends and your application code begins. Action classes provide the glue between the View and Model layers. Figure 5-1 illustrates the Controller layer lifecycle.
Figure 5-1: The Controller layer lifecycle The following sections explain each of the major Controller layer components in detail.
<init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>/do/*</url-pattern> </servlet-mapping> </web-app> Path mapping routes to ActionServlet all requests that match a specified path. The default path is /do/*, as shown in the preceding web.xml file; however, you can use any path that you like. The second way to map requests to ActionServlet is to use extension mapping, as shown next: <?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servletclass> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern>
</servlet-mapping> </web-app> Extension mapping maps to ActionServlet all requests with the specified extension. The default extension to use is .do; however, you can use any extension you like. Note Extension mapping is required if you are using Struts module feature. For more information on modules, see Chapter 9.
processCachedMessages( )
processValidate( )
Invokes the validate( ) method on the Form Bean returned from processActionForm( ) if necessary.
Table 5-1: The process*( ) Methods of the RequestProcessor Class Method processForward( ) Description Processes the forward for the action mapping matching the current request path, if the matching mapping is specified to be a forward. Processes the include for the action mapping matching the current request path, if the matching mapping is specified to be an include. Creates or recycles an existing action to process the current request. Invokes the execute( ) method on the action returned from processActionCreate( ). Forwards to the forward processActionPerform( ). returned from
processInclude( )
By having each phase of the request processing cycle take place in a separate method, request processing can easily be customized. Simply create a custom request processor that extends the base RequestProcessor class and override the methods that need to be customized. For example, a custom request processor can apply a logged-in security check before any action is executed. The RequestProcessor class provides the processPreprocess( ) method hook expressly for this. The processPreprocess( ) method is called before actions are executed. The following example shows how to do this: package com.jamesholmes.example; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.RequestProcessor; public class LoggedInRequestProcessor extends RequestProcessor { protected boolean processPreprocess( HttpServletRequest request, HttpServletResponse response) { // Check if user is logged in. // If so return true to continue processing, // otherwise return false to not continue processing. return (true); } } To use a custom request processor, you have to configure Struts to use it in the Struts configuration file: <controller processorClass="com.jamesholmes.example.LoggedInRequestProcessor"/>
Note
When using Struts module feature, each module has its own request processor. Thus, if you want to apply a custom request processor to all modules, you must configure it in each modules Struts configuration file.
Which URL is used to access the action depends on how ActionServlet is configured in web.xml. If ActionServlet is configured to use path mapping, the action defined in the preceding example is accessed as http://localhost:8080/MiniHR/do/UpdateUser, assuming a server of localhost running on port 8080 and an application deployed as MiniHR. If extension mapping were used to configure ActionServlet, the URL would be http://localhost:8080/MiniHR/UpdateUser.do. When an action is called to process a request, its execute( ) method is invoked. The execute( ) method is analogous to the service( ) method in servlets. It handles all processing. Following is an example Action subclass and its execute( ) method:
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import org.apache.struts.action.Action; org.apache.struts.action.ActionForm; org.apache.struts.action.ActionForward; org.apache.struts.action.ActionMapping;
public class UpdateUserAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Perform request processing here.
} }
org.apache.struts.actions.ForwardAction org.apache.struts.actions.IncludeAction
org.apache.struts.actions.LocaleAction
org.apache.struts.actions.LookupDispatchAction Provides a mechanism for modularizing a set of related functions into a single action instead of having to create separate, independent actions for each function. org.apache.struts.actions.MappingDispatchAction Provides a mechanism for modularizing a set of related functions into a single action instead of having to create separate, independent actions for each function. org.apache.struts.actions.SwitchAction Provides a mechanism for switching between modules in a modularized Struts application.
The following sections describe each of the built-in actions in detail. The org.apache.struts.actions.DispatchAction Class The DispatchAction class provides a mechanism for modularizing a set of related functions into a single action, and thus eliminates the need to create separate,
independent actions for each function. For example, consider a set of related functions for adding a user, updating a user, and removing a user. Instead of creating an AddUserAction class, an UpdateUserAction class, and a RemoveUserAction class, by extending DispatchAction, you can create one UserAction class that has three methods: add( ), update( ), and remove( ). At run time, DispatchAction manages routing requests to the appropriate method in its subclass. DispatchAction determines which method to call based on the value of a request parameter that is passed to it from the incoming request. To use DispatchAction, you must create a subclass from it and provide a set of methods that will be called to process requests. Additionally, you have to set up for the action an action mapping that specifies the name of a request parameter that will be used to select which method should be called for each request. Following is an example UserAction class that extends DispatchAction:
package com.jamesholmes.example; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import org.apache.struts.action.ActionForm; org.apache.struts.action.ActionForward; org.apache.struts.action.ActionMapping; org.apache.struts.actions.DispatchAction;
public class UserAction extends DispatchAction { public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Add user. return new ActionForward("success"); } public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Update user. return new ActionForward("success"); } public ActionForward remove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Remove user.
Notice that this class does not provide an implementation for the execute( ) method the way typical Action classes do. This is because DispatchAction provides to you an implementation of this method that manages delegating to the individual methods. In order for your DispatchAction subclass to work, you must create in the Struts configuration file an action mapping that specifies the name of a request parameter that will be used to select the method that will be called for specific requests. Followingis a sample snippet that illustrates how to do this:
<action-mappings> <action path="/User" type="com.jamesholmes.example.UserAction" parameter="function"/> </action-mappings>
The value specified with the parameter attribute of the action tag will be used as the name of a request parameter that will contain the name of a method to invoke for handling the request. Given the preceding mapping of /User to UserAction, the following URL will invoke the add( ) method (assuming the application was run on your localhost at port 8080 and the application was deployed as /MiniHR/): http://localhost:8080/MiniHR/User.do?function=add To invoke the remove( ) method, use the following URL: http://localhost:8080/MiniHR/User.do?function=remove The org.apache.struts.actions.ForwardAction Class The ForwardAction class provides a mechanism for forwarding to a specified URL. As explained earlier, in an MVC Web application, all requests to the application are supposed to flow through the Controller servlet. This ensures that the Controller layer of the application has an opportunity to prepare any resources that may be needed to handle the request (i.e., selecting the correct module and so on). ForwardAction is provided as a simple utility action that can be used for scenarios in which you simply want to link to a JSP page. Of course, linking directly to the JSP would be a violation of the MVC principles because all requests are supposed to be routed through the Controller. ForwardAction can be used to create links to JSPs so that you dont have to create an action whose only responsibility is to forward a request every time you want to link to a JSP. With ForwardAction, you simply create an action mapping in the Struts configuration file and specify the location to which the action will forward. To use ForwardAction, simply create action mapping entries in the Struts configuration file, as shown next:
<action-mappings> <action path="/menu"
For each page to which you want to link, you must create an action mapping. Each action mapping uses ForwardAction, but specifies a different path for the action. The parameter attribute specifies the URL that will be forwarded to when the specified path is accessed. The org.apache.struts.actions.IncludeAction Class The IncludeAction class provides a mechanism for including the contents of a specified URL. This action behaves similarly to ForwardAction, but instead of forwarding to the specified URL, the specified URL is included. This action is useful when you want to include the contents of one page in another. Using IncludeAction is quite easy. Just create action mapping entries in the Struts configuration file:
<action-mappings> <action path="/menu" type="org.apache.struts.actions.IncludeAction" parameter="/menu.jsp/> </action-mappings>
For each page you want to include, you must create an action mapping. Each action mapping uses IncludeAction, but specifies a different path for the action. The parameter attribute specifies the URL that will be included when the specified path is accessed. The org.apache.struts.actions.LocaleAction Class The LocaleAction class provides a mechanism for setting a users locale and then forwarding to a specified page. This action provides a convenient mechanism for changing a users locale. For example, consider a site that is offered in English and Spanish versions. LocaleAction can be used to offer users a way to switch between the two languages without having to change their browser settings. With LocaleAction you simply create an action mapping and then link to the action, specifying request parameters for which locale to switch to and a page to forward after the locale has been switched. To use LocaleAction, you must create an action mapping entry for it in the Struts configuration file and then link to the action, specifying locale information and a page to forward to after the locale has been set. Following is an example of how to configure LocaleAction in the Struts configuration file:
<action-mappings> <action path="/SwitchLocale" type="org.apache.struts.actions.LocaleAction"/> </action-mappings>
Once configured in the Struts configuration file, LocaleAction can be put to use. Simply create a link to the action and specify the locale settings that will be set and a page to forward to. Locale settings are specified with two request parameters: language and country. The page to forward to after setting the locale is specified with the page request parameter. The following URL illustrates how to use the request parameters: http://localhost:8080/MiniHR/SwitchLocale.do?country=MX&language=es&page=/ Menu.do This example URL sets the country to MX and the language to es. The /Menu.do page will be forwarded to after the new locale has been set. The org.apache.struts.actions.LookupDispatchAction Class LookupDispatchAction is a subclass of DispatchAction. The LookupDispatchAction class provides a mechanism for modularizing a set of related functions into a single action, thus eliminating the need to create separate, independent actions for each function. For example, consider a set of related functions for adding a user, updating a user, and removing a user. Instead of creating an AddUserAction class, an UpdateUserAction class, and a RemoveUserAction class, by extending MappingDispatchAction, you can create one UserAction class that has three methods: add( ), update( ), and remove( ). At run time, LookupDispatchAction manages routing requests to the appropriate method in its subclass. LookupDispatchAction determines which method to route to based on the value of a request parameter being passed to it from the incoming request. LookupDispatchAction uses the value of the request parameter to reversemap to a property in the Struts resource bundle file, ApplicationResources.properties. That is, the value of the request parameter is compared against the values of properties in the resource bundle until a match is found. The key for the matching property is then used as a key to another map that maps to a method in your LookupDispatchAction subclass that will be executed. To use LookupDispatchAction, you must create a subclass from it and provide a set of methods that will be called to process requests. The subclass must also include a getKeyMethodMap( ) method that maps methods in the class to keys in the Struts resource bundle file. Additionally, you have to set up for the action an action mapping that specifies the name of a request parameter that will be used to select which method will be called for each request. Following is an example UserAction class that extends DispatchAction:
package com.jamesholmes.example; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import org.apache.struts.action.ActionForm; org.apache.struts.action.ActionForward; org.apache.struts.action.ActionMapping; org.apache.struts.actions.DispatchAction;
public class UserAction extends LookupDispatchAction { protected Map getKeyMethodMap() { HashMap map = new HashMap(); map.put("button.add", "add"); map.put("button.update", "update"); map.put("button.remove", "remove"); return map; } public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Add user. return new ActionForward("success"); } public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Update user. return new ActionForward("success"); } public ActionForward remove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Remove user. return new ActionForward("success"); } }
Notice that this class does not provide an implementation for the execute( ) method as other Action classes do. This is because DispatchAction provides to you an implementation of this method that manages delegating to the individual methods. Notice also the implementation of the getKeyMethodMap( ) method. This method is required by LookupDispatchAction subclasses and is used to map the names of keys in the Struts resource bundle file to methods in the class. The keys values in the bundle file are used to match against the value of the incoming request parameter specified by the parameter attribute of the action tag in the Struts configuration file.
In order for your LookupDispatchAction subclass to work, you must create in the Struts configuration file an action mapping that specifies the name of a request parameter that will be used to select the method that will be called for a specific request. Following is a sample snippet that illustrates how to do this:
<action-mappings> <action path="/User" type="com.jamesholmes.example.UserAction" parameter="function"/> </action-mappings>
The value specified with the parameter attribute of the action tag will be used as the name of a request parameter that will contain the value of a key in the Struts resource bundle shown here:
button.add=Add User button.update=Update User button.remove=Remove User
LookupDispatchAction will use the value of the incoming request parameter to perform a reverse lookup for a key in the resource bundle. The matching key is then mapped to the appropriate method to execute based on the key-to-method mapping specified by the getKeyMethodMap( ) method. Given the preceding mapping of /User to UserAction, the following URL will invoke the add( ) method (assuming the application was run on your localhost at port 8080 and the application was deployed as /MiniHR/): http://localhost:8080/MiniHR/User.do?function=Add%20User To invoke the remove( ) method, use the following URL: http://localhost:8080/MiniHR/User.do?function=Remove%20User The org.apache.struts.actions.MappingDispatchAction Class MappingDispatchAction is a subclass of DispatchAction. The MappingDispatch Action class provides a mechanism for modularizing a set of related functions into a single action, eliminating the need to create separate, independent actions for each function. For example, consider a set of related functions for adding a user, updating a user, and removing a user. Instead of creating an AddUserAction class, an Update UserAction class, and a RemoveUserAction class, by extending MappingDispatch Action, you can create one UserAction class that has three methods: add( ), update( ), and remove( ). At run time, MappingDispatchAction manages routing requests to the appropriate method in its subclass. MappingDispatchAction determines which method to route to based on the value of a parameter being passed to it from an action mapping in the Struts configuration file. To use MappingDispatchAction, you must create a subclass from it and provide a set of methods that will be called to process requests. Additionally, you must set up
action mappings that specify which method will be called for each request. Following is an example UserAction class that extends MappingDispatchAction:
package com.jamesholmes.example; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import org.apache.struts.action.ActionForm; org.apache.struts.action.ActionForward; org.apache.struts.action.ActionMapping; org.apache.struts.actions.MappingDispatchAction;
public class UserAction extends MappingDispatchAction { public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Add user. return new ActionForward("success"); } public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Update user. return new ActionForward("success"); } public ActionForward remove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Remove user. return new ActionForward("success"); } }
Notice that this class does not provide an implementation for the execute( ) method as other Action classes do. This is because MappingDispatchAction provides to you an implementation of this method that manages delegating to the individual function methods.
In order for your MappingDispatchAction subclass to work, you must create in the Struts configuration file action mappings that specify the method that will be called for specific requests. Following is a sample snippet that illustrates how to do this:
<action-mappings> <action path="/AddUser" type="com.jamesholmes.example.UserAction" parameter="add"/> <action path="/UpdateUser" type="com.jamesholmes.example.UserAction" parameter="update"/> <action path="/RemoveUser" type="com.jamesholmes.example.UserAction" parameter="remove"/> </action-mappings>
Notice that each action mapping uses the UserAction class, but specifies a different path for the action. Each of the unique paths will be processed by the same action, but a different method will be called based on the value specified with the parameter attribute. The value specified with the parameter attribute must match the name of a method in your MappingDispatchAction subclass. The org.apache.struts.actions.SwitchAction Class The SwitchAction class provides a mechanism for switching between modules in a modularized Struts application. As youll see in Chapter 9, Struts enables you to modularize your Struts application. Each module has its own set of configuration data as well as its own request processor. SwitchAction works similarly to ForwardAction, except that before forwarding to a specified resource, the action changes the currently selected module. This is useful for forwarding to JSPs outside of the current module. Note Detailed information on using Struts modules feature is found in Chapter 9. To use SwitchAction, you must create an action mapping entry for it in the Struts configuration file and then link to the action, specifying the module to switch to and a page to forward to after the module has been switched. Following is an example of how to configure SwitchAction in the Struts configuration file:
<action-mappings> <action path="/SwitchModule" type="org.apache.struts.actions.SwitchAction"/> </action-mappings>
Once configured in the Struts configuration file, SwitchAction can be put to use. Simply create a link to the action and specify the module to switch to and a page to forward to afterward. The module to switch to is specified with the prefix parameter and the page to forward to afterward is specified with the page parameter. The following URL illustrates how to use the request parameters: http://localhost:8080/MiniHR/SwitchModule.do?prefix=/Corporate&page=/Menu.do
This example URL switches to the /Corporate module, and the /Menu.do page will be forwarded to after the module has been switched.
import org.apache.struts.action.ActionMapping; public class UpdateUserAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Perform action processing. return new ActionForward("updateSuccess"); } } The value passed to the ActionForward classs constructor corresponds to the logical name of a forward defined in the Struts configuration file.
{ EmployeeSearchService service = new EmployeeSearchService(); ArrayList results; SearchForm searchForm = (SearchForm) form; // Perform employee search based on what criteria was entered. String name = searchForm.getName(); if (name != null && name.trim().length() > 0) { results = service.searchByName(name); } else { results = service.searchBySsNum(searchForm.getSsNum().trim()); } // Place search results in SearchForm for access by JSP. searchForm.setResults(results); // Forward control to this Action's input page. return mapping.getInputForward(); } } When a search is initiated, Struts ActionServlet Controller servlet delegates processing to this Action class. This class acts as the liaison between the Model layer and the View layer. Based on the search criteria entered from the View layer, SearchAction determines which search method to invoke on the EmployeeSearchService Model class and passes in the data from the View layer. EmployeeSearchService returns the results of the search and SearchAction manages getting the data back to the View layer.
Chapter 6: Validator
Overview
One of the major benefits of using the Struts framework is its built-in interface for performing data validations on incoming form data. As discussed in Chapter 4, upon submitting an HTML form, Struts captures the form data and uses it to populate one of your applications ActionForm subclasses (Form Bean) assigned to the form. The Form Beans validate( ) method is then called to perform any necessary validation of the incoming data. If any validations fail, the HTML form is redisplayed so that the invalid data can be corrected. Otherwise, processing continues. This simple interface alleviates much of the headache associated with handling data validation, allowing you to focus on validation code and not the mechanics of capturing data and redisplaying incomplete or invalid data. Struts built-in validation interface, however, still has its shortcomings. Often, for example, validation code is heavily duplicated throughout an application because many fields require the same validation logic. Any change in the validation logic for similar fields requires code changes in several places, as well as recompilation of the affected code. To solve this problem and to enhance Struts validation interface, David Winterfeldt created the Validator framework as a third-party add-on to Struts. Validator was later integrated into the core Struts code base and has since been detached from Struts and is now a stand-alone Jakarta Commons project that can be used with or without Struts. Although Validator is an independent framework again, Struts still comes packaged with it and it is seamlessly integrated. The Validator framework comes prepackaged with several validation routines, making the transition from hard-coded validation logic painless. Instead of coding validation logic in each Form Beans validate( ) method, with Validator you use an XML configuration file to declare the validations that should be applied to each Form Bean. If you need a validation not provided by Validator, you can plug your own custom validations into Validator. Additionally, Validator supports both server-side and client-side (JavaScript) validations whereas Form Beans only provide a serverside validation interface.
Validator Overview
Before getting into the details of using the Validator framework, its necessary to give an overview of how Validator works. Recall that without Validator, you have to code all of your form data validations into the validate( ) methods of your Form Bean objects. Each Form Bean field that you want to perform a validation on requires you to code logic to do so. Additionally, you have to write code that will store error messages for validations that fail. With Validator, you dont have to write any code in your Form Beans for validations or storing error messages. Instead, your Form Beans extend one of Validators ActionForm subclasses that provide this functionality for you. The Validator framework is set up as a pluggable system of validation routines that can be applied to Form Beans. Each validation routine is simply a Java method that is responsible for performing a specific type of validation and can either pass or fail. By default, Validator comes packaged with several useful validation routines that will satisfy most validation scenarios. However, if you need a validation that is not provided by the framework, you can create your own custom validation routine and plug it into the framework. Validator uses two XML configuration files to tell it which validation routines should be installed and how they should be applied for a given application, respectively. The first configuration file, validator-rules.xml, declares the validation routines that are plugged into the framework and assigns logical names to each of the validations. Additionally, the validator-rules.xml file is used to define client-side JavaScript code for each validation routine. If configured to do so, Validator will emit this JavaScript code to the browser so that validations are performed on the client side as well as the server side. The second configuration file, validation.xml, defines which validation routines are applied to which Form Beans. The definitions in this file use the logical names of Form Beans from the strutsconfig.xml file along with the logical names of validation routines from the validatorrules.xml file to tie the two together.
Using Validator
Using the Validator framework involves enabling the Validator plugin, configuring Validator's two configuration files, and creating Form Beans that extend Validator's ActionForm subclasses. The following sections explain how to configure and use the Validator in detail.
way to ensure that you are properly ordering elements in the file is to use a tool like Struts Console that automatically formats your configuration file so that it conforms to the DTD.
Configuring validator-rules.xml
The Validator framework is set up as a pluggable system whereby each of its validation routines is simply a Java method that is plugged into the system to perform a specific validation. The validator-rules.xml file is used to declaratively plug in the validation routines that Validator will use for performing validations. Struts' example applications come packaged with preconfigured copies of this file. Under most circumstances, you will use these preconfigured copies and will not need to modify them unless you are adding your own custom validations to the framework. Following is a sample validator-rules.xml file that illustrates how validation routines are plugged into Validator: <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <form-validation> <global> <validator name="required" classname="org.apache.struts.validator.FieldChecks" method="validateRequired" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionErrors, javax.servlet.http.HttpServletRequest" msg="errors.required"> <javascript> <![CDATA[ function validateRequired(form) { var isValid = true; var focusField = null; var i = 0; var fields = new Array(); oRequired = new required(); for (x in oRequired) { var field = form[oRequired[x][0]]; if (field.type == 'text' || field.type == 'textarea' || field.type == 'file' || field.type == 'select-one' || field.type == 'radio' ||
field.type == 'password') { var value = ''; // get field's value if (field.type == "select-one") { var si = field.selectedIndex; if (si >= 0) { value = field.options[si].value; } } else { value = field.value; } if (trim(value).length == 0) { if (i == 0) { focusField = field; } fields[i++] = oRequired[x][1]; isValid = false; } } } if (fields.length > 0) { focusField.focus(); alert(fields.join('\n')); } return isValid; } // Trim whitespace from left and right sides of s. function trim(s) { return s.replace( /^\s*/, "" ).replace( /\s*$/, "" ); } ]]> </javascript> </validator> </global> </form-validation>
Each validation routine in the validator-rules.xml file has its own definition that is declared with a validator tag. The validator tag is used to assign a logical name to the routine, with the name attribute, and to specify the class and method for the routine. The logical name given to the routine will be used to refer to the routine by other routines in this file as well as by validation definitions in the validation.xml file. Notice that the validator tag encapsulates a javascript tag. The javascript tag is used to define client-side JavaScript code for performing the same validation on the client side as is performed on the server side.
Included Validations
By default, Validator comes packaged with several basic validation routines that you can use to solve most validation scenarios. As mentioned, the sample applications provided by Struts come packaged with preconfigured validator-rules.xml files that define these routines. Table 6-1 lists each of the preconfigured validations by logical name and states its purpose. Table 6-1: Validator's Preconfigured Validations Validation byte creditCard date double email float floatRange integer intRange long mask maxlength minlength range required requiredif short Note Description Validates that the specified field contains a valid byte. Validates that the specified field contains a valid credit card number. Validates that the specified field contains a valid date. Validates that the specified field contains a valid double. Validates that the specified field contains a valid e-mail address. Validates that the specified field contains a valid float. Validates that the specified field contains a valid float and falls within the specified range. Validates that the specified field contains a valid integer. Validates that the specified field contains a valid integer and falls within the specified range. Validates that the specified field contains a valid long. Validates that the specified field conforms to a given regular expression (or 'mask'). Validates that the string in the specified field's length is less than or equal to the specified maximum length. Validates that the string in the specified field's length is greater than or equal to the specified minimum length. Deprecated Use the intRange validation instead. Validates that the specified field contains characters other than white space (i.e., space, tab, and newline characters). Performs the required validation on the specified field if a specified rule is met. Validates that the specified field contains a valid short. Detailed information on configuring the validator-rules.xml file is found in Chapter 18.
<form-bean name="logonForm" type="com.jamesholmes.minihr.LogonForm"/> </form-beans> The logical name given to the Form Bean with the form-bean tag's name attribute is the name that you will use when defining validations in the validation.xml file, as shown here: <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <form-validation> <formset> <form name="logonForm"> <field property="username" depends="required"> <arg0 key="prompt.username"/> </field> </form> </formset> </form-validation> Validator uses the value of the form tag's name attribute to match validation definitions to the name of the Form Bean to which they are applied. The second path you can choose when creating your Form Bean is to define a Dynamic Form Bean in the struts-config.xml file, as shown here: <form-beans> <form-bean name="logonForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans> Dynamic Form Beans do not require you to create concrete Form Bean objects; instead, you define the properties that your Form Bean should have and their types, and Struts will dynamically create the Form Bean for you. Validator allows you to use this concept just as you would with core Struts. The only difference for Validator is that you specify that your Form Bean is of type org.apache.struts.validator.DynaValidatorForm instead of org.apache.struts.action.DynaActionForm. Identical to the way concrete Form Beans work with Validator, the logical name given to Dynamic Form Beans is the name that you will use when defining validations in the validation.xml file. Validator uses the matching names to tie the validations to the Form Bean. In addition to the two standard options for creating Form Beans, Validator provides an advanced feature for tying multiple validation definitions to one Form Bean definition. When using ValidatorForm- or DynaValidatorForm-based Form Beans, Validator uses the logical name for the Form Bean from the struts-config.xml file to map the Form Bean to validation definitions in the validation.xml file. This mechanism is ideal for most cases; however, there
are scenarios where Form Beans are shared among multiple actions. One action may use all the Form Bean's fields and another action may use only a subset of the fields. Because validation definitions are tied to the Form Bean, the action that uses only a subset of the fields has no way of bypassing validations for the unused fields. When the Form Bean is validated, it will generate error messages for the unused fields because Validator has no way of knowing not to validate the unused fields; it simply sees them as missing or invalid. To solve this problem, Validator provides two additional ActionForm subclasses that allow you to tie validations to actions instead of Form Beans. That way you can specify which validations to apply to the Form Bean based on which action is using the Form Bean. For concrete Form Beans, you subclass org.apache.struts.validator.ValidatorActionForm, as shown here: public class AddressForm extends ValidatorActionForm { } For Dynamic Form Beans, you specify a type of org.apache.struts.validator.DynaValidatorActionForm for your Form Bean definition in the struts-config.xml file: <form-bean name="addressForm" type="org.apache.struts.validator.DynaValidatorActionForm"> </form-bean> Inside your validation.xml file, you map a set of validations to an action path instead of a Form Bean name, because if you have two actions defined, Create Address and Edit Address, which use the same Form Bean, as shown here, each has a unique action path: <action-mappings> <action path="/createAddress" type="com.jamesholmes.minihr.CreateAddressAction" name="addressForm"/> <action path="/editAddress" type="com.jamesholmes.minihr.EditAddressAction" name="addressForm"/> </action-mappings> The following validation.xml file snippet shows two sets of validations that are intended for the same Form Bean, but are distinguished by different action paths: <formset> <form name="/createAddress"> <field property="city" depends="required"> <arg0 key="prompt.city"/> </field> </form> <form name="/editAddress"> <field property="state" depends="required"> <arg0 key="prompt.state"/> </field> </form>
</formset> Because your Form Bean subclasses either ValidatorActionForm or DynaValidatorActionForm, Validator knows to use an action path to find validations instead of the Form Bean's logical name.
Configuring validation.xml
The validation.xml file is used to declare sets of validations that should be applied to Form Beans. Each Form Bean that you want to validate has its own definition in this file. Inside that definition you specify the validations that you want to apply to the Form Bean's fields. Following is a sample validation.xml file that illustrates how validations are defined: <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <form-validation> <formset> <form name="logonForm"> <field property="username" depends="required"> <arg0 key="prompt.username"/> </field> <field property="password" depends="required"> <arg0 key="prompt.password"/> </field> </form> </formset> </form-validation> The first element in the validation.xml file is the <form-validation> element. This element is the master element for the file and is defined only once. Inside the <form-validation> element you define <form-set> elements that encapsulate multiple <form> elements. Generally, you will define only one <form-set> element in your file; however, you would use a separate one for each locale if you were internationalizing validations. Each <form> element uses the name attribute to associate a name to the set of field validations it encompasses. Validator uses this logical name to map the validations to a Form Bean defined in the struts-config.xml file. Based on the type of Form Bean being validated, Validator will attempt to match the name either against a Form Bean's logical name or against an action's path. Inside the <form> element, <field> elements are used to define the validations that will be applied to specified Form Bean fields. The <field> element's property attribute corresponds to the name of a field in the specified Form Bean. The depends attribute specifies the logical names of validation routines from the validator-rules.xml file that should be applied to the field. The validations specified with the depends attribute will be performed in the order specified and they all must pass. Note Detailed information on configuring the validation.xml file is found in Chapter 18.
Configuring ApplicationResources.properties
Validator uses Struts' Resource Bundle mechanism for externalizing error messages. Instead of having hard-coded error messages in the framework, Validator allows you to specify a key to a message in the ApplicationResources.properties file that is returned if a validation fails. Each validation routine in the validator-rules.xml file specifies an error message key with the validator tag's msg attribute, as shown here: <validator name="required" classname="org.apache.struts.validator.FieldChecks" method="validateRequired" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionErrors, javax.servlet.http.HttpServletRequest" msg="errors.required"> If the validation fails when it is run, the message corresponding to the key specified by the msg attribute will be returned. The following snippet shows the default set of validation error messages from the ApplicationResources.properties file that comes prepackaged with Struts' example applications. Each message key corresponds to those specified by the validation routines in the validator-rules.xml file that also comes prepackaged with Struts' example applications: # Error messages for Validator framework validations errors.required={0} is required. errors.minlength={0} cannot be less than {1} characters. errors.maxlength={0} cannot be greater than {2} characters. errors.invalid={0} is invalid. errors.byte={0} must be a byte. errors.short={0} must be a short. errors.integer={0} must be an integer. errors.long={0} must be a long. errors.float={0} must be a float. errors.double={0} must be a double. errors.date={0} is not a date. errors.range={0} is not in the range {1} through {2}. errors.creditcard={0} is not a valid credit card number. errors.email={0} is an invalid e-mail address. Notice that each message has placeholders in the form of {0}, {1}, or {2}. At run time, the placeholders will be substituted for another value such as the name of the field being validated. This feature is especially useful in allowing you to create generic validation error messages that can be reused for several different fields of the same type. Take for example the required validation's error message, errors.required: errors.required={0} is required. When you use the required validation in the validation.xml file, you have to define the value that should be used to substitute {0} in the error message: <form name="auctionForm"> <field property="bid" depends="required">
<arg0 key="prompt.bid"/> </field> </form> Error messages can have up to four placeholders: {0} - {3}. These placeholders are known as arg0 - arg3, respectively, and can be specified using the arg0 - arg3 tags. In the preceding example, the arg0 tag specifies the value that should be used to replace the {0} placeholder. This tag's key attribute specifies a message key from the ApplicationResources.properties file, such as the one shown next, whose value will be used as the replacement for the placeholder: prompt.bid=Auction Bid Using a message key for the placeholder value frees you from having to hard-code the replacement value over and over in the validation.xml file. However, if you don't want to use the Resource Bundle key/value mechanism to specify placeholder values, you can explicitly specify the placeholder value by using the following syntax for the arg0 tag: <arg0 key="Auction Bid" resource="false"/> In this example, the resource attribute is set to 'false', telling Validator that the value specified with the key attribute should be taken as the literal placeholder value and not as a key for a message in the ApplicationResources.properties file. Note Detailed information on the ApplicationResources.properties file is found in Chapter 10.
All the validations that you have specified for the <form> definition to run on the server side will be run on the client side.
Validator comes packaged with several useful validations that will suit most validation scenarios; however, your application may require a specific validation that is not provided by the framework. For this situation, Validator provides a simple interface for creating your own custom validations that can easily be plugged into the framework. Following is a list of the steps you need to take to create your own custom validation: 1. 2. 3. 4. Create a new validation method. Add a new validation rule to the validator-rules.xml file. Add new validation definitions to the validation.xml file. Add messages to the ApplicationResources.properties file.
The following sections walk through each step of the process in detail, enabling you to create a custom validation based on the Social Security Number validation used in the example Mini HR application in Chapter 2. Remember that the Social Security Number validation in Chapter 2 was defined inside of the SearchForm Form Bean class. Creating a custom validation for social security numbers enables the validation code to be reused and to be used declaratively instead of being hard-coded in each Form Bean that wants to use it.
Of course, the name of your method will vary, but its arguments should match the types shown in the preceding example. Table 6-2 explains each of the validation methods arguments. Table 6-2: The Validation Method Arguments Argument java.lang.Object Description The Form Bean object (down casted to Object) contains the field to be validated.
org.apache.commons.validator.ValidatorAction The ValidatorAction object encapsulates the <validator> definition from the validatorrules.xml file for this validation routine. org.apache.commons.validator.Field The Field object encapsulates the
Table 6-2: The Validation Method Arguments Argument Description <field> definition from the validation.xml file for the field that is currently being validated. org.apache.struts.action.ActionErrors The ActionErrors object stores validation error messages for the field that is currently being validated. The HTTPServletRequest object encapsulates the current HTTP request.
javax.servlet.http.HttpServletRequest
Following is the custom validation code for validating social security numbers. Notice that the validateSsNum( ) method conforms to the proper method signature for custom validations.
package com.jamesholmes.minihr; import import import import import import javax.servlet.http.HttpServletRequest; org.apache.commons.validator.Field; org.apache.commons.validator.ValidatorAction; org.apache.commons.validator.ValidatorUtil; org.apache.struts.action.ActionErrors; org.apache.struts.validator.Resources;
public class MiniHrValidator { public static boolean validateSsNum(Object bean, ValidatorAction action, Field field, ActionErrors errors, HttpServletRequest request) { String value = ValidatorUtil.getValueAsString(bean, field.getProperty()); if (value == null || value.length() < 11) { errors.add(field.getKey(), Resources.getActionError(request, action, field)); return false; } for (int i = 0; i < 11; i++) { if (i == 3 || i == 6) { if (value.charAt(i) != '-') { errors.add(field.getKey(), Resources.getActionError(request, action, field)); return false; } } else if ("0123456789".indexOf(value.charAt(i)) == -1) { errors.add(field.getKey(), Resources.getActionError(request, action, field)); return false; } }
return true; } }
The validateSsNum( ) method begins by retrieving the value for the field being validated. The value is retrieved by determining the fields name with a call to field.getProperty( ) and then looking up that field in the Form Bean with a call to ValidatorUtil.getValueAsString( ). The getValueAsString( ) method matches the name of the field with the name of one of the Form Bean fields and then gets that fields value. The rest of the validateSsNum( ) method performs the actual validation logic. If the validation fails for any reason, an error message will be stored in the errors ActionErrors object.
} else if ("0123456789".indexOf( value.substring(n, n+1)) == -1) { bRightFormat = false; } } if (!bRightFormat) { if (i == 0) { focusField = form[oSsNum[x][0]]; } fields[i++] = oSsNum[x][1]; bValid = false; } } } if (fields.length > 0) { focusField.focus(); alert(fields.join('\n')); } return bValid; } ]]> </javascript> </validator>
As you can see, the validation rule applies a name to the validation with the name attribute; specifies the class that the validation method is housed in with the classname attribute; and specifies the validation methods arguments with the methodParams attribute. The msg attribute specifies a key that will be used to look up an error message in the ApplicationResources.properties file if the validation fails. Note that the name applied with the name attribute is the logical name for the rule and will be used to apply the rule to definitions in the validation.xml file. The preceding custom validation rule also defines JavaScript code, inside the opening and closing <javascript> elements, which will be used if client-side validation is enabled when using the rule. The JavaScript code simply performs the same validation on the client side as is performed on the server side. If the JavaScript validation fails, it will alert the user and prevent the HTML form from being submitted. Note that validation rules must be placed underneath the <global> element in the validator-rules.xml file, as shown here:
<form-validation> <global> <validator name="ssnum" /> </global> </form-validation>
Of course, the order of the <validator> elements underneath the <global> element is arbitrary, so you can place the new ssnum validation rule anywhere.
Once you have defined your custom validation rule in the validator-rules.xml file, you can make use of it by referencing its logical name in the validation.xml file:
<!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <form-validation> <formset> <form name="searchForm"> <field property="ssNum" depends="required,ssNum"> <arg0 key="prompt.ssNum"/> </field> </form> </formset> </form-validation>
In the preceding validation definition, each of the comma-delimited values of the field tags depends attribute corresponds to the logical name of a validation rule defined in the validation-rules.xml file. The use of ssNum instructs Validator to use the custom Social Security Number validation. Each time you want to use the new Social Security Number validation, you simply have to add its logical name to the depends attribute of a field tag.
Remember that the errors.ssNum message key was specified by the msg attribute of the validator tag for the custom validation rule in the validator-rules.xml file. The keys corresponding message will be used if the validation fails. The prompt.ssNum message key was specified by the arg0 tag of the validation definition in the validation.xml file. Its corresponding message will be used as the parametric replacement for the errors.ssNum messages {0} parameter. Thus, if the Social Security Number custom validation fails, the following error message will be generated by substituting the prompt.ssNum message for {0} in the errors.ssNum message:
Social Security Number is not a valid Social Security Number
Internationalizing Validations
Similar to other areas of Struts, Validator fully supports internationalization. Remember that internationalization is the process of tailoring content to a specific locale or region. In Validators case, internationalization means tailoring validation error messages to a specific locale and/or tailoring actual validation routines to a specific locale. This way, the U.S. and
French versions of a Web site can each have their own language-specific validation error messages. Similarly, internationalization enables the U.S. and French versions of a Web site to validate entries in monetary fields differently. The U.S. version requires commas to separate dollar values and a period to demarcate cents (i.e., 123,456.78), whereas the French (Euro monetary system) version requires periods to separate dollar amounts and a comma to demarcate cents (i.e., 123.456,78). Tailoring validation error messages to a specific locale is built into Struts by way of its Resource Bundle mechanism for externalizing application strings, messages, and labels. You simply create an ApplicationResources.properties file for each locale you want to support. Each locale-specific properties file will have a locale identifier at the end of the filename that denotes which locale it is for, such as ApplicationResources_ja.properties for Japan. (For detailed information on internationalizing a Struts application, see Chapter 10.) Thus, when Validator goes to load an error message, it will use the locale for the current request to determine which properties file to load the message from. Remember that each validation rule in the validator-rules.xml file specifies a key for a validation error message stored in the ApplicationResources.properties files. This key is the same across each locales properties file, thus allowing Validator to load the appropriate message based on only a locale and a key. Tailoring validation routines to specific locales is similar to tailoring error messages; you have to define validation definitions in the validation.xml file for each locale. The validation.xml file contains a form-validation tag that contains one or more formset tags, which in turn contain one or more form tags, and so on: <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <form-validation> <formset> <form name="auctionForm"> <field property="bid" depends="mask"> <var> <var-name>mask</var-name> <var-value>^\d{1,3}(,?\d{3})*\.?(\d{1,2})?$</var-value> </var> </field> </form> </formset> </form-validation> The form-set tag takes optional attributes, country and language, to tailor its nested forms to a specific locale. In the preceding example, all locales use the same currency validation to validate the bid property, because neither the country attribute nor the language attribute was specified for the form-set tag. In order to have a generic currency validation for all users of the Web site and a Euro-specific currency validation for users from France, youd create an additional form-set definition specifically for the French users, as shown here: <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons
Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <form-validation> <formset> <form name="auctionForm"> <field property="bid" depends="required,mask"> <var> <var-name>mask</var-name> <var-value>^\d{1,3}(,?\d{3})*\.?(\d{1,2})?$</var-value> </var> </field> </form> </formset> <formset country="fr"> <form name="auctionForm"> <field property="bid" depends="required,mask"> <var> <var-name>mask</var-name> <var-value>^\d{1,3}(\.?\d{3})*,?(\d{1,2})?$</var-value> </var> </field> </form> </formset> </form-validation> In this listing, the second <formset> definition specifies a country attribute set to fr. That instructs Validator to use the enclosed validation definitions for users with a French locale. Notice that the bid validation in the second <formset> definition is different from the first definition, as the period and comma are transposed in the mask value. Thus, the first <formset> definition validates that bids are in U.S. currency format and the second definition validates that bids are in Euro currency format. A powerful feature of using internationalized <formset> definitions is that you can define only the validations that are locale-specific, and all other validations are taken from the default <formset> definition.
The following sections walk through each step of the process in detail.
} } Notice that this file no longer has the reset( ), validate( ), and validateSsNum( ) methods and that the class been updated to extend ValidatorForm.
<plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/> </plug-in> Notice that each of the configuration files is specified with the set-property tag. The following snippet lists the updated Struts configuration file for Mini HR in its entirety. <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- Form Beans Configuration --> <form-beans> <form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/> </form-beans> <!-- Global Forwards Configuration --> <global-forwards> <forward name="search" path="/search.jsp"/> </global-forwards> <!-- Action Mappings Configuration --> <action-mappings> <action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> </action> </action-mappings> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> <!-- Validator Configuration --> <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,
to
the
Recall from earlier in this chapter that each validation routine defined in the validatorrules.xml file declares a key for an error message in Struts resource bundle file: ApplicationResources.properties. At run time, Validator uses the keys to look up error messages to return when validations fail. Because you are using the mask validation defined in the validator-rules.xml file, you must add the following error message for its declared key to the ApplicationResources.properties file: errors.invalid=<li>{0} is not valid</li> The following code shows the updated ApplicationResources.properties file in its entirety: # Label Resources label.search.name=Name label.search.ssNum=Social Security Number # Error Resources error.search.criteria.missing=<li>Search Criteria Missing</li> error.search.ssNum.invalid=<li>Invalid Social Security Number</li> errors.header=<font color="red"><b>Validation Error(s)</b></font><ul> errors.footer=</ul><hr width="100%" size="1" noshade="true"> errors.invalid=<li>{0} is not valid</li>
C:\java\jakarta-tomcat-4.1.27\common\lib\servlet.jar WEB-INF\src\com\jamesholmes\minihr\*.java -d WEB-INF\classes After recompiling Mini HR, you need to repackage it using the following command line: jar cf MiniHR.war * This command should also be run from the directory where you have set up the Mini HR application (e.g., c:\java\MiniHR). Similar to the way you ran Mini HR the first time, you now need to place the new MiniHR.war file that you just created into Tomcats webapps directory and start Tomcat. As before, to access the Mini HR application, point your browser to http://localhost:8080/MiniHR/. Once you have the updated Mini HR running, try entering valid and invalid social security numbers. As you will see, they are now verified using the new Validator code.
Chapter 7: Tiles
Overview
Two of the foremost principles that underpin the Struts framework are the separation of concerns and reuse. These principles can be seen at work throughout the framework. For example, the separation of business logic into a Model layer applies separation of concerns, and the use of a configuration file for declarative coupling of components applies reuse. These principles are not, however, unique to the server side (Model and Controller) of applications. Client-side (View) development has also been evolving to support the principles of separation of concerns and reuse. For example, cascading style sheets (CSS) allows common style elements to be defined in an external file that can be sourced in (that is, its contents included) by several pages. That way, each page does not have to define the same styles over and over. Instead, the pages simply use the common definitions defined in a style sheet. If a style needs to be changed, that change can be made in one
file rather than in each page individually. This reduces the amount of time and overhead needed to make global changes, and reduces errors. This is the essence of reuse. JSP technology supports a similar feature for reuse called includes. JSP includes allow other files (JSPs or otherwise) to be sourced in to a JSP, either at compile time or dynamically at run time. This feature is useful for abstracting common sections of pages, such as headers, footers, and menus, into reusable chunks that can be used by several files. If a change needs to be made to a common section, the change can be made once and each of the pages that includes that section will automatically receive the updates. JSP includes offer both convenience and time savings. They are, however, somewhat limited insofar as the potential for a great deal of duplication still exists. Each JSP that sources in common sections duplicates the include definitions. If the name of one or more files being included change, each of the files including them would have to be updated. For example, assume that you have a set of JSPs called a.jsp, b.jsp, c.jsp, and d.jsp that all include header.jsp. If you rename header.jsp to mainHeader.jsp, then you would have to update a.jsp, b.jsp, c.jsp, and d.jsp to include mainHeader.jsp instead of header.jsp. This can be tedious, time consuming, and error prone. To solve this problem, and to enhance Struts, Cedric Dumoulin created the Tiles framework as a third-party add-on to Struts. Tiles has since been integrated into the core Struts code base and is now packaged with Struts as of version 1.1. Tiles expands the concept of reuse via includes by allowing you to define layouts (or templates) and then specify how the layouts are populated with content. To understand the value of Tiles, first consider how the JSP include paradigm works. Each JSP specifies its layout and explicitly populates that layout through includes. Most JSP layouts are identical, sourcing in the same files in the same places and then having a section of unique content, which is usually body content. Thus, there is significant duplication. Tiles takes the reverse approach. With Tiles, you define a master layout JSP that specifies each of the includes that fill in the layout and then you define which content should fill in the layout in an external configuration file. The same layout can be used over and over by simply specifying different filler content for the layout in the configuration file. For example, consider a typical Web site layout that has a header at the top of the page, a menu on the left, body content in the middle, and a footer on the bottom, as shown in Figure 7-1. If you were to implement this page using only JSP includes, each JSP that has this layout would have to explicitly include the header, menu, and footer sections of the page, and the body content would be in the JSP itself. Essentially, the only unique part of the page is the body content.
Figure 7-1: Typical Web site layout Alternatively, if you were to implement this layout with Tiles, you'd create one JSP that includes the header, menu, and footer and then dynamically include the body based on a parameter passed to the layout that indicates which JSP to use for the body content. This Tiles layout could then be reused for as many pages as you'd like and the only thing your content JSPs would have to contain is the body content that goes in the middle of the page. Tiles takes care of wrapping the body content JSP with the layout. As you can see, Tiles significantly enhances JSP development and allows for an even greater amount of reuse than JSP includes offer.
Tiles Overview
Before getting into the details of using the Tiles framework, its necessary to give an overview of how Tiles works. Tiles allows you to exploit the concept of JSP includes by providing a framework for defining and dynamically populating page layouts. Each page layout is simply a JSP that defines a template frame (or outline) with placeholders for where content should go. At run time, Tiles replaces the placeholders with their associated content, creating a complete page and unique instance of the layout. To accomplish this, Tiles uses its concepts of definitions and attributes. A Tiles definition creates a piece of content that Tiles can insert into a JSP using that definitions name. Each definition consists of a name (or identifier), a layout JSP, and a set of attributes associated with the definition. Once defined, a definition can be included in a page or, as is most often the case, be used as the target of a Struts forward. In both cases, when the definition is encountered, Tiles passes to the layout JSP specified by the definition the set of attributes that were declared for that definition. An attribute value can be the path to a JSP, a literal string, or a list of either. To facilitate the use of definitions and attributes, Tiles uses an XML configuration file (tilesdefs.xml) for storing their definitions. Tiles also provides a JSP tag library for defining definitions and attributes. Additionally, the Tiles Tag Library is used for inserting attributes into JSPs.
Using Tiles
Using the Tiles framework involves these five steps: 1. Enable the Tiles plugin. 2. Create Tiles definitions. 3. Create layout JSPs and use the Tiles Tag Library. 4. Create content JSPs to fill in the layout JSPs. 5. Use the Tiles definitions. The following sections explain how to configure and use Tiles in detail.
Each definition in the tiles-defs.xml file has its own definition that is declared with a definition tag. The definition tag assigns a logical name to the definition, with the name attribute, and specifies the path to a layout JSP for the definition, with the path attribute. The logical name given to the definition will be used to refer to the definition inside JSPs and the Struts configuration file. Nested inside the definition tag, put tags are used to specify the definition's list of attributes. Note Detailed information on the Tiles configuration file is found in Chapter 17.
Extending Definitions
A powerful and often-used feature of definitions is the ability to have one definition extend another. This functionality is similar to the way inheritance works in Java. When defining a definition, you can specify that the definition extends another definition, instead of specifying a layout JSP for the definition. The child (or sub-) definition inherits the layout JSP and attributes of the parent and can override any of the parent attributes as well as add its own attributes. For example, you may want to have a master layout definition for your site that all other definitions extend from. The master definition can specify values for the attributes that are common across all pages. The child definitions can then specify values for just the attributes that are unique to that page. Additionally, child definitions can override any of the master definitions' common attribute values when necessary, such as for a page that needs a unique header or footer. Following is an example Tiles configuration file that illustrates a few definition-extending scenarios: <?xml version="1.0"?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">
<tiles-definitions> <!-- Main Layout --> <definition name="main.layout" path="/mainLayout.jsp"> <put name="title" <put name="menu" <put name="body" </definition> <!-- Search Page --> <definition name="search.page" extends="main.layout"> <put name="title" <put name="body" </definition> <!-- Employee Layout --> <definition name="employee.layout" extends="main.layout"> <put name="menu" </definition> <!-- Employee Edit Page --> <definition name="employeeEdit.page" extends="employee.layout"> <put name="title" <put name="body" </definition> </tiles-definitions> As stated, this file contains a few different extension scenarios. First, the file declares the main layout from which the other definitions will extend. This layout specifies values for the attributes that will be common across most pages. However, it purposefully leaves the pagespecific attributes' values blank, because they will be overridden by extending definitions. The first extending definition, search.page, extends the main layout definition and provides values for the title and body attributes. The rest of the attributes that are necessary to fill in the main layout will be inherited from the main layout definition. At run time, when the search.page definition is used, Tiles will use the mainLayout.jsp file and populate it with content from the child and parent definitions, as appropriate. The second definition-extension scenario represented in the preceding configuration file creates a specific layout by extending the generic main layout and customizing it. The employee.layout definition extends the main layout and overrides the menu attribute's value to create a new employee-specific layout. Note that the menu attribute points to an employee-specific menu JSP. The employeeEdit.page definition extends the employee.layout layout definition. This final definition, like the search.page definition, specifies values for the title and body attributes. The rest of the attribute values that are necessary to populate the layout JSP are inherited from the parent definition. value="Employee Edit Page"/> value="/employee/edit.jsp"/> value="/employee/menu.jsp"/> value="Search Page"/> value="/search.jsp"/> value=""/> value="/menu.jsp"/> value=""/> <put name="header" value="/header.jsp"/>
Note that Tiles supports an essentially unlimited number of definition extensions. Thus, you could create a generic master definition followed by several more specific definitions that extend it. Then, you could add yet another level of definitions that extend the extended definitions to create even more specific page definitions. This is a very powerful feature and is the key advantage that Tiles has over JSP includes.
Tiles will use a JSP. If the attribute is set to the name of a definition, Tiles will recognize that and use the definition. This feature allows you to create sophisticated layouts that maximize content reuse.
Once you have created Tiles layout JSPs and have defined definitions that use them, you can put the definitions to use. There are two different ways to use definitions: you can insert the definition's content into a JSP, or you can use definitions as the targets of Struts forwards. The following example illustrates how to insert a Tiles definition into a JSP: <%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %> <html> <head> <title>Employee Search</title> </head> <body> <font size="+1">ABC, Inc. Human Resources Portal</font><br> <hr width="100%" noshade="true"> <tiles:insert definition="search.page"/> <hr width="100%" noshade="true"> </body> </html> As you can see, to insert a definition, you just use the Tiles Tag Library's insert tag. The content specified by the definition will be processed and inserted in its final state (i.e., all levels of layouts will be collapsed into one). This way of using definitions is very powerful because you can insert definitions that comprise a page's whole content, as shown next. Or you could insert definitions that are only intended to fill a portion of a page with content, as was shown in the first example. <%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %> <tiles:insert definition="search.page"/> The second way to use Tiles definitions is to have them be used as the target of Struts forwards. This way works by having the names of definitions be used as forwards. Instead of a forward pointing to a JSP or another URL, it points to the name of a Tiles definition. At run time, when the Tiles plugin is enabled, Tiles intercepts all requests being made through the Struts controller servlet. Tiles does this by having its own request processor that extends the behavior of the base Struts request processor. If the Tiles request processor sees a forward whose value is the name of a definition, it will handle the request and return the contents of the definition. Following is an example of how to define a forward in the Struts configuration file that uses a Tiles definition: <global-forwards> <forward name="search" path="search.page"/> </global-forwards> Notice that the path attribute specifies the name of a Tiles definition. The Tiles request processor will recognize this and handle processing for this forward.
Internationalizing Tiles
Similar to other areas of Struts, Tiles fully supports internationalization. Remember that internationalization is the process of tailoring content to a specific locale or region. For Tiles, internationalization means tailoring Tiles definitions and attributes to a specific locale. That way, the U.S. and French versions of a Web site can each have their own language-specific definitions and attributes, for example. Although Tiles internationalization support is similar to the core internationalization support in Struts, its intended to allow you to provide locale-specific layout differences, not to internationalize text or messages. That should be done with the core Struts internationalization features. Tiles internationalization support is for such things as the differences in the size of internationalized images or the length of internationalized text. For example, using Tiles internationalization support, you can define one version of a definition that handles the appropriate sizing for images and text for the United States and a second version designed for France, which accommodates the larger images and longer text necessary for the French content. Tiles simply provides the mechanism to define the layout appropriately for each locale. Note Internationalizing text and images should be left to the core Struts internationalization functionality. To tailor Tiles definitions and attributes to a specific locale, you have to create a Tiles XML configuration file for each locale. Each locale-specific configuration file is distinguished by its filename using the same naming scheme that is used for Java Resource Bundles. Thus, to create English and French versions of a set of definitions and their attributes, you would create a file named tiles-defs_en.xml for the English version and a file named tiles-defs_fr.xml for the French version. The suffix portion of each filename contains the language code for the locale that the file is intended for. At run time, Tiles uses the locale from the current request to determine which configuration file it should use definitions from to process the current request. Each configuration file should have the same definitions with the same attributes. All of the names of definitions and attributes must be the same (that is, line up) in each file. The values of the definitions and attributes, however, will be specific to the locale of the particular file. Parallel to the way Java Resource Bundles function, Tiles will attempt to find definitions and attributes for the current requests locale in a configuration file specific to that locale. If the definitions and attributes are not found in the localespecific configuration file, Tiles will attempt to load them from the default configuration file whose filename does not contain any locale information (e.g., tilesdefs.xml). This is especially helpful when you want to tailor only a few definitions to a specific locale. To accomplish this, create a default configuration file (tilesdefs.xml) that contains all the master Tiles definitions and attributes. Then, create locale-specific configuration files (e.g., tiles-defs_fr.xml or tiles-defs_es.xml) that override definitions in the default configuration file. You can choose to override as few or as many definitions as you like. At run time, if Tiles cannot find a localespecific version of a definition, it will automatically use the definition from the default configuration file.
To illustrate how Tiles internationalization support works, lets step through an example scenario. Assume that you have a Web site that is available in English (default), French, and Spanish versions. This requires three Tiles configuration files. The first file, tiles-defs.xml, which is shown next, houses the master Tiles definitions for the Web site. Because the default language for the site is English, the English definitions will reside in this file and serve as the default definitions for the other languages when the other languages are not overriding the definitions in this file.
<?xml version="1.0"?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"> <tiles-definitions> <!-- Search Page --> <definition name="search.page" path="/mainLayout.jsp"> <put name="header" value="/header.jsp"/> <put name="body" value="/search.jsp"/> <put name="footer" value="/footer.jsp" /> </definition> <!-- View Employee Page --> <definition name="viewEmployee.page" path="/mainLayout.jsp""> <put name="header" value="/header.jsp"/> <put name="body" value="/viewEmployee.jsp"/> <put name="footer" value="/footer.jsp" /> </definition> </tiles-definitions>
This configuration file contains two basic page definitions that utilize the same page layout and simply provide different values for the body attribute. The second file, tiles-defs_fr.xml, provides French-specific definitions and attributes:
<?xml version="1.0"?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"> <tiles-definitions> <!-- Search Page --> <definition name="search.page" path="/mainLayout_fr.jsp"> <put name="header" value="/header_fr.jsp"/> <put name="body" value="/search_fr.jsp"/> <put name="footer" value="/footer_fr.jsp" /> </definition> <!-- View Employee Page --> <definition name="viewEmployee.page" path="/mainLayout_fr.jsp""> <put name="header" value="/header_fr.jsp"/> <put name="body" value="/viewEmployee_fr.jsp"/> <put name="footer" value="/footer_fr.jsp" />
</definition> </tiles-definitions>
The definitions in this file mirror those of the default configuration file, with the exception that the values for the definitions and attributes are different. The definition and attribute names are the same so that they match up with those in the default file. Notice that the values for the definition paths are set to a JSP named mainLayout _fr.jsp. That is a French-specific version of mainLayout.jsp. If the layout JSP itself did not need to change to accommodate the French version, you would leave the path set to /mainLayout.jsp and specify only French-specific versions of the attributes inside the definitions. Its up to you to determine how much customization you need or want for each locale. The third file, tiles-defs_es.xml, includes the Spanish-specific definitions and attributes. This file, shown next, includes only a Spanish-specific version of the search.page definition.
<?xml version="1.0"?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"> <tiles-definitions> <!-- Search Page --> <definition name="search.page" path="/es/mainLayout.jsp"> <put name="header" value="/es/header.jsp"/> <put name="body" value="/es/search.jsp"/> <put name="footer" value="/es/footer.jsp" /> </definition> </tiles-definitions>
Because this file contains only a Spanish version of the search.page definition, if a request is made for the Spanish version of the viewEmployee.page definition, Tiles will (after not finding it in tiles-defs_es.xml) look for it in the default configuration file, tiles-defs.xml. Notice that the attributes in this file point to JSPs underneath an es directory. There are many ways you can organize your applications content to support internationalization. This example places the locale-specific files in a directory dedicated to that locale. The French configuration file points to files whose locale is part of the filename. Which approach you take to organizing content is up to you. Note Detailed information on internationalizing a Struts application is found in Chapter 10.
To use the Tiles Tag Library in a Struts application, you need to include the following snippet in your application's Web Archive (.war) deployment descriptor, web.xml: <taglib> <taglib-uri>/WEB-INF/tlds/struts-tiles.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-tiles.tld</taglib-location> </taglib> Recall from the overview of the web.xml file in Chapter 2 that the <taglib-uri> tag is used to declare the URI (or alias) that will be referenced in each of your JSPs with a taglib directive. The <taglib-location> tag is used to declare the actual location of the Tag Library Descriptor (.tld) file in your Web Archive. The following snippet illustrates how your JSPs will declare their use of the Tiles Tag Library with a JSP taglib directive: <%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %> Notice that the uri attribute specified here is the same as that declared with the <taglib-uri> tag in the web.xml file. Also, notice that the prefix attribute is set to tiles. This attribute can be set to whatever you want; however, tiles is the accepted default for the Tiles Tag Library. The prefix attribute declares the prefix that each tag must have when it is used in the JSP, as shown here: <tiles:insert attribute="header"/> Because tiles was defined as the prefix, the insert tag was used as shown. However, if you were to choose to use a prefix of strutstiles, the tag would be used as follows:
<strutstiles:insert attribute="header"/>
Table 7-1: The Tiles Tag Library Tags Tag put putList useAttribute Description Defines an attribute (or parameter) for a definition. Defines a list attribute (of java.util.List type) containing an ordered collection of individual attributes. Stores the value of a Tiles attribute in a JSP scripting variable.
The remainder of this section discusses each tag in detail, including a complete description of the tag, a table listing each of the tags attributes, and a usage example for the tag. In the tables that describe each tags attributes, pay special attention to the Required and Accepts Scriptlet columns. The Required column simply denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the tag will throw a javax.servlet.jsp.JspException at run time. Note that you can declare an error page in your JSP with a page directive to capture any JspExceptions that might be thrown, as shown here: <%@ page errorPage="error.jsp" %> If an exception occurs, the page specified by the errorPage attribute will be internally redirected to display an error page. The Accepts Scriptlet column denotes whether or not the given attributes value can be specified with a JSP scriptlet. If a JSP scriptlet is used to specify an attribute value, the scriptlet must comprise the complete value, quote () to quote (), as shown here. Correct: <tiles:put name="<%=title%>"> Incorrect: <tiles:put name="<%=title%>-title"> Notice in the incorrect example that -title is used as part of the value for the name attribute following the scriptlet. This is invalid because there are extra characters between the end of the scriptlet and the ending quote. A corrected version of the incorrect example follows: <tiles:put name="<%=title + "-title"%>"/> The concatenation of -title is now part of the scriptlet and the scriptlet comprises the complete value for the attribute.
Attributes
Attribute
Description
Required
beanName
Specifies the name of an object whose value will be used for this entry. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the object whose value will be used for this entry. Specifies the field of the object specified by the name attribute whose value will be used for this entry. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the value attribute instead. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the type attribute set to string instead. Specifies the role the currently authenticated user must have for this entry to be added to the enclosing list. If the user is not in the specified role, the entry will not be added to the list. Specifies the type (string, page, or definition) of the value. If present, it indicates how the value specified with the value attribute is treated. Specifies the value for this entry.
No
beanProperty
Yes
No
beanScope
No
No
content
Yes
No
direct
No
No
role
Yes
No
type
No
No
value
No
No
Example Usage
The following example illustrates the basic usage of the add tag: <tiles:insert page="/mainLayout.jsp"> <tiles:putList name="menu"> <tiles:add value="Home"/> <tiles:add value="Products"/> <tiles:add value="Search"/> </tiles:putList> </tiles:insert> Each add definition is added to the enclosing list in the order specified.
The definition tag is used to define a tile (which is a region within a page) and store it in a JSP scripting variable. Additionally, this tag allows you to specify the scope in which the JSP scripting variable should be placed. Once this definition is defined it can be inserted into JSPs by using the insert tag.
Attributes
Attribute extends id page role Description Specifies the name of another definition that this definition extends. Specifies the name of the JSP variable that will hold this definition. Specifies the URL for the tile. Specifies the role the currently authenticated user must have for this definition to be created. If the user is not in the specified role, the definition will not be created. Specifies the scope (application, page, request, or session) in which the JSP scripting variable defined by the id attribute will be stored. If not specified, the JSP scripting variable will be stored in page scope. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the page attribute instead. Accepts Scriptlet Yes No Yes Yes Required No Yes No No
scope
No
No
template
Yes
No
Example Usage
The following example illustrates the basic usage of the definition tag: <tiles:definition name="mainLayout" page="/layouts/main.jsp"> <tiles:put name="header" value="/layouts/header.jsp"/> <tiles:put name="footer" value="/layouts/footer.jsp"/> </tilesdefinition> Each of the attributes nested underneath the definition tag can be accessed by the JSP specified with the page attribute.
Attributes
Attribute Description Accepts Scriptle t Yes Required
ignore
Accepts true or false to denote whether or not this tag should return without error if the
No
Attribute
Description
Accepts Scriptle t
Required
specified Tiles attribute does not exist. Defaults to false, which will cause a run-time exception to be thrown if the attribute does not exist. name role Specifies the name of a Tiles attribute whose value should be rendered. Specifies the role the currently authenticated user must have for the specified Tiles attribute to be rendered. Yes Yes Yes No
Example Usage
The following example illustrates how to use the getAsString tag: <tiles:getAsString name="title"/> This example renders the value of the title attribute to the JSPs output stream.
Attributes
Attribute Description Accepts Scriptle t Yes Required
ignore
Accepts true or false to denote whether or not this tag should return without error if the specified Tiles attribute does not exist. Defaults to false, which will cause a run-time exception to be thrown if the attribute does not exist. Specifies the name of a Tiles attribute that should be imported. Specifies the scope (application, page, request, or session) that attribute values should be stored in. If not specified, JSP scripting variables will be stored in page scope.
No
name scope
Yes No
No No
Example Usage
The following example illustrates how to use the importAttribute tag:
<tiles:importAttribute/> This example stores the values of all defined Tiles attributes in JSP scripting variables. Alternatively, you could import just one attribute by specifying its name with the name attribute, as shown here: <tiles:importAttribute name="title"/>
Attributes
Attribute Description Accepts Scriptle t No No Required
classname file
Specifies the fully qualified class name of the Tiles definition factory to use. Specifies the Tiles configuration file containing the definitions for the factory.
No Yes
Example Usage
The following example illustrates how to use the initComponentDefinitions tag: <tiles:initComponentDefinitions file="tiles-defs.xml"/> As you can see, using this tag is straightforward.
Attributes
Attribute Description Accepts Scriptle t No Yes Required
attribute beanName
Specifies the name of an attribute to insert. Specifies the name of an object whose value will be inserted into the JSP. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the object whose value will be inserted into the JSP.
No No
Attribute
Description
Required
beanProperty
Specifies the field of the object specified by the name attribute whose value will be inserted into the JSP. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. DeprecatedUse instead. the page attribute
No
beanScope
No
No
component controllerClass
Yes Yes
No No
Specifies the fully qualified class name of a controller object that is executed before this definition is inserted. Specifies the URL for a controller that is executed before this definition is inserted. Specifies the name of a definition to insert. Accepts true to denote that the JSPs output stream should be flushed before this definition is inserted. Accepts true or false to denote whether or not this tag should return without error if the specified Tiles attribute does not exist. Defaults to false, which will cause a run-time exception to be thrown if the attribute does not exist. Specifies the name of an entity (definition or attribute) to insert. Specifies the URL to a page to insert into the JSP. Specifies the role the currently authenticated user must have for the specified attribute to be inserted. If the user is not in the specified role, the attribute will not be inserted. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the page attribute instead.
controllerUrl
Yes
No
definition flush
Yes No
No No
ignore
Yes
No
No No No
template
Yes
No
Example Usage
The following example illustrates the basic usage of the insert tag: <tiles:insert attribute="header"/> This example inserts an attribute named header into a JSP.
To insert a definition, you use the definition attribute, as shown next: <tiles:insert definition="tabLayout"/> This example inserts an entire definition into a JSP. As mentioned, you can also use the insert tag to create and insert a definition: <tiles:insert page="/layouts/tabLayout.jsp"> <tiles:put name="header" value="/tabHeader.jsp"/> <tiles:put name="body" value="/search.jsp"/> <tiles:put name="footer" value="/tabFooter.jsp"/> </tiles:insert> This type of insertion is useful for declaring the attributes that get passed to a layout JSP from inside the JSP instead of in a Tiles configuration file.
Attributes
Attribute beanName Description Specifies the name of an object whose value will be used for this attribute. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the object whose value will be used for this attribute. Specifies the field of the object specified by the name attribute whose value will be used for this attribute. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the value attribute instead. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the value attribute instead. Accepts Scriptlet Yes Required No
beanProperty
Yes
No
beanScope
No
No
content
Yes
No
direct
No
No
Description Specifies the name for the attribute. Specifies the role the currently authenticated user must have for this attribute to be created. If the user is not in the role, this attribute will not be created. Specifies the type (string, page, or definition) of the value. If present, it indicates how the value specified with the value attribute is treated. Specifies the value for the attribute.
Required No No
type
No
No
value
Yes
No
Example Usage
The following example illustrates the basic usage of the put tag: <tiles:definition name="mainLayout" page="/layouts/main.jsp"> <tiles:put name="header" value="/layouts/header.jsp"/> <tiles:put name="footer" value="/layouts/footer.jsp"/> </tiles:definition> Defining attributes with the put tag is as simple as specifying their names and values.
Attribute
Attribute name Description Specifies the name of the attribute. Accepts Scriptlet No Required Yes
Example Usage
The following example illustrates how to use the putList tag: <tiles:insert page="/mainLayout.jsp"> <tiles:putList name="menu"> <tiles:add value="Home"/> <tiles:add value="Products"/> <tiles:add value="Search"/> </tiles:putList>
</tiles:insert> This example creates a java.util.List-based Tiles attribute named menu containing the values specified with the nested add tags. You can also create hierarchical structures with the putList tag by nesting instances of the putList tag inside itself, as shown next: <tiles:putList name="menu"> <tiles:add value="Home"/> <tiles:putList name="Products"> <tiles:add value="Products Menu Item 1"/> <tiles:add value="Products Menu Item 2"/> </tiles:putList> <tiles:add value="Search"/> </tiles:putList> Tiles allows you to nest the putList tags to any level for whatever hierarchal structure you need.
Attributes
Attribute classname id Description DeprecatedThis attribute is no longer used. Specifies the name of the JSP variable that will hold the specified attributes value. Accepts true or false to denote whether or not this tag should return without error if the specified Tiles attribute does not exist. Defaults to false, which will cause a run-time exception to be thrown if the attribute does not exist. Specifies the name of a Tiles attribute whose value should be stored. Specifies the scope (application, page, request, or session) in which the JSP scripting variable defined by the id attribute will be stored. If not specified, the JSP scripting variable will be stored in page scope. Accepts Scriptlet No No Required No No
ignore
Yes
No
name scope
Yes No
Yes No
Example Usage
The following example illustrates how to use the useAttribute tag: <tiles:useAttribute name="title"/> This example stores the value of the Tiles attribute named title in a JSP scripting variable with the same name. If you want to store the value in a JSP scripting variable with a different name, you must use the id attribute, as shown next: <tiles:useAttribute id="pageTitle" name="title"/> Additionally, you can specify the scope of the JSP scripting variable that the attribute value will be stored in with the scope attribute: <tiles:useAttribute id="pageTitle" name="title" scope="session"/>
The following sections walk through each step of the process in detail.
After adding this entry, the TLD can be referenced from JSPs using the following snippet:
<%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %>
<tr> <th>Name</th> <th>Social Security Number</th> </tr> <logic:iterate id="result" name="searchForm" property="results"> <tr> <td><bean:write name="result" property="name"/></td> <td><bean:write name="result" property="ssNum"/></td> </tr> </logic:iterate> </table> </logic:greaterThan> </logic:present> </body> <html>
The body content of the page is shown in bold. This section will be different for each distinct page. The rest of the page, however, will be consistent across several pages, thus allowing for it to be abstracted into a general-purpose layout. There are three JSP files that make up the layout: mainLayout.jsp, header.jsp, and footer.jsp. The mainLayout.jsp file is shown here:
<%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %> <html> <head> <title><tiles:getAsString name="title"/></title> </head> <body> <tiles:insert attribute="header"/> <tiles:insert attribute="body"/> <tiles:insert attribute="footer"/> </body> </html>
This JSP defines the layout's template and is used to source in the other layout JSPs as well as the body content that will be defined by pages utilizing the layout. The body content and other layout JSPs are sourced in with <tiles:insert> tags. These tags specify the names of attributes defined in the Tiles configuration file whose values are the names of the JSPs that should be inserted into the JSP at run time. Notice the use of the <tiles:getAsString> tag. This tag works similarly to the <tiles:insert> tag, but instead of using the specified attribute's value as the name of a page to include, it is used as a literal string. This is useful for defining variables that can be customized by page definitions that extend layout definitions in the Tiles configuration file. Following are the header and footer layout JSPs. header.jsp:
footer.jsp:
<hr width="100%" noshade="true"> Copyright © ABC, Inc.
As you can see, the header and footer JSPs are quite simple and do not contain much HTML. The content of these JSPs could have been placed directly in the mainLayout.jsp file instead of here and content pages would still only have to contain the body content of the page. However, breaking the pages up into smaller chunks allows for more flexibility in how layouts are used. For example, if you wanted all pages to have the standard header and footer, you wouldn't have to worry about changing anything. On the other hand, if you needed some pages to have a custom header and footer and others to use the standard ones, separating the header and footer into discreet chunks would allow you to do that. You would simply define values for the header and footer attributes at the layout level, and each page that wanted a custom header, footer, or both would override the necessary attributes with new values at the page level. This will make more sense after you see the Tiles configuration file, which is discussed shortly.
</html:form> <logic:present name="searchForm" property="results"> <hr width="100%" size="1" noshade="true"> <bean:size id="size" name="searchForm" property="results"/> <logic:equal name="size" value="0"> <center><font color="red"><b>No Employees Found</b></font></center> </logic:equal> <logic:greaterThan name="size" value="0"> <table border="1"> <tr> <th>Name</th> <th>Social Security Number</th> </tr> <logic:iterate id="result" name="searchForm" property="results"> <tr> <td><bean:write name="result" property="name"/></td> <td><bean:write name="result" property="ssNum"/></td> </tr> </logic:iterate> </table> </logic:greaterThan> </logic:present>
As you can see, the updated page no longer contains the header or footer portions of the content. At run time, the layout JSPs will surround the contents of this updated page with the common layout content to create the complete page.
<definition name="search.page" extends="main.layout"> <put name="title" value="Search Page"/> <put name="body" value="/search.jsp"/> </definition> </tiles-definitions>
There are two Tiles definitions in this file. The first definition in the file declares a layout named main.layout. The .layout extension given to the definition's name is used to denote that it is a layout definition. This is not a formal naming scheme; however, it is a simple way to distinguish the types of definitions. Generally speaking, layout definitions specify the template for a page and the list of attributes whose values will be used to fill in the template. Page definitions extend layout definitions and provide values for the attributes defined in the extended layout. So, essentially, page definitions are instances of a layout with attributes set to the content for a specific page. Notice that the first definition defines four attributes with put tags. These attributes will be available for use by the layout JSP specified by the path attribute. The layout JSP uses these attributes to supply it with the locations of its content. Additionally, attributes can be used to supply literal strings, as is the case with the title attribute. This attribute will be used by mainLayout.jsp to enable a dynamic title based on the value set by page definitions that extend the layout definition. The second definition in the file declares a page definition named search.page. This definition extends the main.layout layout definition and supplies values for the attributes that don't have values in the layout definition. This definition can override any of the attributes in the layout definition if so desired; however, only the title and body attributes are overridden in this case.
Update Forward Definitions in, and Add the Tiles Plugin to, the strutsconfig.xml File
After you have created the Tiles configuration file, you must update Mini HR's strutsconfig.xml file to point to Tiles definitions instead of pointing directly to JSPs for each page that has been converted to use Tiles. Additionally, the Tiles plugin must be added to the file. Without Tiles, forward and action definitions point directly to JSPs. With Tiles, they point to the page's definition in the Tiles configuration file. For example, before, the search action pointed directly to search.jsp, as shown here:
<action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"/>
However, now the action will point to the search page's Tiles definition, as shown here:
<action path="/search" type="com.jamesholmes.minihr.SearchAction"
At run time, the Tiles plugin will determine if a specified page is the name of a Tiles definition or an actual path to a page. If a Tiles definition is specified, then Tiles will process the page accordingly; otherwise, normal Struts processing will take place. To add the Tiles plugin to the application, you must add the following snippet to struts-config.xml:
<!-- Tiles Configuration --> <plug-in className="org.apache.struts.tiles.TilesPlugin"> <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/> </plug-in>
This causes Struts to load the Tiles plugin at application startup. Notice that the Tiles configuration file is specified with the set-property tag. You can specify multiple configuration files by providing a comma-delimited list of files. The following code shows the updated Struts configuration file for Mini HR in its entirety. The sections that have changed or that have been added are shown in bold.
<?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- Form Beans Configuration --> <form-beans> <form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/> </form-beans> <!-- Action Mappings Configuration --> <action-mappings> <action path="/viewSearch" type="org.apache.struts.actions.ForwardAction" parameter="search.page"/> <action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="search.page"/> </action-mappings> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> <!-- Tiles Configuration -->
Similar to the way you ran Mini HR the first time, you now need to place the new MiniHR.war file that you just created into Tomcat's webapps directory and start Tomcat. As before, to access the Mini HR application, point your browser to http://localhost:8080/MiniHR/. Once you have the updated Mini HR running, everything should work as it did before. However, now you can add new pages and make global changes to the application with minimal effort.
centralizes all of the exception-handling logic in one place and reduces the duplication of code.
This chapter illustrates how to configure and use Struts' built-in exception handler and provides instructions for creating your own custom exception handler. Finally, this chapter steps through the process of updating the Mini HR application to take advantage of declarative exception handling.
Action-nested exception handlers handle the specified exception only if it is thrown by the enclosing action. If the exception is thrown by another action, it will not be handled.
You don't have to modify your Action classes in any way to take advantage of declarative exception handling. However, if you already have try-catch blocks for exceptions that you want handled by the exception handler, you need to remove them so that the exception gets propagated up the chain for the exception handler to process.
org.apache.struts.config.ExceptionConfig The ExceptionConfig object encapsulates the <exception> definition from the struts-config.xml file for this exception handler. org.apache.struts.action.ActionMapping The ActionMapping object encapsulates the <action> definition from the strutsconfig.xml file for the action that threw the exception that this exception handler is
Table 8-1: The execute( ) Method Arguments Argument org.apache.struts.action.ActionForm Description configured to handle. The ActionForm object encapsulates the Form Bean associated with the action that threw the exception that this exception handler is configured to handle. The HttpServletRequest object encapsulates the current HTTP request. The HttpServletResponse object encapsulates the current HTTP response.
javax.servlet.http.HttpServletRequest javax.servlet.http.HttpServletResponse
To illustrate how to create a custom exception handler, we'll examine a sample exception handler that can be used to send an e-mail with a stack trace for the exception when the handler is triggered. Following is the example EmailExceptionHandler class:
package com.jamesholmes.minihr.EmailExceptionHandler; import import import import import import import import java.io.*; java.util.*; javax.mail.*; javax.mail.internet.*; javax.servlet.*; javax.servlet.http.*; org.apache.struts.action.*; org.apache.struts.config.*;
public class EmailExceptionHandler extends ExceptionHandler { private static final String EMAIL_SERVER = "smtp.company.com"; private static final String EMAIL_SENDER = "[email protected]"; private static final String EMAIL_RECIPIENT "[email protected]"; private static final String EMAIL_SUBJECT = "Exception Thrown"; public ActionForward execute(Exception ex, ExceptionConfig config, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException { // Send email with exception. try { sendEmail(ex); } catch (Exception e) { throw new ServletException(e); } // Forward processing to the default exception handler. return super.execute(ex, config, mapping, form, request, response); } private void sendEmail(Exception ex) throws Exception
{ // Set the host SMTP address. Properties props = new Properties(); props.put("mail.smtp.host", EMAIL_SERVER); // Get the default mail session. Session session = Session.getDefaultInstance(props, null); // Store exception stack trace as message text string. ByteArrayOutputStream stream = new ByteArrayOutputStream(); ex.printStackTrace(new PrintStream(stream)); String messageText = stream.toString(); // Create a message. Message message = new MimeMessage(session); message.setFrom(new InternetAddress(EMAIL_SENDER)); message.setRecipient(Message.RecipientType.TO, new InternetAddress(EMAIL_RECIPIENT)); message.setSubject(EMAIL_SUBJECT); message.setSentDate(new Date()); message.setText(messageText); // Send the message. Transport.send(message); } }
Instead of completely overriding the functionality of the base ExceptionHandler class, the EmailExceptionHandler class augments its functionality. The EmailExceptionHandler class takes the exception passed to it and uses JavaMail to send the exception's stack trace in an e-mail. After sending the e-mail, EmailExceptionHandler calls super.execute( ), which invokes the default exception handler. This example illustrates how you can jump into the exception-handling process and add processing of your own. Alternatively, you could create an exception handler that completely overrides the behavior of the base handler.
Because the EmailExceptionHandler handler simply extends the base handler and does not completely override it, you must specify a path, by using the path attribute, for the handler to forward to. However, if you were to create a custom handler that completely overrides the base handler, you would not have to specify a value for the path attribute unless your handler wanted to use it. The type and key attributes are required regardless of what handler you use.
Exception
Handling
to
the
Mini
HR
Now that you've seen the benefits of using declarative exception handling and how it works, you are ready to revisit the Mini HR application and add declarative exception handling to it. Following is the list of steps involved in adding declarative exception handling to the Mini HR application: 1. Create an application exception class. 2. Update SearchAction to throw an application exception. 3. Set up an exception handler in the struts-config.xml file. 4. Create an exception-handler JSP. 5. Add an exception error message to the ApplicationResources.properties file. 6. Recompile, repackage, and run the updated application. The following sections walk through each step of the process in detail.
package com.jamesholmes.minihr; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public final class SearchAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { EmployeeSearchService service = new EmployeeSearchService(); ArrayList results; SearchForm searchForm = (SearchForm) form; // Perform employee search based on what criteria was entered. String name = searchForm.getName(); if (name != null && name.trim().length() > 0) { results = service.searchByName(name); } else { results = service.searchBySsNum(searchForm.getSsNum().trim()); } // Throw an application exception if results were not found. if (results.size() < 1) { throw new NoResultsFoundException(); } // Place search results in SearchForm for access by JSP. searchForm.setResults(results); // Forward control to this Action's input page. return mapping.getInputForward();
} }
<form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/> </form-beans> <!-- Global Forwards Configuration --> <global-forwards> <forward name="search" path="/search.jsp"/> </global-forwards> <!-- Action Mappings Configuration --> <action-mappings> <action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> <exception type="com.jamesholmes.minihr.NoResultsFoundException" key="error.NoResultsFoundException" path="/exception.jsp"/> </action> </action-mappings> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> </struts-config>
<hr width="100%" noshade="true"> <html:errors/> </body> </html> As stated earlier in this chapter, when an exception is processed by Struts' built-in exception handler, it creates an ActionError object and stores it in request or session scope so that it can be accessed by the page being forwarded to. This page uses the HTML Tag Library's errors tag to output the error message. One other point: Although this JSP is intended to be used by the exception handler for NoResultsFoundException, it is generic enough that you could use it as a general-purpose exception-handler page.
Message
to
the
Recall from earlier in this chapter that each exception handler defined in the strutsconfig.xml file declares a key for an error message in Struts' resource bundle file: ApplicationResources.properties. At run time, the key is used to look up an error message to pass to the exception-handler page when the exception handler is triggered. Following is the error message that you must add to the ApplicationResources .properties file for the exception handler that was defined in the struts-config.xml file: # Exception Error Resources error.NoResultsFoundException=<li>No Search Results Found</li> The following code shows the updated ApplicationResources.properties file in its entirety: # Label Resources label.search.name=Name label.search.ssNum=Social Security Number # Error Resources error.search.criteria.missing=<li>Search Criteria Missing</li> error.search.ssNum.invalid=<li>Invalid Social Security Number</li> errors.header=<font color="red"><b>Error(s)</b></font><ul> errors.footer=</ul><hr width="100%" size="1" noshade="true"> # Exception Error Resources error.NoResultsFoundException=<li>No Search Results Found</li>
WEB-INF\lib\commons-collections.jar; WEB-INF\lib\commons-lang.jar; WEB-INF\lib\commons-logging.jar; WEB-INF\lib\commons-validator.jar; WEB-INF\lib\digester.jar; WEB-INF\lib\fileupload.jar; WEB-INF\lib\jakarta-oro.jar; WEB-INF\lib\struts.jar; C:\java\jakarta-tomcat-4.1.27\common\lib\servlet.jar WEB-INF\src\com\jamesholmes\minihr\*.java -d WEB-INF\classes After recompiling Mini HR, you need to repackage it using the following command line: jar cf MiniHR.war * This command must also be run from the directory where you have set up the Mini HR application (e.g., c:\java\MiniHR). Similar to the way you ran Mini HR the first time, you now need to place the new MiniHR.war file that you just created into Tomcat's webapps directory and start Tomcat. As before, access the Mini HR application at http://localhost:8080/MiniHR/. Once you have the updated Mini HR application running, try entering a name or social security number that will not be found when searched for. When this happens, the SearchAction class will throw the NoResultsFoundException exception and then the exception.jsp page will be displayed, as shown in Figure 8-1.
During the 1.1 release cycle of Struts, a significant number of new features were added to the framework. Each of these features greatly improved the framework, and each is important in its own right. However, one feature stands out because of its profound impact on the framework: support for modules. Support for modules solves a problem commonly encountered when using earlier versions of Struts. In all versions of Struts, including version 1.1, you can run only one instance of the ActionServlet controller servlet per Web application (i.e., .war file). This is because ActionServlet stores several details of the framework's configuration in variables within the Web application scope, which is the scope shared by all parts of the application. Thus, attempting to run multiple instances of ActionServlet in the same Web application causes a problem, because the data associated with one instance overwrites the data associated with another instance. Prior to version 1.1 and its introduction of modules, this was a major limitation of Struts, because your entire application's configuration had to reside in a single Struts configuration file. For large applications, where the configuration file gets very large and several developers are involved, that proved to be a problematic point of contention. Today, you still can have only one instance of ActionServlet for the same reasons as before; however, you can simulate the functionality of having multiple instances by subdividing your application into discreet units by using modules. Each module has its own Struts configuration file, its own set of actions and forwards, a separate URL namespace, and so on. Essentially, modules are like mini-Struts applications inside a master Struts application and are analogous to the way Web applications reside in a servlet container. Each module has its own URL namespace, just as Web applications do. Modules also solve the problems of parallel development that are commonly experienced when using versions of Struts prior to 1.1. Before moving on, it is necessary to mention another feature added by Struts 1.1 that, in some cases, offers an alternative to modules: support for multiple configuration files. Although added in the same release (1.1) as modules, multiple configuration files can be used independently, without having to convert your application for use with modules. Support for multiple configuration files alleviates some of the problems associated with large, team-based development with Struts. However, multiple configuration files do not partition your Struts application the way that modules do. When using multiple configuration files, the information in the files is combined to create one large set of configuration data. This can cause a problem, however. For example, if you define an action with a certain name in one file and another configuration file defines another action with the same name, the configuration file last read will override the first file's data. Basically, last one in wins. Modules do not have this problem, because Struts stores each module's configuration data separately. Thus, modules offer the best solution when you need the functionality of multiple instances of ActionServlet. This chapter describes how to set up and use modules in your application, including details on how to use Tiles and Validator with modules. It also shows how to apply modules to the Mini HR application.
Using Modules
As stated, using modules allows you to partition your Struts application into discreet functional areas, almost like having wholly separate applications. Each module has its own Struts configuration file, actions, forwards, and JSPs, which allows it to function independently of other modules. Using modules in a Struts application involves these three steps: 1. Create a Struts configuration file for each module. 2. Configure the web.xml deployment descriptor for modules. 3. Configure links to access module-specific JSPs.
The following sections explain how to configure and use modules in detail.
With this arrangement you wouldn't have to use the module name in the names of the configuration files, because they are under a module-specific directory. Note Detailed information on the Struts configuration file is found in Chapter 16.
The config parameter is set to the location of your application's Struts configuration file. To specify module configuration files, you need an initialization parameter for each module and it must be named config/moduleName, where moduleName is the name you want to assign to the module. For example, to specify two modules named moduleA and moduleB, your ActionServlet definition must look like this:
<!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servletclass> <init-param> <param-name>config/moduleA</param-name> <param-value>/WEB-INF/struts-config-moduleA.xml</param-value> </init-param> <init-param> <param-name>config/moduleB</param-name> <param-value>/WEB-INF/struts-config-moduleB.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
In this file, two configuration files are specified: struts-config-moduleA.xml and struts-config-moduleB.xml. The name given to the module through the param-name tag is known as the module prefix and is very important. Struts uses the module prefix to distinguish and route requests to the module. The module name becomes part of the URL that must be used to access the module's actions. It is important to understand that all of a module's action paths are relative to the module. For example, the modularized version of the Mini HR application developed later in this chapter is deployed at /MiniHR/ on the server (i.e., http://localhost:8080/ MiniHR/). It has a module named employee, which has an action mapped to /search. Therefore, the URL to access the action is /MiniHR/employee/search.do. Figure 9-1 shows a breakdown of what each piece of the URL is.
Figure 9-1: The module URL breakdown When using modules, the standard config parameter (that is, the one without any module name appended to it) is used to specify a configuration file for a default module. The default module has no module prefix, thus any URL whose path does not include a module name will be routed to the default module. Thus, using the Mini HR example developed later in this chapter, if a request were made to /MiniHR/admin/
viewAppUsers.do, Struts would execute the action mapped to /admin/viewAppUsers in the default module's configuration file, because the URL does not contain a module prefix in it. Note A side effect of using modules is that you cannot use path mapping (e.g., /do/*) for the ActionServlet controller servlet. Instead, you must use extension mapping (e.g., *.do) because Struts uses the URL path to determine which module a request is being made for; using path mapping would interfere with that mechanism. For more information on setting up ActionServlet's mapping, refer to Chapter 5.
You can put this definition to use with the following example link:
<html:link action="/switchMod?prefix=/moduleA&page=/moduleA/main.jsp"> ModuleA main JSP </html:link>
This example usage creates a link that will route the Struts request processor to the JSP at /moduleA/main.jsp and select the configuration data for moduleA.
If you want each of your modules to have its own Tiles configuration file and don't want each module's Tiles definitions to be shared across modules, you can indicate this intent with the Tiles plugin moduleAware parameter. Following is a sample Tiles plugin definition for the Struts configuration file that illustrates this: <!-- Tiles Configuration --> <plug-in className="org.apache.struts.tiles.TilesPlugin"> <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/> <set-property property="moduleAware" value="true"/> </plug-in> By default, Tiles stores each module's configuration file settings in the same internal configuration structure. Thus, moduleA can see all of the Tiles definitions from moduleB's Tiles configuration file and vice versa. Tiles does this by default to save memory in case each module uses the same configuration file. However, in most cases each module has its own module-specific definitions that other modules shouldn't be able to access. For this scenario, you set the moduleAware property to true. This informs Tiles to create a separate configuration structure for each module's Tiles definitions.
As explained earlier, module prefixes apply only to actions. Thus, any URLs to JSPs specified in your Tiles configuration files are not relative to a module prefix, but rather are relative to the root of the application, just as they are in nonmodularized Struts applications.
The following sections walk you through each step of the process in detail.
After you have created the directories, you need to put the corresponding JSPs into them. Because the original Mini HR application already has employee functionality, you can simply move the employee search JSP (search.jsp) from the root application directory (e.g., c:\java\MiniHR) to the new employee module directory you just created. The reports module contains new functionality, which is provided by the menu.jsp file, shown here. Store this file in the reports module directory.
<html> <head> <title>ABC, Inc. Human Resources Portal - Reports Menu</title> </head> <body> <font size="+1"> ABC, Inc. Human Resources Portal - Reports Menu </font><br> <hr width="100%" noshade="true"> • • • • New Employee Report<br> 5-Year Employee Report<br> 10-Year Employee Report<br> 20-Year Employee Report<br>
</body> <html>
After you move the search.jsp file and create the menu.jsp file, your main application directory should look like this:
C:\java\MiniHR\employee\search.jsp c:\java\MiniHR\reports\menu.jsp c:\java\MiniHR\index.jsp
Before moving on, a short, but important, digression is in order. The general idea behind the directory structure just described can be extended to configuration files, if you like. For example, you could create a directory for each module beneath /WEBINF/ that would hold the module's Struts, Tiles, and Validator configuration files. Following is an example directory layout illustrating this:
MiniHR/WEB-INF/moduleA/struts-config.xml MiniHR/WEB-INF/moduleA/tiles-defs.xml MiniHR/WEB-INF/moduleA/validation.xml MiniHR/WEB-INF/moduleB/struts-config.xml MiniHR/WEB-INF/moduleB/tiles-defs.xml MiniHR/WEB-INF/moduleB/validation.xml
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- Form Beans Configuration --> <form-beans> <form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/> </form-beans> <!-- Action Mappings Configuration --> <action-mappings> <action path="/viewSearch" forward="/search.jsp"/> <action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> </action> </action-mappings> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> </struts-config>
In addition to being renamed, this file has also been updated. Notice that the Global Forwards Configuration section has been removed and the /viewSearch action has been added. As mentioned earlier in this chapter, Struts incorporates the concept of a default module. The default module's configuration file is named struts-config.xml and is shown next:
<?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- Global Forwards Configuration --> <global-forwards> <forward name="viewSearch" path="/employee/viewSearch.do"/> </global-forwards> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> </struts-config>
You will need to create this new file and place it under the /WEB-INF/ directory (e.g., c:\java\MiniHR\WEB-INF). This configuration file simply has a forward pointing to the Search page that is used by index.jsp for its Search for Employees link. The reports module's configuration file, struts-config-reports.xml, is shown next:
<?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- Action Mappings Configuration --> <action-mappings> <action path="/viewMenu" forward="/menu.jsp"/> </action-mappings> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> </struts-config>
You will also need to create this new file and place it under the /WEB-INF/ directory (e.g., c:\java\MiniHR\WEB-INF). This configuration file simply has a forward action pointing to the Reports Menu page that is used by index.jsp for its Reports Menu link.
</servlet>
Each initialization parameter specifies a name and a value for the parameter with the param-name and param-value tags, respectively. The default module's configuration file is specified with a parameter named config. Each of the module-specific configuration files is specified with a parameter named config/moduleName, where moduleName is the name of the module the configuration file is for. Following is the updated web.xml file in its entirety:
<?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servletclass> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>config/employee</param-name> <param-value>/WEB-INF/struts-config-employee.xml</param-value> </init-param> <init-param> <param-name>config/reports</param-name> <param-value>/WEB-INF/struts-config-reports.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- The Welcome File List --> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>/WEB-INF/tlds/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/tlds/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/struts-html.tld</taglib-location> </taglib> <taglib>
This command should be run from the directory where you have set up the Mini HR application (e.g., c:\java\MiniHR). Similar to the way you ran Mini HR the first time, you now need to place the new MiniHR.war file that you just created into Tomcat's webapps directory and start Tomcat. As before, to access the Mini HR application, point your browser to http:// localhost:8080/MiniHR/. Once you have the updated Mini HR application running, you will see the screen shown in Figure 9-2.
Figure 9-2: The opening Mini HR application screen Clicking the Search for Employees link takes you to the same employee search page that you were taken to by clicking the same link in the original application. The Reports Menu link takes you to the new menu.jsp page created for the reports module.
Internationalizing
Struts
The Internet has created a global, electronic shopping mall. Whereas businesses once catered only to customers in their same geographic region, today business is transacted with customers worldwide. A product of this globalization is the need for software that can adapt to the conventions and languages used by customers in different countries. To achieve this, applications must provide content in multiple languages. This is called internationalization. From the start, Java was designed with internationalization in mind. For example, it offers support for Unicode character sets and provides built-in classes that manage locale-specific content. Struts builds upon Java's support, making development of internationalized Struts applications straightforward. This chapter presents an overview of Java's built-in internationalization support and then explains how Struts' internationalization builds upon it.
class is used to uniquely represent a specific locale. All other locale-oriented classes use instances of this class to tailor their behavior and output to a specific locale. When you create a Locale object you specify the locale's language; language and country; or language, country, and variant. Thus, Locale lets you represent as specific a locale as needed. The constructors for the Locale class are shown here: Locale(String language) Locale(String language, String country) Locale(String language, String country, String variant) The following snippet illustrates each of these uses in order: Locale locale1 = new Locale("en"); Locale locale2 = new Locale("en", "US"); Locale locale3 = new Locale("en", "US", "WIN"); The first example creates an English language-specific locale; the second example creates a United States English language-specific locale; and the third example creates a United States English language Windows-specific locale. The more attributes you specify, the more specific the locale is. For example, if you create a Spanish language Locale object, as shown next, it will serve all users whose language is Spanish, independent of what country they are from: Locale spanishLocale = new Locale("es"); Alternatively, to create a Spanish language locale specific to Mexico, you can use this declaration: Locale mexicoSpanishLocale = new Locale("es", "MX"); Using combinations of language, country, and variant together narrows the scope of Locale objects. The Locale object's language argument must be specified using a valid two-letter ISO-639 language code (e.g., 'en' for English or 'es' for Spanish). The country argument must be specified using a valid uppercase two-letter ISO-3166 country code (e.g., 'US' for United States or 'CA' for Canada'). The variant argument is for a vendor- or browser-specific code (e.g., 'WIN' for Windows or 'MAC' for Macintosh). For a listing of each of the ISO language and country codes, visit their respective specification Web sites listed here: http://www.unicode.org/unicode/onlinedat/languages.html http://www.unicode.org/unicode/onlinedat/countries.html Here are a couple important notes about Locale objects that you should know. First, Locale objects are immutable; thus, once you have created an instance, you cannot change any of its attributes (i.e., language, country, or variant). Because of this, if you need to change a locale for some reason, you must create a new locale instance. Second, the Locale class provides several static locale constants as a convenience. For example, Locale.US and Locale.FRANCE are country code constants and Locale.ENGLISH and Locale.FRENCH are language code constants. Following is an example use of the constants: Locale us = Locale.US; Locale french = Locale.FRENCH;
across all locales, enabling an application to know only the name of a resource and how it should be handled. For example, assume that a label is displayed beside a field on an HTML form. Using a resource bundle, an application can obtain the text for the label by requesting the label from the resource bundle by name. When requesting the label, the application passes a locale that specifies what version of the label's text to obtain, such as English, Spanish, and so on. The resource bundle returns to the application the proper version of the text, which it can then display beside the field. The application does not need to have separate logic for each translation. It just needs to know how to get the text and display it. This is the premise and power of internationalization. Resource bundles most often contain locale-specific text (e.g., error messages, field and button labels, and so on) for applications, but can also be used to manage any locale-specific resources. The ResourceBundle class provides the core interface for working with resource bundles, but is not intended to be used directly because it is an abstract class. Java provides two subclasses of ResourceBundle: java.util.ListResourceBundle and java.util.PropertyResourceBundle. The ListResourceBundle class is an abstract class that provides a mechanism for using lists to store resources; because it's abstract you must provide a concrete subclass implementation to make use of it. The PropertyResourceBundle class is a concrete subclass that provides a mechanism for using properties files to store resources. This class is the most commonly used for working with resource bundles in Java and is the default mechanism used by the ResourceBundle class's static getBundle( ) methods. Because the PropertyResourceBundle class is the default (and the most commonly used) implementation, it's important to know how it works. The PropertyResourceBundle class provides an interface to access resources stored in properties files. Internally, it uses the java.util.Properties class. The PropertyResourceBundle class requires that resource bundle properties files be named using a special scheme, which takes the following format: bundlename_language_country_variant.properties For example, if you had a bundle named ApplicationResources for the English language in the United States for the Windows platform, the properties file would be ApplicationResources_en_US_WIN.properties. Of course, not all locale components are required, so the name for a simple English file would be ApplicationResources_en.properties. Resource bundles also support the concept of a default resource bundle, which in this case is simply ApplicationResources.properties. The default resource bundle is used when there is not a bundle for a locale or if a localespecific bundle does not have an entry for a certain resource. For example, if you have a French bundle and it does not contain an entry for a requested resource, the ResourceBundle classes will attempt to find an entry for that resource in the default bundle. Typically, applications use the default bundle to store the English version of resources and then create language-specific bundles for other languages. However, if a language-specific bundle does not have an entry for a resource, the English version will be used from the default bundle.
This snippet shows two properties whose values are very similar. The main difference between them is simply the subject of what is required. Using this model, a property would have to be created for every required field, with each property being almost identical to the next. Thus, significant duplication would exist. With the MessageFormat class, you can create a dynamic message to solve the problem of duplication. Instead of creating a separate static property for each required field, you create one dynamic property that can be used to generate specific messages. Following is an example of a dynamic message: error.required={0} is required. This property specifies a placeholder with {0}. The MessageFormat class takes a dynamic message and a list of substitution data and replaces the dynamic message's placeholders with the substitution data. To illustrate how this works, consider the following example code: ResourceBundle bundle ResourceBundle.getBundle("ApplicationResources"); String requiredMessage = bundle.getString("error.required"); String[] substituteData1 = {"First Name"}; String firstNameMessage = MessageFormat.format(requiredMessage, substituteData1); String[] substituteData2 = {"Last Name"}; String lastNameMessage = MessageFormat.format(requiredMessage, substituteData2); In this example, the dynamic message is used twice, with different substitution data to create unique messages. This is a powerful and often used technique in internationalized applications. Of course, the MessageFormat class accepts a locale in its constructor so that dynamic messages can be tailored to specific locales. =
The following three sections cover the specifics of how internationalization works in Struts, starting with an overview of how Struts handles locale, followed by an overview of Struts' message resources, and concluding with a section on how Struts' tag libraries support internationalization.
Locale
Like other Java applications, Struts applications use Locale objects to store users' locale settings and to customize content based on those settings. Struts determines a user's locale from the HTTP requests made to the application. When making a request to a Web server, browsers pass along an HTTP header (Accept-Language) that indicates the user's locale. Java's javax.servlet.http.HttpServletRequest request object makes this locale setting available to servlets via a getLocale( ) method, which returns a Locale object containing the settings from the request. When a user first accesses a Struts application, Struts captures the user's locale and stores it in the user's session. Storing the locale in the session allows it to be easily accessed throughout the Struts framework from one convenient place. A side benefit of placing the Locale object in a user's session (instead of simply requesting it each time from a request) is that applications can update the locale settings for a user independent of the browser settings. For example, assume that a user accesses an application from a browser with English set as its locale. You can change the user's locale (say to French) without making the user change the browser settings by simply placing another Locale object into the user's session. Struts would then operate based on the new locale because Struts determines locale settings from the object stored in the session, not the settings passed in on each request. Conversely, sometimes it's necessary to override Struts' default behavior of storing locale settings in the session. For example, your application may want to always use the locale settings specified in the request. That way if the user changes their browser settings, Struts will pick up the change. To do this, you have to configure Struts not to store locales in the session by setting the controller element's locale attribute to false in your application's Struts configuration file, as shown here: <controller locale="false"/>
Message Resources
As you saw in the example application in Chapter 2, message resources are a fundamental building block in Struts applications whether you're intending to internationalize your application or not. Struts uses message resources as a means of dynamically inserting text into JSPs as well as for storing error messages that get returned from data validations. By using message resources in this way, it's easy to internationalize an application, whether it is an initial requirement for your application or an enhancement. You simply store all of your application's text in resource bundles. Then, when it comes time to internationalize the application, all you have to do is create locale-specific versions of the resource bundle. Struts transparently handles selecting the right resource bundle from which to obtain resources based on the users' locales. To use message resources, you must create a properties file for your resources to be stored in. The standard name that Struts applications use for this file is ApplicationResources .properties; however, you can use any filename with an extension of .properties. Like Java's built-in PropertyResourceBundle class, PropertyMessageResources requires that localespecific versions of the file specify the locale information in the filename. For example, to create a French version of the ApplicationResources.properties file, you would create a file named ApplicationResources_fr.properties. Also in parallel with Java's version, PropertyMessageResources will attempt to load resources for a locale from the corresponding locale-specific resource bundle. If the resource bundle does not exist or the resource does not exist in the bundle, Struts will attempt to load the resource from the default resource bundle.
Once you have created a properties file, it has to be placed somewhere on your application's classpath (i.e., somewhere beneath the /WEB-INF/classes/ directory or inside a .jar file underneath the /WEB-INF/lib directory). This is necessary because Struts uses Java's class loader to load the properties file. This is very important. If the file is not in your application's classpath, Struts will not be able to access the file. Next, you have to configure Struts to know where the file is located. This is done by placing a message-resources element in your application's Struts configuration file, as shown next: <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> The parameter attribute of the message-resources element informs Struts of the location of your application's resource bundle. Notice that the directory of the file is specified using Java's package notation with dots. Thus, the resource bundle in the preceding example is in a directory named com/jamesholmes/minihr that is somewhere in the classpath, and the resource bundle filename is ApplicationResources. Struts automatically appends .properties to the filename specified with the parameter attribute. If you mistakenly specify the .properties extension, Struts will not be able to locate your file. You can define multiple resource bundles in the Struts configuration files by using multiple message-resources elements. Each additional resource bundle beyond the main, default bundle has to specify a bundle name using the key attribute, as shown next: <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> <message-resources key="alternate" parameter="com.jamesholmes.minihr.AlternateResources"/> The main resource bundle does not specify a name because it is the default bundle. Additional bundles have to specify a name so that they can be explicitly used by name by the application. After you have created a resource bundle and configured Struts to use it, you can make use of it in your application a couple ways. First, you can dynamically insert text into JSPs by using the Bean Tag Library's message tag, as shown here: <head> <title><bean:message key="searchPage.title"/></title> </head> This example dynamically inserts the value of the searchPage.title resource into the JSP at run time. Second, you can also return a set of messages from an action to a JSP by using Struts' ActionMessages class. When your application first accesses a resource bundle, Struts loads the bundle into memory and caches it so that it can be quickly accessed again. Because of this, if you modify your resource bundles while your application is running, you will have to restart your application server before you can see the changes.
Several of Struts' tag library tags support internationalization by way of allowing certain attributes' values to be specified as keys to properties in a message resources bundle. At run time, Struts uses the keys to look up a corresponding value and then uses the value for the given attribute. The following table lists each of the tags and their attributes that support internationalization. Library Bean Bean HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML HTML Tag message write button button cancel cancel checkbox checkbox file file frame hidden hidden image image image image img img img img link multibox multibox option password password radio radio reset reset Attribute key formatKey altKey titleKey altKey titleKey altKey titleKey altKey titleKey titleKey altKey titleKey altKey pageKey srcKey titleKey altKey pageKey srcKey titleKey titleKey altKey titleKey key altKey titleKey altKey titleKey altKey titleKey
Library HTML HTML HTML HTML HTML HTML HTML HTML Note
textarea titleKey Several of Struts' tags have a bundle attribute that allows you to specify the name of a bundle from which values are loaded. That way you're not limited to putting all of your internationalized properties into a single resource bundle.
Text
to
the
The first step in updating Mini HR to support internationalization is to add entries for all application text to the ApplicationResources.properties file. As you'll see, having all application text in this file allows it to be easily translated into other languages. The ApplicationResources.properties file from the original Mini HR application in Chapter 2 already contains some of the application's text; however, all text needs to be placed in this file for internationalization to work. Following is the updated ApplicationResources.properties file in its entirety:
# Title Resources title.application=ABC, Inc. Human Resources Portal title.employee.search=Employee Search
# Link Resources link.employee.add=Add an Employee link.employee.search=Search for Employees # Label Resources label.search.name=Name label.search.ssNum=Social Security Number # Error Resources error.search.criteria.missing=<li>Search Criteria Missing</li> error.search.ssNum.invalid=<li>Invalid Social Security Number</li> error.search.not.found=No Employees Found errors.header=<font color="red"><b>Validation Error(s)</b></font><ul> errors.footer=</ul><hr width="100%" size="1" noshade="true">
Notice that there are several new entries. All of the text from the JSPs has been moved into this file so that the application can have both an English and a Spanish version.
Notice that this file's name is only slightly different from ApplicationResources .properties in that it has _es appended to the ApplicationResources part of the original filename. The _es denotes which locale the file is for-in this case, the Spanish language. You could, of course, include a country code along with the language code to make the file specific to a certain country/language combination. This file contains all the same entries as the ApplicationResources.properties file, but the property
values are in Spanish. The property names are universal across all locales' files. That's how applications reference the text. Note The translations in this section were performed using Google's translation service and are for demonstration purposes only.
Application
Text
from
the
After you have updated ApplicationResources.properties and created an ApplicationResources_es.properties file, you have to update Mini HR's JSPs. The original JSPs have a mix of hard-coded text and text that is dynamically inserted into the page from the ApplicationResources.properties file. In order to support internationalization, all of the hard-coded text has to be moved into the ApplicationResources.properties file so that it can be obtained dynamically, based on locale. Following are the updated JSPs, index.jsp and search.jsp, with all the hard-coded text replaced with <bean:message> tags that dynamically insert the text into the page. index.jsp:
<%@ taglib uri="/WEB-INF/tlds/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> <html> <head> <title><bean:message key="title.application"/></title> </head> <body> <font size="+1"><bean:message key="title.application"/></font><br> <hr width="100%" noshade="true"> • <bean:message key="link.employee.add"/><br> • <html:link forward="search"> <bean:message key="link.employee.search"/></html:link><br> </body> </html>
search.jsp:
<%@ taglib uri="/WEB-INF/tlds/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/tlds/struts-logic.tld" prefix="logic" %> <html> <head> <title> <bean:message key="title.application"/> <bean:message key="title.employee.search"/> </title> </head> <body>
<font size="+1"> <bean:message key="title.application"/> <bean:message key="title.employee.search"/> </font><br> <hr width="100%" noshade="true"> <html:errors/> <html:form action="/search"> <table> <tr> <td align="right"><bean:message key="label.search.name"/>:</td> <td><html:text property="name"/></td> </tr> <tr> <td></td> <td>-- or --</td> </tr> <tr> <td align="right"><bean:message key="label.search.ssNum"/>:</td> <td><html:text property="ssNum"/> (xxx-xx-xxxx)</td> </tr> <tr> <td></td> <td><html:submit/></td> </tr> </table> </html:form> <logic:present name="searchForm" property="results"> <hr width="100%" size="1" noshade="true"> <bean:size id="size" name="searchForm" property="results"/> <logic:equal name="size" value="0"> <center><font color="red"><b> <bean:message key="error.search.not.found"/> </b></font></center> </logic:equal> <logic:greaterThan name="size" value="0"> <table border="1"> <tr> <th><bean:message key="label.search.name"/></th> <th><bean:message key="label.search.ssNum"/></th> </tr> <logic:iterate id="result" name="searchForm" property="results"> <tr> <td><bean:write name="result" property="name"/></td> <td><bean:write name="result" property="ssNum"/></td> </tr> </logic:iterate> </table> </logic:greaterThan> </logic:present> </body>
<html>
Note that the Bean Tag Library definition had to be added to index.jsp so that it could use the <bean:message> tag to source in text.
This command should be run from the directory where you have set up the Mini HR application (e.g., c:\java\MiniHR). To test the Spanish version of the application, you have to change your browser's language settings to Spanish. Following are the instructions for doing this with Microsoft Internet Explorer 6: 1. Open Internet Explorer's Internet Options dialog box by selecting Tools | Internet Options. 2. Click the Languages button on the General tab of the Internet Options dialog box, as shown here:
4. Add the Spanish (United States) [es-us] language preference by selecting it in the list, as shown here, and then clicking OK.
5. Select the Spanish (United States) [es-us] language preference and click Move Up so that it is the first preference in the list, like this:
Once you have added the Spanish language setting for your browser, you need to place the new MiniHR.war file that you just created into Tomcat's webapps directory and start Tomcat. As before, to access the Mini HR application, point your browser to http://localhost:8080/MiniHR/. When you run the updated Mini HR application with your browser set to Spanish, it should detect that you are accessing it from a Spanish-language browser and automatically serve the Spanish version of the application. Figure 10-1 shows the search page in Spanish.
Scope request
Description Variables placed in this scope persist until processing for the current request is completed. This scope differs from page scope because multiple servlets may be executed during the lifespan of a request. Page scope variables persist only for the execution of one servlet. Variables placed in this scope persist until the current users session is invalidated or expires. This scope is valid only if the JSP or servlet in question is participating in a session.
session
Note that variables must be explicitly placed into a scope, as shown here: <% request.setAttribute("reqScopeVar", "test"); %> This snippet uses JSPs implicit request object to place a variable into request scope. Similarly, the following snippet uses JSPs implicit session object to place a variable into session scope: <% session.setAttribute("sesScopeVar", "test"); %> Of course, variables can also be put into each of the scopes by JSP library tags as many of the tags in the Logic Tag Library do.
Remember from the overview of the web.xml file in Chapter 2 that the <taglib-uri> tag declares the URI (or alias) that will be referenced in each of your JSPs with a taglib directive. The <taglib-location> tag declares the actual location of the Tag Library Descriptor (.tld) file in your Web Archive. The following snippet illustrates how your JSPs will declare their use of the HTML Tag Library with a JSP taglib directive:
<%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %>
Notice that the uri attribute specified here is the same as that declared with the <taglib-uri> tag in the web.xml file. Also, notice that the prefix attribute is set to html. This attribute can be set to whatever you want; however, html is the accepted default for the HTML Tag Library. The prefix attribute declares the prefix that each tag must have when it is used in the JSP, as shown here:
<html:html action="/logon">
Because html was defined as the prefix, the form tag was used as shown. However, if you chose to use a prefix of strutshtml, the tag would be used the following way:
<strutshtml:form action="/logon">
messages
multibox
option
Tag options optionsCollection password radio reset rewrite select submit text textarea xhtml
Description Generates a set of HTML <option> tags for each element in a collection. Generates a set of HTML <option> tags for each element in a collection. Generates an HTML <input type=password> tag populated with data from a specified object. Generates an HTML <input type=radio> tag populated with data from a specified object. Generates an HTML <input type=reset> tag. Generates a URL by specifying a base URL and optionally specifying an anchor and/or query string parameters to add to the URL. Generates an HTML <select> tag. Generates an HTML <input type=submit> tag. Generates an HTML <input type=text> tag populated with data from a specified object. Generates an HTML <textarea> tag populated with data from a specified object. Instructs the rest of the tags in the HTML Tag Library to generate their output as XHTML instead of HTML.
The remainder of this chapter discusses each tag in detail, including a complete description of the tag, a table listing each of the tags attributes, and a usage example for the tag. In the tables that describe each tags attributes, pay special attention to the Required and Accepts Scriptlet columns. The Required column denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the tag will throw a javax.servlet.jsp.JspException at run time. Note that you can declare an error page in your JSP with a page directive to capture any JspExceptions that might be thrown, as shown here: <%@ page errorPage="error.jsp" %> If an exception occurs, the page specified by the errorPage attribute will be internally redirected to display an error page. The Accepts Scriptlet column denotes whether or not the given attributes value can be specified with a JSP scriptlet. If a JSP scriptlet is used to specify an attribute value, the scriptlet must comprise the complete value, quote () to quote (), as shown here. Correct: <html:checkbox property="<%=prop%>"> Incorrect: <html:checkbox property="<%=result%>-checked"> Notice in the incorrect example that -checked is used as part of the value for the property attribute following the scriptlet. This is invalid because there are extra characters between the end of the scriptlet and the ending quote. A corrected version of the incorrect example follows:
<html:checkbox property="<%=result + "-checked"%>"/> The concatenation of -checked is now part of the scriptlet and the scriptlet comprises the complete value for the attribute. One other point: Several of the tags have a set of tag attributes in common, such as onclick, onmousedown, and onkeypress. Rather than repeat the description for these common attributes numerous times, they are described once, at the end of this chapter in the Common Tag Attributes section. The Description column in each tags table of attributes refers you to the Common Tag Attributes section for any of the common tag attributes that the tag has in its set of tag attributes.
Attributes
Attribute server Description Specifies the server name to use when generating the corresponding HTML tags href attribute. Same as the corresponding HTML tags attribute with the same name.Specifies the frame or window target in which links will be opened. Accepts Scriptlet Yes Required No
target
Yes
No
Example Usage
The following snippet shows the basic usage of the base tag: <html:html> <head> <title></title> <html:base/> </head> Remember that this tag must be enclosed by opening and closing HTML <head> tags.
Attributes
Attribute Description Accepts Scriptlet Required
Attribute accesskey
Description See the Common Tag Attributes section at the end of this chapter. See Common Attributes. See Common Attributes. See Common Attributes. Tag Tag Tag
Required No
No No No No
Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., property[x]).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No
Attribute property
Description Same as the corresponding HTML tags name attribute.Specifies the name that will be associated with this control. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. Tag Tag Tag Tag Tag Tag
Required Yes
No No No No No No No
Specifies a constant value that this control will be populated with.This value will be used for the buttons label. If not specified, this tag will attempt to use its body content as the value for the buttons label.
Example Usage
The following snippet illustrates the basic usage of the button tag: <html:button property="direction" value="Next Page"/> The value specified with the value attribute will be used for the buttons label. Alternatively, you can specify the buttons label value by nesting a string between opening and closing button tags, as shown here: <html:button>Next Page</html:button>
Attributes
Attribute
Description
Required
accesskey
See the Common Tag Attributes section at the end of this chapter. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag Tag
No
alt altKey disabled onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No No No No No
Same as the corresponding HTML tags name attribute.Specifies the name that will be associated with this control.Caution: If you set this attribute to a value other than its default, Struts Cancel feature will not be triggered.
Attribute
Description
Accepts Scriptle t Tag Tag Tag Tag Tag Tag Yes Yes Yes Yes Yes Yes Yes
Required
See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes. See Common Attributes.
No No No No No No No
Specifies a constant value that this control will be populated with.This value will be used for the buttons label. If not specified, this tag will attempt to use its body content as the value for the buttons label.
Example Usage
The following snippet illustrates the basic usage of the cancel tag: <html:cancel value="Cancel Update"/> The value specified with the value attribute will be used for the buttons label. Alternatively, you can specify the buttons label value by nesting a string between opening and closing cancel tags, as shown here: <html:cancel>Cancel Update</html:cancel>
Attributes
Attribute accesskey Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. Accepts Scriptlet Yes Required No
alt altKey
Yes Yes
No No
Description See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index. (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to populate this control with data. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to set the corresponding HTML tags name attribute to.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. See Common Tag Attributes.
Required No No
name
Yes
No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No Yes
style
Yes
No
Description See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that the control will be populated with.
Required No No No No No No
Example Usage
The following snippet illustrates the basic usage of the checkbox tag: <html:form action="/register"> Subscribe to mailing list: <html:checkbox property="subscribe"/><br> <html:submit/> </html:form> This tag will look up the Form Bean associated with the Action specified by the form tags action attribute and then call the getter method for the field specified by this tags property attribute. The fields value will then be used to populate the HTML control with data.
Attributes
Attribute bundle Description Specifies the logical name of a resource bundle that will be used when looking up error keys. Specifies the key for a java.util.Locale AcceptsScriptlet Yes Required No
locale
Yes
No
Attribute
Description instance stored as a session attribute that will be used when looking up error keys.
AcceptsScriptlet
Required
name
Specifies the key (in any scope) under which the errors to be displayed are stored. If not specified, the default org.apache.struts.Globals.ERROR_KE Y key will be used. Specifies the field for which errors will be displayed. If not specified, all errors will be displayed.
Yes
No
property
Yes
No
Example Usage
The following snippet illustrates the basic usage of the errors tag: <html:errors/> This example will display all the errors currently stored. If you want to limit the errors that are displayed to a specific property, you can specify that property when using the errors tag, as shown here: <html:errors property="username"/> In this case, only the errors associated with the property specified by the property attribute will be displayed.
Attributes
Attribute accept Description Same as the corresponding HTML tags attribute with the same name. Specifies a comma-delimited list of content types that the server you submit the enclosing form to knows how to process. Browsers can use this list to limit the set of files made available for selection. See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. Accepts Scriptlet Yes Required No
No No No
Description See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Same as the corresponding HTML tags attribute with the same name.Specifies the maximum number of characters that this control will accept. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to populate this control with data. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to which the corresponding HTML tags name attribute is set.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute.
Required No No
maxlength
Yes
No
name
Yes
No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No Yes
Attribute size
Description Same as the corresponding HTML tags attribute with the same name.Specifies the number of characters that will be visible in the control. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that is used to populate the control.
Required No
No No No No No No No
Example Usage
The following snippet illustrates the basic usage of the file tag: <html:form method="POST" enctype="multipart/form-data" action="/attachFile"> Upload File: <html:file property="uploadFile"/><br> <html:submit/> </html:form> This tag will look up the Form Bean associated with the Action specified by the form tags action attribute and then call the getter method for the field specified by this tags property attribute. The fields value will then be used to populate the HTML control with data.
Attributes
Attribute action Description Specifies the logical name of an Action, defined in the struts-config.xml file, whose path will be used to define the corresponding HTML tags Accepts Scriptlet Yes Required Yes
Attribute enctype
Description attribute with the same name. Same as the corresponding HTML tags attribute with the same name.Specifies the encoding to be used when submitting this form if the POST method is used. This must be set to multipart/ form-data if you are using the file tag to upload files. Specifies the name of a field inside this form that initial focus will be assigned to using JavaScript. Specifies an index to be used if the field specified with the focus attribute is a field array such as a radio button group. Same as the corresponding HTML tags attribute with the same name.Specifies the HTTP method that will be used when submitting this form (i.e., GET or POST). DeprecatedUse the attribute instead. action
Required No
focus
Yes
No
focusIndex
Yes
No
method
Yes
No
name onreset
Yes Yes
No No
Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this form is reset. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this form is submitted. DeprecatedUse the attribute instead. action
onsubmit
Yes
No
scope scriptLanguage
Yes Yes
No No
Accepts false to denote that the language attribute of the <focus> tag generated by this tags focus attribute should be omitted.Note that the generated <focus> tags language attribute is omitted regardless when the tags in this library are in XHTML mode. See the Common Tag Attributes section at the end of this chapter.
style
Yes
No
Required No No No
Same as the corresponding HTML tags attribute with the same name.Specifies the frame or window target in which this form will be submitted. DeprecatedUse the attribute instead. action
type
Yes
No
Example Usage
The following snippet illustrates the basic usage of the form tag: <html:form action="/logon"> UserName: <html:text property="username"/><br> Password: <html:password property="password"/><br> <html:submit/> </html:form> The Form Bean associated with the Action specified by the action attribute will be used for populating the nested HTML controls inside the opening and closing form tags.
Attributes
Attribute Description Accepts Scriptle t Required
Attribute
Description
Required
action
Specifies the name of an Action, from the Action Mappings section of the Struts configuration file, that contains the URL for this frame. Specifies the anchor (e.g., #bottom) to be added to the URL for this frame. This value must be specified without the leading hash (#) character. Specifies the name of a forward, from the Global Forwards Configuration section of the Struts configuration file, that contains the URL for this frame. Same as the corresponding HTML tags attribute with the same name.Specifies the border size for this frame (0 for no border). Same as the corresponding HTML tags name attribute.Specifies the logical name to give the frame. Specifies the absolute URL, including protocol (e.g., http://www.yahoo.com), for this frame. Same as the corresponding HTML tags attribute with the same name.Specifies the URL for a long description of the frame that supplements the short description provided by the title attribute. Same as the corresponding HTML tags attribute with the same name.Specifies the number of pixels that will be placed between this frames contents and its top and bottom margins. Same as the corresponding HTML tags attribute with the same name.Specifies the number of pixels that will be placed between this frames contents and its left and right margins. Specifies the name of the java.util.Map object whose elements are added as query string parameters to the URL for this frame. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the java.util.Map object whose elements are added as query string parameters to the URL for this frame. Same as the corresponding HTML tags
No
anchor
Yes
No
forward
Yes
No
frameborder
Yes
No
frameName
Yes
No
href
Yes
No
longdesc
Yes
No
marginheight
Yes
No
marginwidth
Yes
No
name
Yes
No
noresize
Yes
No
Attribute
Description
Accepts Scriptle t
Required
attribute with the same name.Accepts true to denote that this frame will not be resizable. page Specifies the application-relative URL (starts with a leading slash, /) for this frame. Specifies the name of a single parameter to add to the URL for this frame. Specifies the name of an object whose value will be used as the value for the parameter specified with the paramId attribute. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the scope (application, page, request, or session) to look in for the object specified by the paramName attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the field of the object specified by the name attribute whose getter method will be called to return the java.util.Map object whose elements are added as query string parameters to the URL for this frame. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Same as the corresponding HTML tags attribute with the same name.Specifies whether or not this frame is scrollable. Specify yes to have scroll bars unconditionally, no to never have scroll bars, or auto to have scroll bars automatically based on how much Yes No
paramId
Yes
No
paramName
Yes
No
paramProperty
Yes
No
paramScope
Yes
No
property
Yes
No
scope
Yes
No
scrolling
Yes
No
Attribute
Description
Required
content is in the frame. style styleClass styleId title titleKey transaction See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts true or false to denote whether or not the current transaction token is included in the URL for this frame. No No No No No No
Example Usage
There are a few different ways to use the frame tag. The first way, shown here, uses the href attribute to specify an absolute URL for the frame: <html:frame href="http://www.yahoo.com/"/> The following example adds to the first example by specifying a single query string parameter to add to the base URL specified by the href attribute: <html:frame href="http://www.yahoo.com/" paramId="query" paramName="queryObj"/> This example takes the base URL specified by the href attribute and appends the query string parameter specified by the paramId and paramName attributes and composes a URL for the frame. Another way to use the frame tag is shown here: <html:frame page="/search.jsp" name="params"/> This example uses the page attribute to specify an application-relative base URL and uses the name attribute to specify a java.util.Map object whose entries are added to the URL as query string parameters.
Attributes
Attribute accesskey alt Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. Accepts Scriptlet Yes Yes Required No No
Description See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to populate this control with data. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to which the corresponding HTML tags name attribute is set.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. See Common Tag Attributes. See Common Tag Attributes.
Required No No
name
Yes
No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No Yes
style styleClass
Yes Yes
No No
Description See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that this control will be populated with. Accepts true to denote that this controls value will also be written out to the page so that it is visible.
Required No No No No No
Example Usage
The following snippet illustrates the basic usage of the hidden tag: <html:form action="/search"> <html:hidden property="lastId"/> <html:submit/> </html:form> This tag will look up the Form Bean associated with the Action specified by the form tags action attribute and then call the getter method for the field specified by this tags property attribute. The fields value will then be used to populate the HTML control with data.
Attributes
Attribute Description Accepts Scriptle t Yes Required
locale
Accepts true to denote that a java.util.Locale object, set to the current requests AcceptLanguage HTTP header, will be placed into the session. If a session does not exist, one will be created upon this tag storing the Locale object. Accepts true to denote that the rest of the tags in the HTML Tag Library will generate their output as XHTML instead of HTML.
No
xhtml
Yes
No
Example Usage
The following snippet shows the basic usage of the html tag: <html:html/>
If you want the HTML Tag Librarys tags to generate XHTML instead of HTML, use the html tag as shown here: <html:html xhtml="true"/>
Attributes
Attribute Description Accepts Scriptle t Yes Yes Required
accesskey align
See the Common Tag Attributes section at the end of this chapter. Same as the corresponding HTML tags attribute with the same name.Specifies how the image will be aligned (i.e., left, right, top, bottom, and so on). See Common Tag Attributes. See Common Tag Attributes. Same as the corresponding HTML tags attribute with the same name.Specifies the width of the border surrounding the image. Specifies the logical name of a resource bundle that will be used when looking up message keys specified by other attributes of this tag (i.e., pageKey and srcKey). See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., property[x]).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Specifies the key for a java.util.Locale instance stored as a session attribute that will be used when looking up message keys specified by other attributes of this tag (i.e., pageKey and srcKey).
No No
No No No
bundle
Yes
No
disabled indexed
Yes Yes
No No
locale
Yes
No
Attribute
Description
Accepts Scriptle t Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
Required
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup page pageKey
See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a module-relative URL for this buttons image. Specifies a key from Struts ApplicationResources.properties file whose value will be used to set the corresponding HTML tags src attribute with a module-relative URL. Same as the corresponding HTML tags name attribute.Specifies the name that will be associated with this control. Same as the corresponding HTML tags attribute with the same name.Specifies the source URL for this buttons image. Specifies a key from Struts ApplicationResources.properties file whose value will be used to set the corresponding HTML tags src attribute. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes.
No No No No No No No No No No No No No No No
property
Yes
No
src
Yes
No
srcKey
Yes
No
No No No No No No
Attribute
Description
Required
value
Specifies a constant value that this control will be populated with. If not specified, this tag will attempt to use its body content as the value.
No
Example Usage
As mentioned, there are two ways you can use the image tag. The first way, shown here, uses the page attribute to specify a module-relative URL for the generated HTML tags src attribute: <html:image page="previous.gif"/> The second way to use the image tag is shown here: <html:image src="/images/next.gif"/> This example uses the src attribute to specify a URL for the generated HTML tags src attribute. The image tag will be used in this way for most applications; however, if you are taking advantage of Struts module functionality, you will use the page or pageKey attribute.
Attributes
Attribute action Description Specifies the name of an action, from the Action Mappings Configuration section of the Struts configuration file, that contains the base URL for the image. Same as the corresponding HTML tags attribute with the same name.Specifies how the image will be aligned (i.e., left, right, top, bottom, and so on). See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. Same as the corresponding HTML tags attribute with the same name.Specifies the width of the border surrounding the image. Specifies the logical name of a resource bundle that will be used to look up messages for this tag. Same as the corresponding HTML Accepts Scriptlet Yes Required No
align
Yes
No
No No No
bundle
Yes
No
height
Yes
No
Attribute
Description tags attribute with the same name.Specifies the height of this image in pixels.
Accepts Scriptlet
Required
hspace
Same as the corresponding HTML tags attribute with the same name.Specifies the number of pixels that will be placed between this image and text surrounding it from the top or bottom. Same as the corresponding HTML tags name attribute.Specifies the scriptable name for the image. Same as the corresponding HTML tags attribute with the same name.Specifies the name of a serverside image map to use to map different regions of the image to different URLs. Specifies the key for a java.util.Locale instance stored as a session attribute that will be used when looking up message keys specified by other attributes of this tag (i.e., pageKey and srcKey). DeprecatedThis attribute has been removed from the HTML spec as of version 4.01 and will no longer be supported in future versions of Struts. Specifies the name of a module that contains the action specified with the action attribute. Specifies the name of the java.util.Map object whose elements are added as query string parameters to the URL for this image. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the java.util.Map object whose elements are added as query string parameters to the URL for this image. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes.
Yes
No
imageName
Yes
No
ismap
Yes
No
locale
Yes
No
lowsrc
Yes
No
module
Yes
No
name
Yes
No
No No No No No No
Description See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a module-relative URL for this image. Specifies a key from Struts ApplicationResources.properties file whose value will be used to set the corresponding HTML tags src attribute with a module-relative URL. Specifies the name of a single parameter to add to the URL for this image. Specifies the name of an object whose value will be used as the value for the parameter specified with the paramId attribute. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the scope (application, page, request, or session) to look in for the object specified by the paramName attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the field of the object specified by the name attribute whose getter method will be called to return the java.util.Map object whose elements are added as query string parameters to the URL for this image. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page,
Required No No No No No No
paramId
Yes
No
paramName
Yes
No
paramProperty
Yes
No
paramScope
Yes
No
property
Yes
No
scope
Yes
No
Attribute
Accepts Scriptlet
Required
src
Same as the corresponding HTML tags attribute with the same name.Specifies the source URL for this image. Specifies a key from Struts ApplicationResources.properties file whose value will be used to set the corresponding HTML tags src attribute. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Same as the corresponding HTML tags attribute with the same name.Specifies the name of a clientside image map to use for mapping different regions of the image to different URLs. Same as the corresponding HTML tags attribute with the same name.Specifies the number of pixels that will be placed between this image and text surrounding it from the left or right. Same as the corresponding HTML tags attribute with the same name.Specifies the width of this image in pixels.
Yes
No
srcKey
Yes
No
No No No No No No
vspace
Yes
No
width
Yes
No
Example Usage
There are a few different ways to use the img tag. The first way, shown here, uses the src attribute to specify an absolute URL for the image: <html:img src="http://www.domain.com/image.gif"/> The following example adds to the first example by specifying a single query string parameter to add to the base URL specified by the src attribute: <html:img href=" http://www.domain.com/image.gif" paramId="image" paramName="imageObj"/> This example takes the base URL specified by the href attribute and appends the query string parameter specified by the paramId and paramName attributes and composes a URL for the image.
Attributes
Attribute Description Accepts Scriptle t Yes Required
cdata
Accepts false to denote that the generated JavaScript code will not be enclosed in XML CDATA tags (i.e., <![CDATA[ ]]>).Note that CDATA tags are necessary to prevent JavaScript code from being parsed when used in conjunction with XHTML. Accepts false to denote that dynamic JavaScript will not be generated. Specifies the logical name of a Form Bean whose validation rules will be used to generate JavaScript validation code. Accepts false to denote that the generated JavaScript code will not be enclosed in HTML comment tags (i.e., <!- - - ->). Specifies an alternate JavaScript method name to be used instead of the default. Specifies the logical name of a page to use to filter which validations for the specified form will be generated. This logical name matches the logical name that can be applied to individual fields in the validation.xml file. Accepts false to denote that the language attribute of the <script> tag generated by this tag should be omitted.Note that the generated <script> tags language attribute is omitted regardless when the tags in
No
dynamicJavascript
No
No
formName
Yes
No
htmlComment
Yes
No
method
Yes
No
page
Yes
No
scriptLanguage
Yes
No
Attribute
Description
Required
this library are in XHTML mode. src Same as the corresponding JavaScript tags attribute with the same name.Specifies the URL to a static JavaScript file to be included. Accepts false to denote that static JavaScript will not be generated. No
staticJavascript
No
No
Example Usage
The following snippet illustrates the basic usage of the javascript tag: <html:javascript formName="logonForm"/> This example will generate JavaScript code for all the validation rules in the validation.xml file that are tied to the Form Bean specified by the formName attribute. If you want to separate the dynamic and static pieces of the generated JavaScript code, you can use the javascript tag as shown next: <html:javascript formName="logonForm" staticJavascript="false" src="staticJavascript.jsp"/> This usage only generates the dynamic JavaScript for the specified form. The following example generates the generic static JavaScript code: <html:javascript formName="logonForm" dynamicJavascript="false"/> This snippet must be placed into a separate file named staticJavascript.jsp to match the file name specified by the src attribute of the previous example.
Attributes
Description See the Common Tag Attributes section at the end of this chapter. Specifies the name of an Action, from the Action Mappings section of the Struts configuration file, that contains the URL for this link. Specifies the anchor (e.g., #bottom) to be added to the URL for this link. This value must be specified without the leading hash (#) character. Specifies the name of a forward, from the Global Forwards Configuration section of the Struts configuration file, that contains the URL for this link. Specifies the absolute URL, including protocol (e.g., http://www.yahoo.com), for this link. Accepts true to denote that an indexed parameter, whose name is specified by the indexId attribute, will be added to the URL for this link.This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Specifies the name to use for an indexed parameter if the indexed attribute is used.This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Same as the corresponding HTML tags name attribute.Specifies the name of a page anchor for intrapage hyperlinks. Specifies the name of a module that contains the action specified with the action attribute. Specifies the name of the java.util.Map object whose elements are added as query string parameters to the URL for this link. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the java.util.Map object whose elements are added as query string parameters to the URL for this
Required No No
anchor
Yes
No
forward
Yes
No
href
Yes
No
indexed
Yes
No
indexId
Yes
No
linkName
Yes
No
module
Yes
No
name
Yes
No
Attribute
Description link.
Accepts Scriptlet Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
Required
onblur onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup page
See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the application-relative URL (starts with a leading slash, /) for this link. Specifies the name of a single parameter to add to the URL for this link. Specifies the name of an object whose value will be used as the value for the parameter specified with the paramId attribute. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the scope (application, page, request, or session) to look in for the object specified by the paramName attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application.
No No No No No No No No No No No No No
paramId
Yes
No
paramName
Yes
No
paramProperty
Yes
No
paramScope
Yes
No
Attribute property
Description Specifies the field of the object specified by the name attribute whose getter method will be called to return the java.util.Map object whose elements are added as query string parameters to the URL for this link. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts true or false to denote whether or not the current transaction token will be included in the URL for this link.
Required No
scope
Yes
No
No No No No No No No
Example Usage
There are a few different ways to use the link tag. The first way, shown here, uses the href attribute to specify an absolute URL for the frame: <html:link href="http://www.yahoo.com/"/> The following example adds to the first example by specifying a single query string parameter to add to the base URL specified by the href attribute: <html:link href="http://www.yahoo.com/" paramId="query" paramName="queryObj"/> This example takes the base URL specified by the href attribute and appends the query string parameter specified by the paramId and paramName attributes and composes a URL that the tag then redirects to. Another way to use the link tag is shown here: <html:link page="/search.jsp" name="params"/> This example uses the page attribute to specify an application-relative base URL and uses the name attribute to specify a java.util.Map object whose entries are added to the URL as query string parameters.
The messages tag is used to display a set of messages stored as an org.apache.struts .action.ActionErrors object, an org.apache.struts.action.ActionMessages object, a String, or a String array in request scope. Similar to the errors tag, this tag will take the stored messages and iterate over them; however, instead of actually outputting the messages to the JSP, this tag behaves similar to the Logic Tag Librarys iterate tag. On each iteration, it stores the current message in a page scope JSP variable.
Attributes
Attribute bundle Description Specifies the logical name of a resource bundle that will be used when looking up message keys specified by other attributes of this tag (e.g., footer and header). Specifies a key from Struts ApplicationResources.properties file whose value will be printed after the messages have been iterated through. Specifies a key from Struts ApplicationResources.properties file whose value will be printed before the messages have been iterated through. Specifies the name for a page scope JSP variable that will hold a reference to the current message on each iteration. Specifies the key for a java.util.Locale instance stored as a session attribute that will be used when looking up message keys specified by other attributes of this tag (e.g., footer and header). Accepts true to denote that the messages to be displayed are stored under the org.apache.struts.Globals.MESSAGE_KE Y key instead of the org.apache.struts.Globals.ERROR_KEY key. Specifies the key (in any scope) under which the messages to be displayed are stored. If not specified, the default org.apache.struts.Globals.ERROR_KEY key will be used. Specifies the field for which messages will be displayed. If not specified, all messages will be displayed. Accepts Scriptlet Yes Required No
footer
Yes
No
header
Yes
No
id
No
Yes
locale
Yes
No
message
Yes
No
name
Yes
No
property
Yes
No
Example Usage
The following snippet illustrates the basic usage of the messages tag: <html:messages id="msg"> <li><bean:write name="msg"/></li> </html:messages>
This example will display all the messages currently stored. If you want to limit the messages that are displayed to a specific property, you can specify that property when using the messages tag, as shown here: <html:messages id="msg" property="category"> <li><bean:write name="msg"/></li> </html:messages> In this case, only the messages associated with the property specified by the property attribute will be displayed.
Attributes
Attribute accesskey alt altKey disabled name Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to populate this control with data. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts Scriptlet Yes Yes Yes Yes Yes Required No No No No No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No
Attribute property
Description Specifies the value to set the corresponding HTML tags name attribute to.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that the control will be populated with.
Required Yes
No No No No No No No
Example Usage
The following snippet illustrates the basic usage of the multibox tag: <logic:iterate id="item" property="items"> <html:multibox property="selectedItems"> <bean:write name="item"/> </html:multibox> <bean:write name="item"/> </logic:iterate> In this example, the iterate tag loops through each of the elements in the collection specified by its property attribute. Each iteration of the loop uses the multibox tag to generate an HTML <input type=checkbox> tag whose checked status is based on whether or not the loops current element is present in the collection specified by the property attribute of the multibox tag.
Attributes
Attribute bundle
Description Specifies the logical name of a resource bundle that will be used to look up the message specified by the key attribute. See the Common Tag Attributes section at the end of this chapter. Specifies a key from Struts ApplicationResources.properties file whose value will be used to set the text that will be displayed for this option. If not specified, the text displayed for this option will be taken from this tags body content. Specifies the key for a java.util.Locale instance stored as a session attribute that will be used when looking up the message specified by the key attribute. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that the control will be populated with.
Required No
disabled key
Yes Yes
No No
locale
Yes
No
No No No Yes
Example Usage
The following snippet illustrates the basic usage of the option tag: <html:select property="gender"> <html:option value="male">Male</html:option> <html:option value="female">Female</html:option> </html:select> If the value specified by an option tags value attribute matches the value of the Form Bean field specified by the select tags property attribute, the HTML <option> tag will be marked as selected when it is generated.
Attributes
Attribute collection
Description Specifies the name of a collection object (in any scope) whose elements will be used to create a set of options. Accepts false to denote that option labels will not be filtered for sensitive HTML characters (e.g., > and <). Specifies the name of an object (in any scope) whose field, specified by the property attribute, will have its getter method called to return a collection object whose elements will be used as labels for the created options. Specifies the field of the object specified by the name attribute whose getter method will be called to return a collection object whose elements will be used as labels for the created options. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will have its getter method called to return a collection object whose elements will be used to create a set of options. Specifies the field of the object specified by the name attribute whose getter method will be called to return a collection object whose elements will be used to create a set of options. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes.
Required No
filter
Yes
No
labelName
Yes
No
labelProperty
Yes
No
name
Yes
No
property
Yes
No
style styleClass
Yes Yes
No No
Example Usage
As mentioned there are three basic ways you can use the options tag. The first way, shown here, uses the collection attribute to specify the collection from which to create options: <html:select property="category"> <html:options collection="catOptions"/> </html:select>
This example assumes you have defined a collection named category and then uses its elements to generate a set of options. The second way to use the options tag is shown here: <html:select property="category"> <html:options name="catOptions"/> </html:select> This example is very similar to the first example; however, it differs in that the collection from which to generate options is specified with the name attribute. The third way to use the options tag is shown here: <html:select property="category"> <html:options name="cat" property="options"/> </html:select> In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return a collection for which to generate options.
Attributes
Attribute Description Accepts Scriptle t Yes Required
filter
Accepts false to denote that option labels will not be filtered for sensitive HTML characters (e.g., > and <). Specifies the field of the object specified with the name and property attributes whose value will be used as the label for the generated options. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will have its getter method called to return a collection object whose elements will be used to create a set of options. Specifies the field of the object specified by the name attribute whose getter method will be called to return a collection object whose elements will be used to create a set of options. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute.
No
label
Yes
No
name
Yes
No
property
Yes
No
Attribute
Description
Required
See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. Specifies the field of the object specified with the name and property attributes whose value will be used as the value for the generated options.
No No No
Example Usage
The following snippet illustrates the basic usage of the optionsCollection tag: <html:select property="category"> <html:optionsCollection name="cat" property="options"/> </html:select> In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return a collection for which to generate options.
Attributes
Attribute accesskey alt altKey disabled indexed Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Same as the corresponding HTML tags attribute with the same name.Specifies the maximum number of characters that this control will accept. Specifies the name of an object (in any scope) whose field, specified by Accepts Scriptlet Yes Yes Yes Yes Yes Required No No No No No
maxlength
Yes
No
name
Yes
No
Attribute
Description the property attribute, will be used to populate this control with data.
Accepts Scriptlet
Required
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to set the corresponding HTML tags name attribute to.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. Same as the corresponding HTML tags attribute with the same name.Accepts true to denote that the controls value will be read-only, preventing it from being changed. Accepts false to denote that the value specified with the name and property attributes or specified with the value attribute will not be used to populate this control. Same as the corresponding HTML tags attribute with the same name.Specifies the number of characters that will be visible in the control. See Common Tag Attributes.
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No Yes
readonly
Yes
No
redisplay
Yes
No
size
Yes
No
style
Yes
No
Description See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that the control will be populated with.
Required No No No No No No
Example Usage
The following snippet illustrates the basic usage of the password tag: <html:form action="/logon"> UserName: <html:text property="username"/><br> Password: <html:password property="password"/><br> <html:submit/> </html:form> This tag will look up the Form Bean associated with the Action specified by the form tags action attribute and then call the getter method for the field specified by this tags property attribute. The fields value will then be used to populate the HTML control with data.
Attributes
Attribute accesskey alt altKey disabled idName Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. If an iterator is used to generate a series of radio tags, this attribute can be used to specify a name for the object exposed by the iterator. In this case, the value attribute is used as the name of a field on the exposed object whose getter method will be called to return the value that the radio tag for this iteration will be populated with. Accepts true to denote that the Accepts Scriptlet Yes Yes Yes Yes Yes Required No No No No No
indexed
Yes
No
Attribute
Description value assigned to the corresponding HTML tags name attribute will have an index (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag.
Accepts Scriptlet
Required
name
Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to populate this control with data. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to set the corresponding HTML tags name attribute to.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes.
Yes
No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No Yes
No No No No No No
Attribute value
Description Specifies a constant value that the control will be populated with.
Required Yes
Example Usage
The following snippet illustrates the basic usage of the radio tag: <html:form action="/register"/> Male: <html:radio property="gender" value="male"/><br> Female: <html:radio property="gender" value="female"/><br> <html:submit/> </html:form> This tag will look up the Form Bean associated with the Action specified by the form tags action attribute and then call the getter method for the field specified by this tags property attribute. If the fields value matches the value specified with the value attribute, the radio button will be selected.
Attributes
Attribute accesskey Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts Scriptlet Yes Required No
alt altKey disabled onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No No
Description See Common Tag Attributes. See Common Tag Attributes. Same as the corresponding HTML tags name attribute.Specifies the name that will be associated with this control. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that this control will be populated with.This value will be used for the buttons label. If not specified, this tag will attempt to use its body content as the value for the buttons label.
Required No No No
No No No No No No No
Example Usage
The following snippet illustrates the basic usage of the reset tag: <html:reset value="Reset Form"/> The value specified with the value attribute will be used for the buttons label. Alternatively, you can specify the buttons label value by nesting a string between opening and closing reset tags, as shown here: <html:reset>Reset Form</html:reset>
You can use the paramId attribute in conjunction with the paramName attribute, and optionally the paramProperty attribute, to specify a single parameter. You can use the name attribute, either alone or in tandem with the property attribute, to specify a java.util.Map object that will be used to add several parameters.
Attributes
Attribute action Description Specifies the name of an action, from the Action Mappings Configuration section of the Struts configuration file, that contains the base URL for the generated URL. Specifies the anchor (e.g., #bottom) to be added to the generated URL. This value must be specified without the leading hash (#) character. Specifies the name of a forward, from the Global Forwards Configuration section of the Struts configuration file, that contains the base URL for the generated URL. Specifies the absolute base including protocol for http://www.yahoo.com), generated URL. URL, (e.g., the Accepts Scriptlet Yes Required No
anchor
Yes
No
forward
Yes
No
href
Yes
No
module
Specifies the name of a module that contains the action specified with the action attribute. Specifies the name of the java.util.Map object whose elements are added as query string parameters to the generated URL. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the java.util.Map object whose elements are added as query string parameters to the generated URL. Specifies the application-relative base URL (starts with a leading slash, /) for the generated URL. Specifies the name of a single parameter to add to the generated URL. Specifies the name of an object whose value will be used as the value for the parameter specified with the paramId attribute. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value will be used as the value for the parameter specified with the
Yes
No
name
Yes
No
page
Yes
No
paramId paramName
Yes Yes
No No
Attribute
Required
paramProperty
Specifies the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the scope (application, page, request, or session) to look in for the object specified by the paramName attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the field of the object specified by the name attribute whose getter method will be called to return the java.util.Map object whose elements are added as query string parameters to the generated URL. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Accepts true or false to denote whether or not the current transaction token will be included in the generated URL.
No
paramScope
Yes
No
property
Yes
No
scope
Yes
No
transaction
Yes
No
Example Usage
There are a few different ways to use the rewrite tag. The first way, shown here, uses the href attribute to specify an absolute URL for the frame: <html:rewrite href="http://www.yahoo.com/"/> The following example adds to the first example by specifying a single query string parameter to add to the base URL specified by the href attribute: <html:rewrite href="http://www.yahoo.com/" paramId="query" paramName="queryObj"/> This example takes the base URL specified by the href attribute and appends the query string parameter specified by the paramId and paramName attributes and composes a URL that the tag then redirects to. Another way to use the rewrite tag is shown here: <html:rewrite page="/search.jsp" name="params"/> This example uses the page attribute to specify an application-relative base URL and uses the name attribute to specify a java.util.Map object whose entries are added to the URL as query string parameters.
Attributes
Attribute Description Accepts Scriptle t Yes Yes Yes Yes Required
See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Same as the corresponding HTML tags attribute with the same name.Accepts an arbitrary value to denote that multiple selections can be made on the control. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to determine which nested option tag will be set as selected. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes.
No No No No
multiple
Yes
No
name
Yes
No
No No No No No No No No No
Attribute
Description
Required
See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to set the corresponding HTML tags name attribute to.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to determine which nested option tag will be set as selected. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. Same as the corresponding HTML tags attribute with the same name.Specifies the number of options that will be visible at a time. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to use when determining which nested option tag will be set as selected.
No No No No Yes
size
Yes
No
No No No No No No No
Example Usage
As mentioned, there are two ways you can use the select tag. The first way, shown here, omits the multiple attribute so that only one nested option will be selected: <html:select property="gender"> <html:option value="male">Male</html:option> <html:option value="female">Female</html:option> </html:select> The second way to use the select tag is shown here: <html:select property="color" multiple="true" size="3">
<html:option value="red">Red</html:option> <html:option value="green">Green</html:option> <html:option value="blue">Blue</html:option> <html:option value="black">Black</html:option> <html:option value="white">White</html:option> </html:select> This example uses the multiple attribute to denote that the object specified with the property attribute will be an array of values for each nested option that will be selected.
Attributes
Attribute accesskey Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., property[x]).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts Scriptlet Yes Required No
No No No No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No
Description See Common Tag Attributes. Same as the corresponding HTML tags name attribute.Specifies the name that will be associated with this control. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that this control will be populated with.This value will be used as the buttons label.
Required No No
No No No No No No No
Example Usage
The following snippet illustrates the basic usage of the submit tag: <html:submit value="Add Record"/> The value specified with the value attribute will be used for the buttons label. Alternatively, you can specify the buttons label value by nesting a string between opening and closing submit tags, as shown here: <html:submit>Next Page</html:submit>
Attributes
Attribute accesskey alt altKey disabled indexed Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML Accepts Scriptlet Yes Yes Yes Yes Yes Required No No No No No
Attribute
Description tags name attribute must have an index (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag.
Accepts Scriptlet
Required
maxlength
Same as the corresponding HTML tags attribute with the same name.Specifies the maximum number of characters that this control will accept. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to populate this control with data. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to set the corresponding HTML tags name attribute to.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. Same as the corresponding HTML tags attribute with the same name.Accepts true to denote that the controls value will be read-only, preventing it from being changed.
Yes
No
name
Yes
No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No Yes
readonly
Yes
No
Attribute size
Description Same as the corresponding HTML tags attribute with the same name.Specifies the number of characters that will be visible in the control. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that the control will be populated with.
Required No
No No No No No No No
Example Usage
The following snippet illustrates the basic usage of the text tag: <html:form action="/search"> Search Query: <html:text property="query"/><br> <html:submit/> </html:form> This tag will look up the Form Bean associated with the Action specified by the form tags action attribute and then call the getter method for the field specified by this tags property attribute. The fields value will then be used to populate the HTML control with data.
Attributes
Attribute accesskey alt altKey cols Description See the Common Tag Attributes section at the end of this chapter. See Common Tag Attributes. See Common Tag Attributes. Same as the corresponding HTML tags attribute with the same name.Specifies the number of columns Accepts Scriptlet Yes Yes Yes Yes Required No No No No
Attribute
Description to display.
Required
disabled indexed
See Common Tag Attributes. Accepts true to denote that the value assigned to the corresponding HTML tags name attribute will have an index (e.g., name[x].property).This attribute is applicable only when this tag is nested inside the Logic Tag Librarys iterate tag. Specifies the name of an object (in any scope) whose field, specified by the property attribute, will be used to populate this control with data. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies the value to set the corresponding HTML tags name attribute to.Additionally, this attribute is used to specify the field of the object specified by the name attribute whose getter method will be called to return an object whose value will be used to populate this control. If no object is specified with the name attribute, the Form Bean object associated with the enclosing form tag will be used to retrieve the property specified by this attribute. Same as the corresponding HTML tags attribute with the same name.Accepts true to denote that the controls value will be read-only, preventing it from being changed.
No No
name
Yes
No
onblur onchange onclick ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup property
Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
No No No No No No No No No No No No No Yes
readonly
Yes
No
Attribute rows
Description Same as the corresponding HTML tags attribute with the same name.Specifies the number of rows to display. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. See Common Tag Attributes. Specifies a constant value that the control will be populated with.
Required No
No No No No No No No
Example Usage
The following snippet illustrates the basic usage of the textarea tag: <html:form action="/feedback"> Comments: <html:textarea property="comments"/><br> <html:submit/> </html:form> This tag will look up the Form Bean associated with the Action specified by the form tags action attribute and then call the getter method for the field specified by this tags property attribute. The fields value will then be used to populate the HTML control with data.
Attributes
This tag does not have any attributes.
Example Usage
Using the xhtml tag is very simple because it has no attributes that can or need to be set. You simply just use the tag as shown here: <html:xhtml/>
You must place this tag before using any other tags from this library; otherwise, they will not know to generate XHTML. Note that using this tag will not guarantee that your JSP is 100 percent XHTML compliant; you must ensure that any direct usages of HTML tags also conform.
alt
Yes
No
altKey
Yes
No
disabled
Yes
No
onblur
Yes
No
onchange
Yes
No
onclick
Yes
No
ondblclick
Yes
No
Attribute onfocus
Description Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control receives input focus. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control has focus and a key is pressed. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control has focus and a key is pressed and released. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control has focus and a key is released. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control is under the mouse pointer and a mouse button is pressed. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control is under the mouse pointer and the mouse is moved. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control is under the mouse pointer and is then moved away from the control. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control is not under the mouse pointer and is then moved to the control. Same as the corresponding HTML tags attribute with the same name.Specifies the JavaScript code to execute when this control is under the mouse pointer and a mouse button is released.
Required No
onkeydown
Yes
No
onkeypress
Yes
No
onkeyup
Yes
No
onmousedown
Yes
No
onmousemove
Yes
No
onmouseout
Yes
No
onmouseover
Yes
No
onmouseup
Yes
No
Attribute style
Description Same as the corresponding HTML tags attribute with the same name.Specifies the CSS style to apply to this control. Same as the corresponding HTML tags class attribute.Specifies the CSS class to apply to this control. Same as the corresponding HTML tags id attribute.Specifies the CSS id to apply to this control. Same as the corresponding HTML tags attribute with the same name.Specifies the tab order for this control. Same as the corresponding HTML tags attribute with the same name.Specifies the title for this control. Specifies a key from Struts ApplicationResources.properties file whose value will be used to set the corresponding HTML tags title attribute.
Required No
styleClass
Yes
No
styleId
Yes
No
tabindex
Yes
No
title
Yes
No
titleKey
Yes
No
Before discussing each of the tags in this library, its necessary to review how variables and scope work in JSPs. First, remember that JSPs get converted into servlets and then compiled before they are executed. All of the HTML and code inside the JSP gets placed into the generated servlets service( ) method. Because of this, any variables that get defined inside the JSP with a scriptlet, as shown here, are local to the service( ) method:
<% String test = "test value"; %>
Similarly, any variables that are defined with a JSP declaration are local to the service( ) method:
<%! String test = "test value"; %>
Also, all of the implicit JSP objects, such as application, request, and session, are local to the resulting servlets service( ) method. JSPs and servlets also have the notion of scope for variables, because some variables need to persist longer than the lifespan of a page request and some variables need to be accessible outside of a servlets service( ) method. There are four scopes that JSP variables can be placed in: application, page, request, and session. The following table explains each scope. Scope application page Description Variables placed in this scope persist for the life of an application. Variables placed in this scope persist until the current JSPs service( ) method completes. Included JSPs cannot see page scope variables from the page including them. Also, this scope is exclusive to JSPs. Variables placed in this scope persist until processing for the current request is completed. This scope differs from page scope because multiple servlets may be executed during the lifespan of a request. Page scoped variables only persist for the execution of one servlet. Variables placed in this scope persist until the current users session is invalidated or expires. This scope is only valid if the JSP or servlet in question is participating in a session.
request
session
Note that variables must be explicitly placed into a scope, as shown here:
<% request.setAttribute("reqScopeVar", "test"); %>
This snippet uses JSPs implicit request object to place a variable into request scope. Similarly, the following snippet uses JSPs implicit session object to place a variable into session scope:
<% session.setAttribute("sesScopeVar", "test"); %>
Of course, variables can also be put into each of the scopes by JSP tag library tags as do many of the tags in the Bean Tag Library.
define header
Description Used to perform a request to a page and store the contents as a String object in a JSP scripting variable with page scope. Used to retrieve an internationalized message from Struts ApplicationResources.properties file and render it to a JSPs output stream. Used to store one of a JSPs implicit objects in a JSP scripting variable with page scope. Used to store a parameter from the incoming request as a String object in a JSP scripting variable with page scope. Used to load the contents of a Web application resource and store them as a String object in a JSP scripting variable with page scope. Used to store the size (number of elements) of an array, java.util.Collection-based object, or java.util.Map-based object as a java.lang.Integer in a JSP scripting variable with page scope. Used to store a reference to a Struts configuration object in a JSP scripting variable with page scope. Used to render the value of an object to a JSPs output stream.
size
struts write
Following is a dedicated section for each tag, including a complete description of the tag, a table listing each of the tags attributes, and a usage example for the tag. In the tables that describe each tags attributes, pay special attention to the Accepts Scriptlet and Required columns. The Required column simply denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the tag will throw a javax.servlet.jsp.JspException at run time. Note that you can declare an error page in your JSP with a page directive to capture any JspExceptions that might be thrown, as shown here: <%@ page errorPage="error.jsp" %> If an exception occurs, the page specified by the errorPage attribute will be internally redirected to display an error page. The Accepts Scriptlet column denotes whether or not the given attributes value can be specified with a JSP scriptlet. If a JSP scriptlet is used to specify an attribute value, the scriptlet must comprise the complete value, quote () to quote (), as shown here. Correct: <bean:cookie id="category" name="<%=catName%>"/> Incorrect: <bean:cookie id="category" name="<%=catName%>-cat"/> Notice in the incorrect example that -cat is used as part of the value for the name attribute following the scriptlet. This is invalid because there are extra characters between the end of the scriptlet and the ending quote. A corrected version of the incorrect example follows: <bean:cookie id="category" name="<%=catName + "-cat"%>"/> The concatenation of -cat is now part of the scriptlet and the scriptlet comprises the complete value for the attribute.
Attributes
Attribute id Description Specifies the name of the JSP variable that will hold the specified HTTP cookie(s). Accepts an arbitrary value to denote that multiple cookies should be stored. If specified, cookies will be stored in a Cookie[ ] array. If not specified and multiple cookies have the same name, the first cookie with the name specified will be stored. Specifies the name of the HTTP cookie(s) to store in the JSP variable. If the cookie specified with the name attribute is not found and this attribute is specified, a new Cookie object will be created and assigned the value of this attribute. Accepts Scriptlet No Required Yes
multiple
Yes
No
name
Yes
Yes
value
Yes
No
Example Usage
The following snippet shows a basic usage of the cookie tag: <bean:cookie id="category" name="cat" value="default"/> This example attempts to find a cookie named cat in the incoming request and store a reference to it in a variable named category. If the cookie is not in the request, a new cookie will be created and assigned the value of default. This new cookie will then have a reference stored to it. After you have used the cookie tag to store a cookie, you can access it with the following snippet: Category Cookie: <%=category.getValue()%>
This snippet simply uses a JSP expression to render the value of the Cookie object stored in the category variable. Additionally, because the cookie tag places the category variable in page scope, it can be accessed by other tags, as shown here: Category Cookie: <bean:write name="category" property="value"/> The following snippet shows an example of using the cookie tag for multiple cookies with the same name: <bean:cookie id="category" name="cat" multiple="true"/> The multiple attribute accepts arbitrary values; thus, you could use true as shown in the example, or any other value. As long as a value is present, a Cookie[ ] array will be used to store the cookies. To access the first cookie stored when using the multiple attribute, use the following snippet: Category Cookie: <%=category[0].getValue()%>
Attributes
Attribute id name Description Specifies the name of the JSP variable that will hold the specified object reference. Specifies the name of the object to which a reference will be stored. If the property Accepts Scriptlet No Yes Required Yes No
Attribute
Description attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the object to which a reference will be stored.
Accepts Scriptlet
Required
property
Specifies the field of the object specified by the name attribute, whose getter method will be called to return the object to which a reference will be stored. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the scope (application, page, request, or session) that the specified object reference should be stored in. Specifies the fully qualified class type (e.g., java.lang.Integer) of the object being stored as a scripting variable. If not specified, defaults to java.lang.String if you specify a value attribute; otherwise, defaults to java.lang.Object. Specifies a value that will be used to create a new String object whose reference will be stored.
Yes
No
scope
Yes
No
toScope
Yes
No
type
Yes
No
value
Yes
No
Example Usage
As mentioned, there are four ways you can use the define tag. The first way, shown here, uses the name attribute to specify the name of the object to which a reference will be stored: <bean:define id="name" name="nameObj"/> Of course, this example assumes you have an object in some scope with the name nameObj. Remember that you can explicitly specify the scope of the object with the scope attribute. The second way to use the define tag is shown here: <bean:define id="name" name="nameObj" property="nameField"/> In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object to which a reference will be stored. Again, you can explicitly specify the scope of the object with the scope attribute. The third way to use the define tag is shown here: <bean:define id="name" value="James"/> In this example, the value attribute is used to specify the value for a new String object whose reference will be stored. The following is the fourth and final way to use the define tag:
<bean:define id="name">James</bean:define> This example uses the nested string between the opening and closing define tags to specify a value for a new String object whose reference will be stored. After you have used the define tag to store a reference to an object, you can access it with the following snippet: Name: <%=name%> This snippet simply uses a JSP expression to render the value of the object stored in the name variable. Additionally, because the define tag places the name variable into a scope, it can be accessed by other tags, as shown here: Name: <bean:write name="name"/>
Attributes
Attribute id Description Specifies the name of the JSP variable that will hold the specified HTTP header(s). Accepts an arbitrary value to denote that multiple headers should be stored. If specified, headers will be stored in a String[ ] array. If not specified and multiple headers have the same name, the first header with the name specified will be stored. Specifies the name of the HTTP header(s) to store in the JSP variable. Specifies the default value to assign to the String object being placed in the JSP variable if the specified header is not present in the incoming request. Accepts Scriptlet No Required Yes
multiple
Yes
No
name
Yes
Yes
value
Yes
No
Example Usage
The following snippet shows a basic usage of the header tag:
<bean:header id="browser" name="User-Agent" value="unknown"/> This example attempts to find an HTTP header named User-Agent in the incoming request and store a reference to it in a variable named browser. If the header is not in the request, a new String object will be created and have the value of the value attribute assigned to it. A reference to the String will then be stored. After you have used the header tag to store a header, you can access it with the following snippet: This page was requested using: <%=browser%> This snippet simply uses a JSP expression to render the value of the browser variable. Additionally, because the header tag places the browser variable in page scope, it can be accessed by other tags, as shown here: This page was requested using: <bean:write name="browser"/> The following snippet shows an example of using the header tag for multiple headers with the same name: <bean:header id="languages" name="Accept-Language" multiple="true"/> Remember that the multiple attribute accepts an arbitrary value; thus, you could use true as shown in the example, or any other value. As long as a value is present, a String[ ] array will be used to store the headers. To access the first header stored when using the multiple attribute, use the following snippet: First Language: <%=languages[0]%>
Attributes
Attribute anchor Description Specifies the anchor (e.g., #bottom) to be added to the URL for this include. This value should be specified without the leading hash (#) character. Specifies the name of a forward, from the Global Forwards Configuration section of the Struts configuration file, which contains the URL of the page whose Accepts Scriptlet Yes Required No
forward
Yes
No
Attribute
Required
href
Specifies the absolute URL, including the protocol, of the page whose contents should be stored (e.g., http://www.yahoo.com). Specifies the name of the JSP variable that will hold the contents of the specified page. DeprecatedUse the page attribute instead. Specifies the application-relative URL (starts with a leading slash, /) of the page whose contents should be stored. Accepts true or false to denote whether or not you want the current transaction token included in the URL for this include.
No
id
No
Yes
name page
Yes Yes
No No
transaction
Yes
No
Example Usage
As mentioned, there are three ways you can use the include tag. The first way, shown here, uses the forward attribute to specify the name of a forward, from the Global Forwards Configuration section of the Struts configuration file, whose contents will be stored: <bean:include id="searchPageContents" forward="search"/> The following is the second way to use the include tag: <bean:include id="yahooContents" href="http://www.yahoo.com/"/> This example uses the href attribute to specify the absolute URL of a page whose contents will be stored. The third way to use the include tag is shown here: <bean:include id="searchPageContents" page="/search.jsp"/> In this example, the page attribute is used to specify an application-relative URL whose contents will be stored.
error.search.criteria.missing=<li>Search Criteria Missing</li> error.search.ssNum.invalid=<li>Invalid Social Security Number</li> errors.header=<font color="red"><b>Validation Error(s)</b></font><ul> errors.footer=</ul><hr width="100%" size="1" noshade="true"> As you can see, the file is made up of key/value pairs. The message tag simply looks up a message with a given key and returns its matching value. The message tag also allows you to insert content into messages at run time before they are rendered using parametric replacement, which is the substitution of an argument for a placeholder parameter at run time. Following is an example message that uses parametric replacement: missing.field=The {0} field is missing. Notice the {0} reference in the message. At run time, the {0} parameter will be replaced with another value. The message tag allows you to specify what the replacement values are by use of the arg0 arg4 attributes. Thus, you can have up to five parametric replacements in a message with {0} {4}. There are three ways you can use the message tag, as listed here and shown later in the Example Usage section: You can specify a messages key with the key attribute. You can use the name attribute to specify the name of the object whose value will be used as the key for the message. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used as the key for the message. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute. If the specified message does not exist, a JspException will be thrown by this tag at run time.
Attributes
Attribute arg0 Description First parametric replacement value as specified by {0} in the message to be rendered. Second parametric replacement value as specified by {1} in the message to be rendered. Third parametric replacement value as specified by {2} in the message to be rendered. Fourth parametric replacement value as specified by {3} in the message to be rendered. Fifth parametric replacement value as specified by {4} in the message to be rendered. Specifies the name of the application scoped org.apache.struts.util.MessageResources object that contains the messages to be Accepts Scriptlet Yes Required No
arg1
Yes
No
arg2
Yes
No
arg3
Yes
No
arg4
Yes
No
bundle
Yes
No
Attribute
Accepts Scriptlet
Required
key locale
Specifies the name of the key whose corresponding value will be rendered. Specifies the name of the session scoped java.util.Locale object to use for selecting which message to render. Defaults to the value stored in the org.apache.struts.Globals.LOCALE_KEY constant. Specifies the name of the object whose value will be used as the key for the message. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the object whose value will be used as the key for the message. Specifies the field, of the object specified by the name attribute, whose getter method will be called to return the object whose value will be used as the key for the message. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application.
Yes Yes
No No
name
Yes
No
property
Yes
No
scope
Yes
No
Example Usage
As mentioned, there are three ways you can use the message tag. The first way, shown here, uses the key attribute to specify the key for the message that will be rendered: <bean:message key="label.search.name"/> The following is the second way to use the message tag: <bean:message name="keyObj"/> This example uses the name attribute to specify the name of an object whose value will be used as the key for the message. Remember that you can explicitly specify the scope of the object with the scope attribute. The third way to use the message tag is shown here: <bean:message name="keyObj" property="keyField"/> In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used as the key for the message. Again, you can explicitly specify the scope of the object with the scope attribute.
The page tag is used to store one of a JSPs implicit objects in a JSP scripting variable with page scope. By default, a JSPs implicit objects cannot be seen by JSP tag library tags because they are not stored in a scope. Remember that JSPs get converted into a servlet and then compiled into a class file before they are run. JSP implicit objects are simply local variables in a servlets service( ) method. For this reason, JSP tag library tags cannot see the objects. However, JSP tag library tags can access objects that have been stored in a scope, thus the reason for the page tag. The page tag works with the following implicit JSP objects: application config request response session One of the aforementioned objects must be specified with the property attribute or this tag will throw a JspException at run time.
Attributes
Attribute id Description Specifies the name of the JSP variable that will hold the specified JSP implicit object. Specifies the name of the JSP implicit object to store in the JSP variable. Must be application, config, request, response, or session. Accepts Scriptlet No Required Yes
property
Yes
Yes
Example Usage
The following snippet shows a basic usage of the page tag: <bean:page id="cfg" property="config"/> This example simply stores a reference to the implicit config object in a page scope variable named cfg. After you have used the page tag to store a JSP implicit object, you can use it with a JSP tag library tag as shown here: <bean:write name="cfg" property="servletName"/> Remember that the write tag wouldnt be able to see the implicit config object without the use of the page tag.
Attributes
Attribute id Description Specifies the name of the JSP variable that will hold the specified parameter(s). Accepts an arbitrary value to denote that multiple parameters should be stored. If specified, parameters will be stored in a String[ ] array. If not specified and multiple parameters have the same name, the first parameter with the name specified will be stored. Specifies the name of the parameter(s) to store in the JSP variable. Specifies the default value to assign to the String object being placed in the JSP variable if the specified parameter is not present in the incoming request. Accepts Scriptlet No Required Yes
multiple
Yes
No
name
Yes
Yes
value
Yes
No
Example Usage
The following snippet shows a basic usage of the parameter tag: <bean:parameter id="color" name="clr" value="none"/> This example attempts to find a parameter named clr in the incoming request and store a reference to it in a variable named color. If the parameter is not in the request, a new String object will be created and have the value of the value attribute assigned to it. A reference to the String will then be stored. After you have used the parameter tag to store a parameter, you can access it with the following snippet: Color Request Parameter: <%=color%> This snippet simply uses a JSP expression to render the value of the color variable. Additionally, because the parameter tag places the color variable in page scope, it can be accessed by JSP tag library tags, as shown here: Color Request Parameter: <bean:write name="color"/> The following snippet shows an example of using the parameter tag for multiple parameters with the same name: <bean:parameter id="color" name="clr" multiple="true" value="none"/> Remember that the multiple attribute accepts an arbitrary value; thus, you could use true as shown in the example, or any other value. As long as a value is present, a String[ ] array will be used to store the parameters. To access the first parameter stored when using the multiple attribute, use the following snippet: First Color: <%=color[0]%>
The resource tag is used to load the contents of a Web application resource and store them as a String object in a JSP scripting variable with page scope. Additionally, you have the option of specifying whether or not you simply want a java.io.InputStream reference for the resource instead of storing its contents in a String. Web application resources are those files that are packaged as part of a Web application. For example, if your Web application is housed in a directory called c:\java\MiniHR and has the following files c:\java\MiniHR\index.jsp c:\java\MiniHR\WEB-INF\struts-config.xml c:\java\MiniHR\WEB-INF\web.xml then you can access the index.jsp file as /index.jsp. Similarly, you can access the strutsconfig.xml file as /WEB-INF/struts-config.xml. Note that Web application resource paths are relative to the application and must begin with a leading slash (/). A JspException will be thrown by the resource tag at run time if there is a problem loading the resource.
Attributes
Attribute id Description Specifies the name of the JSP variable that will hold the contents of the specified resource. Accepts an arbitrary value to denote that a java.io.InputStream reference for the resource should be stored in the JSP variable instead of storing the contents of the resource in a String. Specifies the path of the Web application resource that will be loaded. The path must begin with a leading slash (/). Accepts Scriptlet No Required Yes
input
Yes
No
name
Yes
Yes
Example Usage
The following snippet shows how you could use the resource tag to load the contents of a struts-config.xml file: <bean:resource id="strutsConfig" name="/WEB-INF/struts-config.xml"/> After you have used the resource tag to load the contents of the Struts configuration file, you can access them with the following snippet: Struts Config file contents: <%=strutsConfig%> This snippet simply uses a JSP expression to render the value of the String object stored in the strutsConfig variable. Additionally, because the resource tag places the strutsConfig variable in page scope, it can be accessed by JSP tag library tags, as shown here: Struts Config file: <bean:write name="strutsConfig"/> The following snippet shows how to use the resource tag to obtain a java.io. InputStream reference for a Web application resource: <bean:resource id="strutsConfig" input="true" name="/WEB-INF/struts-config.xml"/>
Remember that the input attribute accepts arbitrary values; thus, you could use true as shown in the example, or any other value. As long as a value is present, a java.io.InputStream reference will be stored in the JSP variable instead of a String containing the contents of the resource file. As you can imagine, using the resource tag is significantly cleaner and less involved than using a scriptlet to load the contents of a Web application resource.
Attributes
Attribute collection Description Specifies a scriptlet that evaluates to an array, java.util.Collection-based object, or java.util.Mapbased object. Specifies the name of the JSP variable that will hold the size value (java.lang.Integer). Specifies the name of the object that contains the collection whose size will be taken. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the collection whose size will be taken. Accepts Scriptlet Yes Required No
id
No
Yes
name
Yes
No
Attribute property
Description Specifies the field, of the object specified by the name attribute, whose getter method will be called to return the collection whose size will be taken. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application.
Required No
scope
Yes
No
Example Usage
As mentioned, there are three basic ways you can use the size tag. The first way, shown here, uses the collection attribute to specify which collections size to store: <% // Example array. String[] genders = {"male", "female"}; %> <bean:size id="genderCount" collection="<%=genders%>"/> Remember that the collection attribute only accepts a scriptlet as a value. Because the collection attribute was used to specify the collection whose size will be taken, variables that are not in any particular scope can be accessed by the tag by way of a scriptlet. The following two ways of using the size tag require the collection whose size is being taken to be in a scope. The second way to use the size tag is shown here: <% // Example Collection. ArrayList genders = new ArrayList(); genders.add("male"); genders.add("female"); pageContext.setAttribute("genders", genders); %> <bean:size id="genderCount" name="genders"/> In this example, the name attribute is used to specify the name of a JSP scripting variable that has a collection in it. Remember that all scopes will be searched in succession (page then request then session then application) to find the specified object. Optionally, you can use the scope attribute to explicitly tell the tag which scope to look in for the collection. The third and final way to use the size tag is shown here: <bean:size id="genderCount" name="employee" property="genders"/>
This example assumes you have an object named employee with a field called genders. The objects getGenders( ) method will be called to return a collection whose size will be taken. After you have used the size tag to store a collections size, you can access it with the following snippet: Gender Count: <%=genderCount%> This snippet simply uses a JSP expression to render the value of the Integer object stored in the genderCount variable. Additionally, because the size tag places the genderCount variable in page scope, it can be accessed by JSP tag library tags, as shown here: Gender Count: <bean:write name="genderCount"/>
Attributes
Attribute formBean Description Specifies the name of a FormBean whose org.apache.struts.config.FormBeanConfi g object reference will be stored in the specified JSP scripting variable. Specifies the name of a forward whose org.apache.struts.config.ForwardConfig object reference will be stored in the specified JSP scripting variable. Specifies the name of the JSP variable that will hold the reference to the specified Struts configuration object. Specifies the name of an Action whose org.apache.struts.config.ActionConfig object reference will be stored in the specified JSP scripting variable. Accepts Scriptlet Yes Required No
forward
Yes
No
id
No
Yes
mapping
Yes
No
Example Usage
Before you examine the struts tag usage, review the following sample Struts configuration file. Each of the following examples is based on this configuration file. <?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config> <!-- Form Beans Configuration --> <form-beans> <form-bean name="searchForm" type="com.jamesholmes.minihr.SearchForm"/> </form-beans> <!-- Global Forwards Configuration --> <global-forwards> <forward name="search" path="/search.jsp"/> </global-forwards> <!-- Action Mappings Configuration --> <action-mappings> <action path="/search" type="com.jamesholmes.minihr.SearchAction" name="searchForm" scope="request" validate="true" input="/search.jsp"> </action> </action-mappings> <!-- Message Resources Configuration --> <message-resources parameter="com.jamesholmes.minihr.ApplicationResources"/> </struts-config> There are three ways to use the struts tag. The first way, shown here, is used to store a reference to a FormBean configuration object: <bean:struts id="cfg" formBean="searchForm"/> Notice that the value used for the formBean attribute matches the value given to the name attribute of the <form-bean> tag in the Struts configuration file shown earlier. The second way to use the struts tag, shown next, stores a reference to a forward configuration object: <bean:struts id="cfg" forward="search"/> Again, notice in this example that the value used for the forward attribute matches the value given to the name attribute of the <forward> tag in the Struts configuration file shown earlier. The third way to use the struts tag stores a reference to an Action configuration object, as shown here: <bean:struts id="cfg" mapping="/search"/>
Similar to the previous two examples, the value used for the mapping attribute matches the value given to the path attribute of the <action> tag in the Struts configuration file shown earlier. Because the struts tag stores configuration object references in page scope, the objects can be accessed both with scriptlets and JSP tag library tags.
Attributes
Attribute bundle Description Specifies the name of the application scoped org.apache.struts.util.MessageResources object that contains the format key/value pairs for the formatKey attribute.Defaults to the value stored in the org.apache.struts.Globals.MESSAGES_KE Y constant. Accepts true or false to denote whether or not HTML-sensitive characters should be converted to their entity equivalents. For example, if this attribute is enabled, the lessthan sign (<) would get converted to < before it is rendered. Defaults to true. Specifies the format string to use to convert the value being rendered. If not specified, write will try to use the formatKey attribute to look up the format string. Specifies the key for a format string to look up in Struts ApplicationResources.properties file. Accepts true or false to denote whether or not to skip throwing a JspException, similar to the other tags in this library, when the object specified by the name attribute is not found. Defaults to false. Specifies the name of the session scoped java.util.Locale object to use when formatting values with the format attribute. Defaults to the value stored in the org.apache.struts Accepts Scriptlet Yes Required No
filter
Yes
No
format
Yes
No
formatKey
Yes
No
ignore
Yes
No
locale
Yes
No
Attribute
Required
name
Specifies the name of the object that will be rendered. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the object that will be rendered. Specifies the field, of the object specified by the name attribute, whose getter method will be called to return the object that will be rendered. Specifies the scope (application, page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application.
Yes
property
Yes
No
scope
Yes
No
Example Usage
As mentioned, there are two ways you can use the write tag. The first way, shown here, uses the name attribute to specify the name of an object to be rendered. Remember that you can explicitly specify the scope of the object with the scope attribute. <bean:write name="bizObj"/> The following is the second way to use the write tag: <bean:write name="bizObj" property="bizField"/> In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object to be rendered. Again, you can explicitly specify the scope of the object with the scope attribute.
If the object specified by the name attribute is equal to 0, as specified by the value attribute, then the content between the starting and ending equal tags will be processed. Otherwise, the content will be ignored. Note that the Logic Library has two tags that are not used for conditional logic: the iterate tag and the redirect tag. The iterate tag is used to wrap content that should be processed for each element of a collection, and the redirect tag is used to send an HTTP redirect response to a browser, causing it to load another page.
Scope
Description service( ) method completes. Included JSPs cannot see page scope variables from the page including them. Also, this scope is exclusive to JSPs.
request
Variables placed in this scope persist until processing for the current request is completed. This scope differs from page scope because multiple servlets may be executed during the lifespan of a request. Page scope variables persist only for the execution of one servlet. Variables placed in this scope persist until the current users session is invalidated or expires. This scope is valid only if the JSP or servlet in question is participating in a session.
session
Note that variables must be explicitly placed into a scope, as shown here: <% request.setAttribute("reqScopeVar", "test"); %> This snippet uses JSPs implicit request object to place a variable into request scope. Similarly, the following snippet uses JSPs implicit session object to place a variable into session scope: <% session.setAttribute("sesScopeVar", "test"); %> Of course, variables can also be put into each of the scopes by JSP tag library tags as many of the tags in the Logic Tag Library do.
Because logic was defined as the prefix, the present tag was used as shown. However, if you chose to use a prefix of strutslgc, the tag would be used the following way: <strutslgc:present name="searchForm" property="results">
equal
forward greaterEqual
greaterThan
iterate lessEqual
lessThan
match
messagesNotPresent
messagesPresent
Tag notEmpty
Description Wraps content that is conditionally processed based on whether or not the specified object is non-null and does not contain an empty (zero length) String. Wraps content that is conditionally processed based on whether or not a specified objects value is not equal to a specified constant value. Wraps content that is conditionally processed based on whether or not a specified objects value does not contain, start with, or end with a specified constant value. Wraps content that is conditionally processed based on whether or not a specified object does not exist. Wraps content that is conditionally processed based on whether or not a specified object exists. Composes a URL to another page and then redirects to it.
notEqual
notMatch
The remainder of this chapter discusses each tag in detail, including a complete description of the tag, a table listing each of the tags attributes, and a usage example for the tag. In the tables that describe each tags attributes, pay special attention to the Required and Accepts Scriptlet columns. The Required column simply denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the tag will throw a javax.servlet.jsp.JspException at run time. Note that you can declare an error page in your JSP with a page directive to capture any JspExceptions that might be thrown, as shown here:
<%@ page errorPage="error.jsp" %>
If an exception occurs, the page specified by the errorPage attribute will be internally redirected to display an error page. The Accepts Scriptlet column denotes whether or not the given attributes value can be specified with a JSP scriptlet. If a JSP scriptlet is used to specify an attribute value, the scriptlet must comprise the complete value, quote () to quote (), as shown here. Correct:
<logic:present name="<%=result%>">
Incorrect:
<logic:present name="<%=result%>-result">
Notice in the incorrect example that -result is used as part of the value for the name attribute following the scriptlet. This is invalid because there are extra characters between the end of the scriptlet and the ending quote.
The concatenation of -result is now part of the scriptlet and the scriptlet comprises the complete value for the attribute.
property
Specifies the field of the object specified by Yes the name attribute whose getter method will be called to return an object to check as being empty. Specifies the scope (application, page, request, Yes or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application.
scope
No
Example Usage There are two ways you can use the empty tag. The first way, shown here, uses the name attribute to specify the name of an object to check as being empty:
<logic:empty name="results"> Your search yielded no results. </logic:empty>
If the results object is null, contains a zero-length string, or contains a Collection or Map with zero elements, then the content between the starting and ending empty tags will be processed. Remember that you can explicitly specify the scope of the object with the scope attribute.
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object to check as being empty. Again, you can explicitly specify the scope of the object with the scope attribute.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used for the comparison. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used for the comparison. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used for the comparison. You can use the name attribute to specify the name of an object whose value will be used for the comparison. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used for the comparison. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Attributes Attribute cookie Accepts Required Scriptlet Specifies the name of the HTTP cookie Yes No from the incoming request whose value is used for comparison. Specifies the name of the HTTP header Yes from the incoming request whose value is used for comparison. Specifies the name of an object whose Yes value is used for comparison. If the property attribute is also specified, one of the fields of the object defined by this No Description
header
name
No
Attribute
Description attribute will have its getter method called to return an object whose value is used for comparison.
parameter
Specifies the name of the parameter Yes from the incoming request whose value is used for comparison. Specifies the field of the object specified Yes by the name attribute whose getter method will be called to return an object whose value is used for comparison. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which the Yes value, specified by other attributes of this tag, will be compared.
No
property
No
scope
No
value
Yes
Example Usage As mentioned, there are five ways you can use the equal tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is compared against the value specified with the value attribute:
<logic:equal cookie="role" value="Manager"> User is a Manager. </logic:equal>
If the cookies value and the value attributes value are equal, the content between the starting and ending equal tags will be processed. Otherwise it will be ignored. The second way to use the equal tag is shown here:
<logic:equal header="User-Agent" value="MSIE"> Browser is Internet Explorer. </logic:equal>
This example compares the incoming requests User-Agent header against the value attribute to determine whether or not they are equal. If so, the content between the starting and ending equal tags will be processed. The following is the third way to use the equal tag:
<logic:equal parameter="catId" value="10">
This example compares a request parameter from the incoming request against the value of the value attribute. If the two values are equal, the content between the starting and ending equal tags will be processed. The fourth way to use the equal tag is shown here:
<logic:equal name="resultCount" value="0"> Search returned no results. </logic:equal>
This example compares the value of the resultCount object against the value of the value attribute. If the two values are equal, the content between the starting and ending equal tags will be processed. Remember that you can explicitly specify the scope of the resultCount object with the scope attribute. The following is the fifth and final way to use the equal tag:
<logic:equal name="employee" property="name" value="Bob"> Hello, Bob! </logic:equal>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be compared against that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
A redirect sends a response to the users browser instructing it to load another page. A forward, on the other hand, simply forwards control (on the server side) to another URL without having the browser make another request. Note that only URLs residing on the same server can be forwarded to, whereas any URL can be redirected to. Attribute Attribute name Description Accepts Scriptlet Specifies the name of the forward to Yes redirect to. Required Yes
Example Usage The following snippet shows how to use the forward tag:
<logic:forward name="search"/>
As you can see, this tag is very straightforward. All you have to do is specify the name of the forward to redirect to.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used for the comparison. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used for the comparison. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used for the comparison. You can use the name attribute to specify the name of an object whose value will be used for the comparison. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used for the comparison. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Attributes Attribute cookie Accepts Scriptlet Specifies the name of the HTTP Yes cookie from the incoming request whose value is used for comparison. Specifies the name of the HTTP Yes header from the incoming request whose value is used for comparison. Specifies the name of an object whose Yes value is used for comparison. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object Description Required No
header
No
name
No
Attribute
Accepts Scriptlet
Required
parameter
Specifies the name of the parameter Yes from the incoming request whose value is used for comparison. Specifies the field of the object Yes specified by the name attribute whose getter method will be called to return an object whose value is used for comparison. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which Yes the value, specified by other attributes of this tag, will be compared.
No
property
No
scope
No
value
Yes
Example Usage As mentioned, there are five ways you can use the greaterEqual tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is compared against the value specified with the value attribute:
<logic:greaterEqual cookie="size" value="0"> Size is valid. </logic:greaterEqual>
If the cookies value is greater than or equal to the value attributes value, the content between the starting and ending greaterEqual tags will be processed. Otherwise it will be ignored. The following is the second way to use the greaterEqual tag:
<logic:greaterEqual header="Content-Length" value="100000"> Page has large amount of content. </logic:greaterEqual>
This example compares the incoming requests User-Agent header against the value attribute to determine whether or not it is greater than or equal to it. If so, the content between the starting and ending greaterEqual tags will be processed. The third way to use the greaterEqual tag is shown here:
<logic:greaterEqual parameter="catId" value="10">
This example compares a request parameter from the incoming request against the value of the value attribute. If the parameter is greater than or equal to the value, the content between the starting and ending greaterEqual tags will be processed. The fourth way to use the greaterEqual tag is shown here:
<logic:greaterEqual name="resultCount" value="1"> Search returned at least 1 result. </logic:greaterEqual>
This example compares the value of the resultCount object against the value of the value attribute. If the resultCount objects value is greater than or equal to the value of the value attribute, the content between the starting and ending greaterEqual tags will be processed. Remember that you can explicitly specify the scope of the resultCount object with the scope attribute. The following is the fifth and final way to use the greaterEqual tag:
<logic:greaterEqual name="employee" property="age" value="21"> Employee is old enough to drink. </logic:greaterEqual>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be compared against that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used for the comparison. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used for the comparison. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used for the comparison. You can use the name attribute to specify the name of an object whose value will be used for the comparison. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used for the comparison. Again, the object specified by
the name attribute can be in any scope or limited to a specific scope with the scope attribute. Attributes Attribute cookie Description Accepts Scriptlet Specifies the name of the HTTP cookie Yes from the incoming request whose value is used for comparison. Specifies the name of the HTTP header Yes from the incoming request whose value is used for comparison. Specifies the name of an object whose Yes value is used for comparison. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value is used for comparison. Specifies the name of the parameter Yes from the incoming request whose value is used for comparison. Specifies the field of the object Yes specified by the name attribute whose getter method will be called to return an object whose value is used for comparison. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which Yes the value, specified by other attributes of this tag, will be compared. Required No
header
No
name
No
parameter
No
property
No
scope
No
value
Yes
Example Usage As mentioned, there are five ways you can use the greaterThan tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is compared against the value specified with the value attribute:
<logic:greaterThan cookie="size" value="0"> Size is valid. </logic:greaterThan>
If the cookies value is greater than the value attributes value, the content between the starting and ending greaterThan tags will be processed. Otherwise it will be ignored. The second way to use the greaterThan tag is shown here:
<logic:greaterThan header="Content-Length" value="100000"> Page has large amount of content. </logic:greaterThan>
This example compares the incoming requests User-Agent header against the value attribute to determine whether or not it is greater than it. If so, the content between the starting and ending greaterThan tags will be processed. The following is the third way to use the greaterThan tag:
<logic:greaterThan parameter="catId" value="10"> Category Id is greater than 10. </logic:greaterThan>
This example compares a request parameter from the incoming request against the value of the value attribute. If the parameter is greater than the value, the content between the starting and ending greaterThan tags will be processed. The fourth way to use the greaterThan tag is shown here:
<logic:greaterThan name="resultCount" value="1"> Search returned at least 1 result. </logic:greaterThan>
This example compares the value of the resultCount object against the value of the value attribute. If the resultCount objects value is greater than the value of the value attribute, the content between the starting and ending greaterThan tags will be processed. Remember that you can explicitly specify the scope of the resultCount object with the scope attribute. The following is the fifth and final way to use the greaterThan tag:
<logic:greaterThan name="employee" property="age" value="21"> Employee is old enough to drink. </logic:greaterThan>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be compared against that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
An array of Java objects or primitives (i.e., int, long, and so on). An implementation of java.util.Collection. An implementation of java.util.Enumeration. An implementation of java.util.Iterator. An implementation of java.util.Map.
Additionally, this tag sets two JSP variables on each iteration through the specified collections elements. The first variable, whose name is specified by the id attribute, stores a reference to the current element of the collection. The second variable, whose name is specified by the indexId attribute, stores the numeric index for the current element. Note that each element will be stored as type Object unless specified otherwise with use of the type attribute. Also note that java.util.Map-based collections house java.util.Map.Entry elements, which contain the key and value for the given element. There are three ways you can specify which collection to iterate over, as listed here and shown later in the Example Usage section:
You can use the collection attribute to specify a scriptlet that evaluates to a collection object. You can use the name attribute to specify the name of a collection object that can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return a collection object. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Note that if none of the aforementioned ways is used to specify the collection that should be iterated over, a JspException will be thrown by this tag at run time. Also note that if the collection you are iterating over can contain null values, the loop will still be performed but no page scope attribute will be stored for elements that are null. Attributes Attribute collection Description Accepts Scriptlet Specifies a scriptlet that evaluates to Yes an array, java.util.Collection, java.util.Enumeration, java.util.Iterator, or java.util.Map. Required No
id
Specifies the name for the page No scoped JSP variable that will hold a reference to the current element of the collection on each iteration. Specifies the name for the page No scoped JSP variable that will hold the
Yes
indexId
No
Attribute
Accepts Scriptlet
Required
length
Specifies the maximum number of Yes entries from the collection that will be iterated over. If not specified, all entries in the collection will be iterated over.This attribute can specify an integer value or the name of an Integer object (in any scope). Specifies the name of the object that Yes contains the collection that will be iterated over. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the collection that will be iterated over. Specifies the zero-relative index to Yes start the collection iteration at. If not specified, iteration will start at 0 (the beginning). This attribute can specify an integer value or the name of an Integer object (in any scope). Specifies the field of the object Yes specified by the name attribute whose getter method will be called to return the collection that will be iterated over. Specifies the scope (application, Yes page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the fully qualified class type Yes (e.g., java.lang.Integer) to expose each collection element as, with the id attribute. A ClassCastException will be thrown at run time if any of the collections elements are not assignment compatible with this type. If not specified, no type conversions will be performed and each element will be exposed as type Object.
No
name
No
offset
No
property
No
scope
No
type
No
Example Usage As mentioned, there are three basic ways you can use the iterate tag. The first way, shown here, uses the collection attribute to specify the collection to iterate over:
<logic:iterate id="result" collection="<%=results%>"> Result: <%=result%><br> </logic:iterate>
This example assumes you have defined a collection named results and then iterates over each result, printing it. Notice that the collection attribute was specified with a scriptlet. This attribute only accepts scriptlets. Anything else will result in a JspException being thrown at run time. The second way to use the iterate tag is shown here:
<logic:iterate id="result" name="results"> Result: <bean:write name="result"/><br> </logic:iterate>
This example is similar to the first example; however, it differs in that the collection to be iterated over is specified with the name attribute. Remember that you can explicitly specify the scope of the object, defined by the name attribute, with the scope attribute. The following is the third and final way to use the iterate tag:
<logic:iterate id="result" name="searchObj" property="results"> Result: <bean:write name="result"/><br> </logic:iterate>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return a collection to be iterated over. Again, you can explicitly specify the scope of the object with the scope attribute.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used for the comparison. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used for the comparison. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used for the comparison.
You can use the name attribute to specify the name of an object whose value will be used for the comparison. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used for the comparison. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Attributes Attribute cookie Accepts Scriptlet Specifies the name of the HTTP Yes cookie from the incoming request whose value is used for comparison. Specifies the name of the HTTP Yes header from the incoming request whose value is used for comparison. Specifies the name of an object whose Yes value is used for comparison. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value is used for comparison. Specifies the name of the parameter Yes from the incoming request whose value is used for comparison. Specifies the field of the object Yes specified by the name attribute whose getter method will be called to return an object whose value is used for comparison. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which Yes the value, specified by other attributes of this tag, will be compared. Description Required No
header
No
name
No
parameter
No
property
No
scope
No
value
Yes
Example Usage
As mentioned, there are five ways you can use the lessEqual tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is compared against the value specified with the value attribute:
<logic:lessEqual cookie="size" value="0"> Size is invalid. </logic:lessEqual>
If the cookies value is less than or equal to the value attributes value, the content between the starting and ending lessEqual tags will be processed. Otherwise it will be ignored. The following is the second way to use the lessEqual tag:
<logic:lessEqual header="Content-Length" value="500"> Page has small amount of content. </logic:lessEqual>
This example compares the incoming requests User-Agent header against the value attribute to determine whether or not it is less than or equal to it. If so, the content between the starting and ending lessEqual tags will be processed. The third way to use the lessEqual tag is shown here:
<logic:lessEqual parameter="catId" value="10"> Category Id is less than or equal to 10. </logic:lessEqual>
This example compares a request parameter from the incoming request against the value of the value attribute. If the parameter is less than or equal to the value, the content between the starting and ending lessEqual tags will be processed. The fourth way to use the lessEqual tag is shown here:
<logic:lessEqual name="resultCount" value="20"> Search returned less than 20 results. </logic:lessEqual>
This example compares the value of the resultCount object against the value of the value attribute. If the resultCount objects value is less than or equal to the value of the value attribute, the content between the starting and ending lessEqual tags will be processed. Remember that you can explicitly specify the scope of the resultCount object with the scope attribute. The following is the fifth and final way to use the lessEqual tag:
<logic:lessEqual name="employee" property="age" value="20"> Employee is not old enough to drink. </logic:lessEqual>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object
whose value will be compared against that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used for the comparison. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used for the comparison. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used for the comparison. You can use the name attribute to specify the name of an object whose value will be used for the comparison. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used for the comparison. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Attributes Attribute cookie Accepts Scriptlet Specifies the name of the HTTP Yes cookie from the incoming request whose value is used for comparison. Specifies the name of the HTTP Yes header from the incoming request whose value is used for comparison. Specifies the name of an object Yes whose value is used for comparison. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value is used for comparison. Specifies the name of the parameter Yes from the incoming request whose value is used for comparison. Description Required No
header
No
name
No
parameter
No
Attribute property
Description
Accepts Scriptlet
Required No
Specifies the field of the object Yes specified by the name attribute whose getter method will be called to return an object whose value is used for comparison. Specifies the scope (application, Yes page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which Yes the value, specified by other attributes of this tag, will be compared.
scope
No
value
Yes
Example Usage As mentioned, there are five ways you can use the lessThan tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is compared against the value specified with the value attribute:
<logic:lessThan cookie="size" value="1"> Size is invalid. </logic:lessThan>
If the cookies value is less than the value attributes value, the content between the starting and ending lessThan tags will be processed. Otherwise it will be ignored. The second way to use the lessThan tag is shown here:
<logic:lessThan header="Content-Length" value="500"> Page has small amount of content. </logic:lessThan>
This example compares the incoming requests User-Agent header against the value attribute to determine whether or not it is less than it. If so, the content between the starting and ending lessThan tags will be processed. The following is the third way to use the lessThan tag:
<logic:lessThan parameter="catId" value="10"> Category Id is less than 10. </logic:lessThan>
This example compares a request parameter from the incoming request against the value of the value attribute. If the parameter is less than the value, the content between the starting and ending lessThan tags will be processed. The following is the fourth way to use the lessThan tag:
<logic:lessThan name="resultCount" value="1"> Search returned no results. </logic:lessThan>
This example compares the value of the resultCount object against the value of the value attribute. If the resultCount objects value is less than the value of the value attribute, the content between the starting and ending lessThan tags will be processed. Remember that you can explicitly specify the scope of the resultCount object with the scope attribute. The fifth and final way to use the lessThan tag is shown here:
<logic:lessThan name="employee" property="age" value="21"> Employee is not old enough to drink. </logic:lessThan>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be compared against that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used to match against. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used to match against. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used to match against.
You can use the name attribute to specify the name of an object whose value will be used to match against. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used to match against. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Attributes Attribute cookie Description Accepts Scriptlet Specifies the name of the HTTP cookie Yes from the incoming request whose value is matched against. Specifies the name of the HTTP header Yes from the incoming request whose value is matched against. Accepts start or end to specify where Yes the match should occur. If not specified, the match can occur at any location. Specifies the name of an object whose Yes value is matched against. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value is matched against. Specifies the name of the parameter from Yes the incoming request whose value is matched against. Specifies the field of the object specified Yes by the name attribute whose getter method will be called to return an object whose value is matched against. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which the Yes value, specified by other attributes of this tag, should contain, start with, or end with. Required No
header
No
location
No
name
No
parameter
No
property
No
scope
No
value
Yes
Example Usage As mentioned, there are five ways you can use the match tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is used to match the value specified with the value attribute:
<logic:match cookie="roles" value="Manager"> User is a Manager. </logic:match>
If the cookies value contains the value specified by the value attribute, the content between the starting and ending match tags will be processed. Otherwise it will be ignored. The second way to use the match tag is shown here:
<logic:match header="User-Agent" location="end" value="MSIE"> Browser is Internet Explorer. </logic:match>
This example checks to see if the incoming requests User-Agent header ends with the value specified by the value attribute. If so, the content between the starting and ending match tags will be processed. The third way to use the match tag is shown here:
<logic:match parameter="category" location="start" value="Cloth"> Category is Clothes. </logic:match>
This example checks to see if the parameter from the incoming request starts with the value of the value attribute. If so, the content between the starting and ending match tags will be processed. The following is the fourth way to use the match tag:
<logic:match name="lastName" location="start" value="H"> Last name starts with "H". </logic:match>
This example checks to see if the value of the lastName object starts with the value of the value attribute. If so, the content between the starting and ending match tags will be processed. Remember that you can explicitly specify the scope of the lastName object with the scope attribute. The fifth and final way to use the match tag is shown here:
<logic:match name="employee" property="lastName" location="start" value="H"> Last name starts with "H".
</logic:match>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used to match that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
name
No
property
No
Example Usage The following example illustrates the basic usage of the messagesNotPresent tag:
<logic:messagesNotPresent> No errors are present. </logic:messagesNotPresent>
This example simply checks if no errors are present. The following example checks if no errors are present for a specific property named username:
<logic:messagesNotPresent property="username"> No errors are present for username property. </logic:messagesNotPresent>
Accepts a value of true to denote that the Yes presence of messages is checked for. If not specified, the presence of errors will be checked for. If this attribute it set to true, the name attribute will be ignored. Specifies the key to use to look up an Yes org.apache.struts.action.ActionErrors or org.apache.struts.action.ActionMessages object that will be checked for the presence of errors or messages, respectively. Specifies the name of a particular property Yes to check for the presence of errors or messages. If not specified, the presence of any messages or errors will be checked for.
name
No
property
No
Example Usage The following example illustrates the basic usage of the messagesPresent tag:
<logic:messagesPresent> Errors are present. </logic:messagesPresent>
This example simply checks if any errors are present. The following example checks if there are any errors present for a specific property named username:
<logic:messagesPresent property="username"> Errors are present for username property. </logic:messagesPresent>
object is not empty. If the object is not empty, then the wrapped content will be processed. Attributes Attribute name Description Accepts Scriptlet Specifies the name of an object to check Yes as being not empty. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object to check as being not empty. Required No
property
Specifies the field of the object specified Yes by the name attribute whose getter method will be called to return an object to check as being not empty. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application.
No
scope
No
Example Usage There are two ways you can use the notEmpty tag. The first way, shown here, uses the name attribute to specify the name of an object to check as being not empty:
<logic:notEmpty name="results"> Your search returned results! </logic:notEmpty>
If the results object is non-null, does not contain a zero-length string, and does not contain a Collection or Map with zero elements, then the content between the starting and ending empty tags will be processed. Remember that you can explicitly specify the scope of the object with the scope attribute. The second way to use the notEmpty tag is shown here:
<logic:notEmpty name="bizObj" property="results"> Your search returned results! </logic:empty>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object to check as being not empty. Again, you can explicitly specify the scope of the object with the scope attribute.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used for the comparison. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used for the comparison. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used for the comparison. You can use the name attribute to specify the name of an object whose value will be used for the comparison. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used for the comparison. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Attributes Attribute cookie Description AcceptsScriptlet Required Specifies the name of the HTTP Yes No cookie from the incoming request whose value is used for comparison. Specifies the name of the HTTP Yes header from the incoming request whose value is used for comparison. Specifies the name of an object whose Yes value is used for comparison. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value is used for comparison. Specifies the name of the parameter Yes from the incoming request whose value is used for comparison. Specifies the field of the object Yes specified by the name attribute whose getter method will be called to return an object whose value is used for comparison. No
header
name
No
parameter
No
property
No
Attribute scope
Description AcceptsScriptlet Required Specifies the scope (application, page, Yes No request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which Yes the value, specified by other attributes of this tag, will be compared. Yes
value
Example Usage As mentioned, there are five ways you can use the notEqual tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is compared against the value specified with the value attribute:
<logic:notEqual cookie="role" value="Manager"> User is NOT a Manager. </logic:notEqual>
If the cookies value and the value attributes value are not equal, the content between the starting and ending notEqual tags will be processed. Otherwise it will be ignored. The following is the second way to use the notEqual tag:
<logic:notEqual header="User-Agent" value="MSIE"> Browser is NOT Internet Explorer. </logic:notEqual>
This example compares the incoming requests User-Agent header against the value attribute to determine whether or not they are equal. If they are not equal, the content between the starting and ending notEqual tags will be processed. The third way to use the notEqual tag is shown here:
<logic:notEqual parameter="catId" value="10"> Category Id is NOT 10. </logic:notEqual>
This example compares a request parameter from the incoming request against the value of the value attribute. If the two values are not equal, the content between the starting and ending notEqual tags will be processed. The following is the fourth way to use the notEqual tag:
<logic:notEqual name="resultCount" value="0"> Search returned results. </logic:notEqual>
This example compares the value of the resultCount object against the value of the value attribute. If the two values are not equal, the content between the starting and ending notEqual tags will be processed. Remember that you can explicitly specify the scope of the resultCount object with the scope attribute. The fifth and final way to use the notEqual tag is shown here:
<logic:notEqual name="employee" property="gender" value="male"> Employee is a female. </logic:notEqual>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be compared against that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value will be used to match against. You can use the header attribute to specify the name of an HTTP header from the incoming request whose value will be used to match against. You can use the parameter attribute to specify the name of a parameter from the incoming request whose value will be used to match against. You can use the name attribute to specify the name of an object whose value will be used to match against. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used to match against. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute.
Attributes
Attribute cookie
Description
Accepts Scriptlet
Required No
Specifies the name of the HTTP Yes cookie from the incoming request whose value is matched against. Specifies the name of the HTTP Yes header from the incoming request whose value is matched against. Accepts start or end to specify Yes where the match should occur. If not specified, the match can occur at any location. Specifies the name of an object Yes whose value is matched against. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value is matched against. Specifies the name of the parameter Yes from the incoming request whose value is matched against. Specifies the field of the object Yes specified by the name attribute whose getter method will be called to return an object whose value is matched against. Specifies the scope (application, Yes page, request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the constant value to which Yes the value, specified by other attributes of this tag, should contain, start with, or end with.
header
No
location
No
name
No
parameter
No
property
No
scope
No
value
Yes
Example Usage As mentioned, there are five ways you can use the notMatch tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose value is used to match the value specified with the value attribute:
<logic:notMatch cookie="roles" value="Manager"> User is not a Manager.
</logic:notMatch>
If the cookies value does not contain the value specified by the value attribute, the content between the starting and ending notMatch tags will be processed. Otherwise it will be ignored. The second way to use the notMatch tag is shown here:
<logic:notMatch header="User-Agent" location="end" value="MSIE"> Browser is not Internet Explorer. </logic:notMatch>
This example checks to see if the incoming requests User-Agent header does not end with the value specified by the value attribute. If so, the content between the starting and ending notMatch tags will be processed. The following is the third way to use the notMatch tag:
<logic:notMatch parameter="category" location="start" value="Cloth"> Category is not Clothes. </logic:notMatch>
This example checks to see if the parameter from the incoming request does not start with the value of the value attribute. If so, the content between the starting and ending notMatch tags will be processed. The following is the fourth way to use the notMatch tag:
<logic:notMatch name="lastName" location="start" value="H"> Last name does not start with "H". </logic:notMatch>
This example checks to see if the value of the lastName object does not start with the value of the value attribute. If so, the content between the starting and ending notMatch tags will be processed. Remember that you can explicitly specify the scope of the lastName object with the scope attribute. The fifth and final way to use the notMatch tag is shown here:
<logic:notMatch name="employee" property="lastName" location="start" value="H"> Last name starts with "H". </logic:notMatch>
In this example, the name and property attributes are used in tandem to specify the name of an object and its field whose getter method will be called to return an object whose value will be used to match that of the value attribute. Again, you can explicitly specify the scope of the object with the scope attribute.
The notPresent tag wraps content that is conditionally processed based on whether or not a specified object does not exist. That is, this tag looks up the specified object, and if it is not found, the wrapped content will be processed. If the specified object is found, the content will be skipped. In addition to the standard existence checks this tag offers, it provides a mechanism for interacting with the J2EE security system to determine if there is an authenticated user with a given name or role. For more information on security in Struts applications, refer to Chapter 19. There are seven ways you can use the notPresent tag, as listed here and shown later in the Example Usage section:
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose existence will be checked for. You can use the header attribute to specify the name of an HTTP header from the incoming request whose existence will be checked for. You can use the parameter attribute to specify the name of a parameter from the incoming request whose existence will be checked for. You can use the name attribute to specify the name of an object whose existence will be checked for. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and one of its fields whose existence will be checked for. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute. You can use the role attribute to specify a list of roles to check the currently authenticated user against. If the current user is in one of the roles, the existence check passes. You can use the user attribute to specify a name to compare against the currently authenticated user. If the name matches the user, the existence check passes.
Attributes Attribute cookie Accepts Required Scriptlet Specifies the name of the HTTP cookie from Yes No the incoming request whose existence is checked for. Specifies the name of the HTTP header from Yes the incoming request whose existence is checked for. Specifies the name of an object whose Yes existence is checked for. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its existence checked for. No Description
header
name
No
Attribute parameter
Description
Specifies the name of the parameter from the Yes incoming request whose existence is checked for. Specifies the field of the object specified by Yes the name attribute whose existence is checked for. Specifies the list of roles to check the Yes currently authenticated user against. Multiple roles are delimited by commas (i.e., role1,role2,role3). Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies a name to compare against the Yes currently authenticated user.
property
No
role
No
scope
No
user
No
Example Usage As mentioned, there are seven ways you can use the notPresent tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose existence is checked for:
<logic:notPresent cookie="role"> No role cookie exists. </logic:notPresent>
If the incoming request does not have a cookie named role in it, the content between the starting and ending notPresent tags will be processed. Otherwise it will be ignored. The second way to use the notPresent tag is shown here:
<logic:notPresent header="Host"> Host header was not specified. </logic:notPresent>
This example checks the incoming request for a header named Host. If the header does not exist, the content between the starting and ending notPresent tags will be processed. The following is the third way to use the notPresent tag:
<logic:notPresent parameter="catId"> Category Id was not specified.
</logic:notPresent>
This example checks the incoming request for a parameter name catId. If the parameter does not exist, the content between the starting and ending notPresent tags will be processed. The fourth way to use the notPresent tag is shown here:
<logic:notPresent name="results"> Results object exists. </logic:notPresent>
This example looks up an object named results to see if it exists. If it does not, the content between the starting and ending notPresent tags will be processed. Remember that you can explicitly specify the scope of the results object with the scope attribute. The following is the fifth way to use the notPresent tag:
<logic:notPresent name="employee" property="name"> Employee object does not have a name field. </logic:notPresent>
In this example, the name and property attributes are used in tandem to specify the name of an object and one of its fields that is checked for existence. Again, you can explicitly specify the scope of the object with the scope attribute. The following is the sixth way to use the notPresent tag:
<logic:notPresent role="manager"> James is not part of the manager group. </logic:notPresent>
This example checks if the currently authenticated user is associated with the manager role. If not, the content between the starting and ending notPresent tags will be processed. The seventh and final way to use the notPresent tag is shown here:
<logic:notPresent user="jholmes"> James Holmes is not currently logged in. </logic:notPresent>
This example checks if there is currently an authenticated user and if that users name is jholmes. If not, the content between the starting and ending notPresent tags will be processed. Otherwise the content will be skipped.
found (regardless of its value), the wrapped content will be processed. If the specified object is not found, the content will be skipped. In addition to the standard existence checks this tag offers, it provides a mechanism for interacting with the J2EE security system to determine if there is an authenticated user with a given name or role. For more information on security in Struts applications, refer to Chapter 19. There are seven ways you can use the present tag, as listed here and shown later in the Example Usage section:
You can use the cookie attribute to specify the name of an HTTP cookie from the incoming request whose existence will be checked for. You can use the header attribute to specify the name of an HTTP header from the incoming request whose existence will be checked for. You can use the parameter attribute to specify the name of a parameter from the incoming request whose existence will be checked for. You can use the name attribute to specify the name of an object whose existence will be checked for. This object can be in any scope or limited to a specific scope with the scope attribute. You can use the name and property attributes in tandem to specify the name of an object and one of its fields whose existence will be checked for. Again, the object specified by the name attribute can be in any scope or limited to a specific scope with the scope attribute. You can use the role attribute to specify a list of roles to check the currently authenticated user against. If the current user is in one of the roles, the existence check passes. You can use the user attribute to specify a name to compare against the currently authenticated user. If the name matches the user, the existence check passes.
Attributes Attribute cookie Description Accepts Required Scriptlet Specifies the name of the HTTP Yes No cookie from the incoming request whose existence is checked for. Specifies the name of the HTTP Yes header from the incoming request whose existence is checked for. Specifies the name of an object whose Yes existence is checked for. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its existence checked for. Specifies the name of the parameter Yes No
header
name
No
parameter
No
Attribute
property
Specifies the field of the object Yes specified by the name attribute whose existence is checked for. Specifies the list of roles to check the Yes currently authenticated user against. Multiple roles are delimited by commas (i.e., role1,role2,role3). Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies a name to compare against Yes the currently authenticated user.
No
role
No
scope
No
user
No
Example Usage As mentioned, there are seven ways you can use the present tag. The first way, shown here, uses the cookie attribute to specify the name of an HTTP cookie from the incoming request whose existence is checked for:
<logic:present cookie="role"> Role cookie exists. </logic:present>
If the incoming request has a cookie named role in it, the content between the starting and ending present tags will be processed. Otherwise it will be ignored. The following is the second way to use the present tag:
<logic:present header="Host"> Host header was specified. </logic:present>
This example checks the incoming request for a header named Host. If the header exists, the content between the starting and ending present tags will be processed. The third way to use the present tag is shown here:
<logic:present parameter="catId"> Category Id was specified. </logic:present>
This example checks the incoming request for a parameter name catId. If the parameter exists, the content between the starting and ending present tags will be processed. The following is the fourth way to use the present tag:
<logic:present name="results"> Results object exists. </logic:present>
This example looks up an object named results to see if it exists. If it does, the content between the starting and ending present tags will be processed. Remember that you can explicitly specify the scope of the results object with the scope attribute. The fifth way to use the present tag is shown here:
<logic:present name="employee" property="name"> Employee object has a name field. </logic:present>
In this example, the name and property attributes are used in tandem to specify the name of an object and one of its fields that is checked for existence. Again, you can explicitly specify the scope of the object with the scope attribute. The sixth way to use the present tag is shown here:
<logic:present role="manager"> James is part of the manager group. </logic:present>
This example checks if the currently authenticated user is associated with the manager role. If so, the content between the starting and ending present tags will be processed. The seventh and final way to use the present tag is shown here:
<logic:present user="jholmes"> James Holmes is currently logged in. </logic:present>
This example checks if there is currently an authenticated user and if that users name is jholmes. If so, the content between the starting and ending present tags will be processed. Otherwise the content will be skipped.
You can use the action attribute to specify the name of an action from the Struts configuration file whose URL will be used. You can use the forward attribute to specify the name of a forward from the Struts configuration file whose URL will be used. You can use the href attribute to specify an absolute URL, including protocol (e.g., http://www.yahoo.com/). You can use the page attribute to specify an application-relative URL.
In addition to specifying the base URL, you have two options for specifying query string parameters to add to the base URL:
You can use the paramId attribute in conjunction with the paramName attribute, and optionally the paramProperty attribute, to specify a single parameter. You can use the name attribute, either alone or in tandem with the property attribute, to specify a java.util.Map object that will be used to add several parameters.
Attributes Attribute action Description Accepts Required Scriptlet Specifies the name of an action from the Yes No Action Mappings Configuration section of the Struts configuration file, which contains the URL that will be used as the base of the URL generated by this tag. Specifies the anchor (e.g., #bottom) to Yes be added to the URL generated by this tag. This value should be specified without the leading hash (#) character. Specifies the name of a forward, from the Yes Global Forwards Configuration section of the Struts configuration file, which contains the URL that will be used as the base of the URL generated by this tag. Specifies the absolute URL (including Yes protocol, such as http://) that will be used as the base of the URL generated by this tag. Specifies the name of the java.util.Map Yes object whose elements are added as query string parameters to the URL generated by this tag. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return the java.util.Map object whose elements are No
anchor
forward
No
href
No
name
No
Attribute
Description added as query string parameters to the URL generated by this tag.
page
Specifies the application-relative URL Yes (starts with a leading slash, /) that will be used as the base of the URL generated by this tag. Specifies the name of a single parameter Yes to add to the URL generated by this tag. Specifies the name of an object whose Yes value will be used as the value for the parameter specified with the paramId attribute. If the property attribute is also specified, one of the fields of the object defined by this attribute will have its getter method called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the field of the object specified Yes by the name attribute whose getter method will be called to return an object whose value will be used as the value for the parameter specified with the paramId attribute. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the paramName attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Specifies the field of the object specified Yes by the name attribute whose getter method will be called to return the java.util.Map object whose elements are added as query string parameters to the URL generated by this tag. Specifies the scope (application, page, Yes request, or session) to look in for the object specified by the name attribute. If not specified, each scope will be searched, in this order: page, request, session, and then application. Accepts true or false to denote Yes whether or not you want the current transaction token included in the URL
No
paramId paramName
No No
paramProperty
No
paramScope
No
property
No
scope
No
transaction
No
Attribute
Example Usage There are a few different ways to use the redirect tag. The first way, shown here, uses the href attribute to specify an absolute URL to redirect to:
<logic:redirect href="http://www.yahoo.com/"/>
Upon this tags execution, an HTTP redirect response will be sent to the requesting browser. The browser will then request the specified URL. The following example adds to the first example by specifying a single query string parameter to add to the base URL specified by the href attribute:
<logic:redirect href="http://www.yahoo.com/" paramId="query" paramName="queryObj"/>
This example takes the base URL specified by the href attribute and appends the query string parameter specified by the paramId and paramName attributes and composes a URL that the tag then redirects to. Another way to use the redirect tag is shown here:
<logic:redirect page="/search.jsp" name="params"/>
This example uses the page attribute to specify an application-relative base URL and the name attribute to specify a java.util.Map object whose entries are added to the URL as query string parameters.
Fortunately, the Nested Tag Library eliminates both problems associated with the default approach to nested tags. The Nested Tag Library allows you to define logical nesting levels and then associate objects with them so that all tags nested inside a level are relative to that level. Therefore, instead of fully qualifying a nested reference within each level of the object hierarchy, you can use the Nested Tag Librarys nest tag to define each level of the hierarchy. Within each level, you specify the property that youre interested in along with any other tags you need. Here is an example: <nested:nest property="employee"> <nested:nest property="address"> Line 1: <nested:text property="line1"/> Line 2: <nested:text property="line2"/> City: <nested:text property="city"/> State: <nested:text property="state"/> Zip: <nested:text property="zip"/> </nested:nest> </nested:nest> Notice that the Nested Tag Librarys text tag was used instead of the HTML Tag Librarys text tag. The reason is that the base tags are not designed to work with the Nested Tag Librarys nesting features. Instead, you must use the Nested Tag Librarys extensions to the base tags. To best understand the benefits of the Nested Tag Library, here is the same example coded without use of the Nested Tag Library: Line 1: <html:text property="employee.address.line1"/> Line 2: <html:text property="employee.address.line2"/> City: <html:text property="employee.address.city"/> State: <html:text property="employee.address.state"/> Zip: <html:text property="employee.address.zip"/> As you can see, specifying nested properties without the Nested Tag Library results in more verbose JSP code, which increases the chance for error and the potential for a great deal of maintenance work should the nesting hierarchy change.
Notice that the uri attribute specified here is the same as that declared with the <taglib-uri> tag in the web.xml file. Also, notice that the prefix attribute is set to nested. This attribute can be set to whatever you want; however, nested is the accepted default for the Nested Tag Library. The prefix attribute declares the prefix that each tag must have when it is used in the JSP, as shown here: <nested:nest property="results"> Because nested was defined as the prefix, the nest tag was used as shown. However, if you choose to use a prefix of strutsnest, the tag would be used as follows: <strutsnest:nest property="results">
Tag lessThan link match message messages messagesNotPresent messagesPresent multibox nest
Description Nesting-enabled version of the Logic Tag Librarys lessThan tag. Nesting-enabled version of the HTML Tag Librarys link tag. Nesting-enabled version of the Logic Tag Librarys match tag. Nesting-enabled version of the Bean Tag Librarys message tag. Nesting-enabled version of the HTML Tag Librarys messages tag. Nesting-enabled version of the Logic Tag Librarys messagesNotPresent tag. Nesting-enabled version of the Logic Tag Librarys messagesPresent tag. Nesting-enabled version of the HTML Tag Librarys multibox tag. Defines a logical level in a nesting hierarchy and associates an object with it that all of its nested tags will be relative to. Nesting-enabled version of the Logic Tag Librarys notEmpty tag. Nesting-enabled version of the Logic Tag Librarys notEqual tag. Nesting-enabled version of the Logic Tag Librarys notMatch tag. Nesting-enabled version of the Logic Tag Librarys notPresent tag. Nesting-enabled version of the HTML Tag Librarys options tag. Nesting-enabled version of the HTML Tag Librarys optionsCollection tag. Nesting-enabled version of the HTML Tag Librarys password tag. Nesting-enabled version of the Logic Tag Librarys present tag. Nesting-enabled version of the HTML Tag Librarys radio tag. Defines the root object of a nested hierarchy of objects. Nesting-enabled version of the HTML Tag Librarys select tag. Nesting-enabled version of the Bean Tag Librarys size tag.
notEmpty notEqual notMatch notPresent options optionsCollection password present radio root select size
Description Nesting-enabled version of the HTML Tag Librarys submit tag. Nesting-enabled version of the HTML Tag Librarys text tag. Nesting-enabled version of the HTML Tag Librarys textarea tag. Nesting-enabled version of the Bean Tag Librarys write tag. Renders or creates a JSP scripting variable for a string representation of the object related to the current nesting level.
As mentioned, the majority of the tags in the Nested Tag Library are simply tags from the Bean, HTML, and Logic tag libraries to which support for nesting has been added. Thus, they are not individually covered in detail here. Instead, the basic concepts of nesting are discussed because they apply to all the extended tags in the same way. For nonnesting-related information on each of the extended tags, see the descriptions of their base tags in their respective chapters. The remainder of this section discusses in detail each of the tags that are specific to the Nesting Tag Library, including a complete description of the tag, a table listing each of the tags attributes, and a usage example for the tag. In the tables that describe each tags attributes, pay special attention to the Required and Accepts Scriptlet columns. The Required column simply denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the tag will throw a javax.servlet.jsp.JspException at run time. Note that you can declare an error page in your JSP with a page directive to capture any JspExceptions that might be thrown, as shown here:
<%@ page errorPage="error.jsp" %>
If an exception occurs, the page specified by the errorPage attribute will be internally redirected to display an error page. The Accepts Scriptlet column denotes whether or not the given attributes value can be specified with a JSP scriptlet. If a JSP scriptlet is used to specify an attribute value, the scriptlet must comprise the complete value, quote () to quote (), as shown here. Correct:
<nested:nest property="<%=result%>">
Incorrect:
<nested:nest property="<%=result%>-result">
Notice in the incorrect example that -result is used as part of the value for the property attribute following the scriptlet. This is invalid because there are extra characters between the end of the scriptlet and the ending quote. A corrected version of the incorrect example follows:
<nested:nest property="<%=result + "-result"%>"/>
The concatenation of -result is now part of the scriptlet and the scriptlet comprises the complete value for the attribute.
Example Usage The following example illustrates how to use the nest tag:
<nested:nest property="address"> Line 1: <nested:text property="line1"/> Line 2: <nested:text property="line2"/> City: <nested:text property="city"/> State: <nested:text property="state"/> Zip: <nested:text property="zip"/> </nested:nest>
Because the nest tag specifies the object that all of its nested tags are relative to, the nested tags only have to specify properties of the nested object. The equivalent without the Nested Tag Library is shown here:
Line 1: <html:text property="address.line1"/> Line 2: <html:text property="address.line2"/> City: <html:text property="address.city"/> State: <html:text property="address.state"/> Zip: <html:text property="address.zip"/>
As you can see, nesting simplifies JSP development for even this short example; however, the real benefit comes when you have several layers of nesting to handle.
The root tag is used to define the root object of a nested hierarchy of objects. Typically, tags from this tag library and the HTML Tag Library are nested inside the HTML Tag Librarys form tag and use the forms Form Bean object as the root of the nested hierarchy. However, the root tag can be used to explicitly set the hierarchys root object, effectively overriding the Form Bean object. Attribute Attribute name Description Accepts Scriptlet Required No
Specifies the name of an object (in any Yes scope) to set as the root of a nested hierarchy.
Example Usage The following example illustrates how to use the root tag:
<nested:root name="employee"> <nested:nest property="address"> Line 1: <nested:text property="line1"/> Line 2: <nested:text property="line2"/> City: <nested:text property="city"/> State: <nested:text property="state"/> Zip: <nested:text property="zip"/> </nested:nest> </nested:root>
All tags nested inside the root tag will use its associated object as the root object for a nested hierarchy. Thus, in the example, the nested text tags will actually be accessing employee.address.line1 even though they only specify line1.
filter
No
Attribute
Description example, if this attribute is enabled, the less than sign (<) would get converted to < before it is rendered.Defaults to true.
Accepts Scriptlet
Required
Example Usage The following snippet shows the basic usage of the writeNesting tag:
<nested:nest property="address"> Line 1: <nested:writeNesting property="line1"/> </nested:nest>
Essentially, this tag generates the fully qualified nested reference for the given property.
JSTL Overview
JSTL can be broken into two functional areas: a set of tag libraries and an expression language. The tag libraries provide a set of tags that implement general-purpose functionality for iteration and conditional processing, data formatting and localization, XML manipulation, and database access. The expression language simplifies access to Java language constructs within JSPs. Together, the two make up a powerful set of functionality for developing applications with JSPs. JSTL arose out of the Java Community Process (JCP) and thus had the input and forethought of many high-profile organizations and influential individuals in the industry. In June of 2000, the first JSTL specification was finalized and targeted to work with the JSP 1.2 and Servlet 2.3 specifications. Since then, a maintenance release (1.1) became available in January of 2004. Currently, JSTL expressions can only be used as an attribute value with Tag Library tags that support them. However, when JSP 2.0 is finalized, expressions will be supported
throughout JSPs. This means that the expressions will be usable anywhere inside the JSP and will not be limited to just JSTL-enabled tags as they are now. You can download JSTL from Sun's Web site at http://java.sun.com/products/jsp/jstl/ .
Accessing Objects
The JSTL expression language provides a simple mechanism for accessing objects and their properties. The dot (.) operator is used to traverse object hierarchies and access properties. The following snippet illustrates a basic example of the dot operator's usage: <c:out value="${customer.address.city}"/> In this example, the dot operator is used to access the customer object's address property and then the address object's city property. Each instance of the dot operator in the expression evaluates to a getter method call for the property on the left of the operator. Thus, the first dot will call a getAddress( ) method on the customer object. The second dot will then call a getCity( ) method on the object returned from the getAddress( ) call. In order for the dot operator to work, the object on the right of the operator must have a getter method for the property on the left of the operator. Otherwise, the operator will fail. As you can see, this method of traversing object hierarchies is quick and simple. Without JSTL, you'd have to use a JSP expression similar to the following to access properties down a hierarchy: <%= customer.getAddress().getCity() %> The dot operator is great for accessing simple properties; however, it doesn't allow you to access elements of arrays or collections. For that, JSTL has the brackets ([ ]) operator. The brackets operator allows you to specify the index of an element you want to access, as shown next: <c:set var="highBid" value="${bids[0]}"/> This approach works for arrays and list-based collections. For map-based collections, you specify the key for the element you want to access, as shown next: <c:set var="color" value="${param['color']}"/>
Implicit Objects
JSTL makes several objects available to the expression language as implicit objects. The implicit objects are built in and can be used by any expression without having to be initialized or otherwise set up. They are available by default. Utilizing an implicit object is as simple as referencing its name in an expression, as shown here: <c:out value="${header['User-Agent']}"/> In this example, the Core Tag Library's out tag is used to output the value of the 'User-Agent' HTTP header. The header implicit object is a java.util.Map instance containing the incoming request's HTTP headers. The following table lists and describes each of the JSTL implicit objects. Category Cookies Initialization parameters JSP Request headers Implicit Object cookie initParam Description A java.util.Map instance containing the current request's cookies A java.util.Map instance containing the Web application's context initialization parameters specified in web.xml A javax.servlet.jsp.PageContext instance for the current page A java.util.Map instance containing the primary values for the current request's HTTP headers A java.util.Map instance containing all the values for the current request's HTTP headers A java.util.Map instance containing the primary values for the current request's parameters A java.util.Map instance containing all the values for the current request's parameters A java.util.Map instance application-scoped attributes containing
pageContext header
headerValues
Request parameters
param
A java.util.Map instance containing pagescoped attributes A java.util.Map instance containing requestscoped attributes A java.util.Map instance containing sessionscoped attributes
Using Operators
The JSTL expression language supports several operators for comparing and manipulating data in expressions. When expressions contain operators, the operators are applied to the operands and the resulting value is used as the expression's value. Take for example the following snippet: <c:set var="sqrFt" value="${width * length}"/> This example uses the asterisk (*) multiplication operator to multiply a width variable times a length variable and stores the result in a JSP scripting variable.
Operators can also be combined to create complex expressions, as shown next: <c:set var="halfSqrFt" value="${(width * length) / 2}"/> Here, width and length are multiplied and then divided and the resulting value is stored. Logical and relational operators work the same; however, their results are often used with conditional tags, as shown here: <c:if test="${count == 5}"> Count equals 5. </c:if> This example compares the value of the count object to 5 to see if they match. If they do, the text enclosed between the opening and closing if tags is processed. Otherwise it is skipped. The following table lists each of the JSTL expression language operators. Category Arithmetic Logical Relational Validation Operators +, -, *, / (or div), % (or mod) && (or and), || (or or), ! (or not) == (or eq), != (or ne), < (or lt), > (or gt), <= (or le), >= (or ge) empty
As mentioned, multiple operators can be used together in a single expression. The following lists the order of precedence of operators: [ ], . () unary -, not, !, empty *, /, div, %, mod +, binary <, >, <=, >=, lt, gt, le, ge ==, !=, eq, ne &&, and ||, or
import
Description Parses the string representation of a date and/or time. Parses the string representation of a number, currency, or percentage. Sets the request character encoding. Loads a resource bundle and stores it in the named scoped variable or the bundle configuration variable. Stores the given locale in the locale configuration variable. Stores the given time zone in the time zone configuration variable. Specifies the time zone for any time formatting or parsing tags nested in its body.
transaction
update
Description Adds a parameter to a containing transform tag. Parses XML content from a specified source attribute or from body content. Saves the result of an XPath expression evaluation in a scope. Conducts a transformation given a source XML document and an XSLT stylesheet. Subtag of choose that includes its body if its expression evaluates to true.
Second, you can employ the approach used by the Struts tag libraries: an entry is made in the web.xml file and then the URI assigned in the file is used by JSPs. If you choose to take this route, you need to copy the JSTL .tld files into your Web application's WEB-INF directory so that you can reference them. The following table lists the absolute URI for each of the libraries, should you choose to reference the .tlds in that way. Library Core Format SQL XML Prefix c fmt sql x URI http://java.sun.com/jstl/core http://java.sun.com/jstl/fmt http://java.sun.com/jstl/sql http://java.sun.com/jstl/xml
Struts-EL
As previously mentioned, with the advent of JSTL, the Struts tag library tags should now be used only when there is not a JSTL equivalent tag to replace them. This ensures that JSPs are as portable as possible and shields your application from being too heavily tied to Struts-specific facilities. The following table lists each of the Struts tag library tags that can be replaced by JSTL tags and their corresponding replacements. Struts Tag Library Tag JSTL Replacement
Struts Tag Library Bean Bean Bean Bean Bean Bean Logic Logic Logic Logic Logic Logic Logic Logic Logic
Tag cookie define header include parameter write empty equal greaterEqual greaterThan iterate lessEqual lessThan notEmpty notEqual
JSTL Replacement c:set c:set c:set c:import c:set c:out c:if, c:when c:if, c:when c:if, c:when c:if, c:when c:forEach c:if, c:when c:if, c:when c:if, c:when c:if, c:when
As you can see, JSTL can be used in lieu of many of the Struts tag library tags. However, you may have noticed that none of the tags from the Struts HTML Tag Library has an equivalent JSTL tag. JSTL does not have a tag library for rendering HTML form elements, thus the absence of Struts tag replacements. For the HTML tags and all the tags in the Bean and Logic tag libraries that do not have JSTL replacements, a project called Struts-EL was created. Struts-EL was created by David Karr and is currently distributed with Struts in the distribution's contrib folder. It is likely that Struts-EL will eventually become part of the Struts core. The Struts-EL project is an extension to Struts that provides a JSTL expression language-enabled version of each Struts tag for which no JSTL replacement exists. Most of the base Struts tag library tags' attributes accept values represented as scriptlet expressions. This allows the tags to have dynamic attribute values. For example, the Bean Tag Library's message tag accepts scriptlet expressions for its key attribute, as shown here:
<bean:message key="<%=messageKey%>"/>
This example uses the value of the messageKey JSP scripting variable as the value for the message tag's key attribute. Notice that the JSP scripting variable had to be specified within the scriptlet expression identifiers <%= and %>. The following example shows the Struts-EL equivalent of the previous example using a JSTL expression to specify a dynamic value for the key attribute:
<bean-el:message key="${messageKey}"/>
As you can see, the JSTL expression syntax is a little shorter and is cleaner looking.
As mentioned, the basic concepts of using JSTL expressions apply to all the Struts-EL tags in the same way. Any tag attribute that accepts a scriptlet expression with the base tags will accept a JSTL expression with the Struts-EL tags.
This example accesses the cat cookie with a JSTL expression that makes use of the JSTL implicit cookie object. bean:define Replacement Example The following snippet shows the basic usage of the define tag from the Bean Tag Library:
<bean:define id="name" name="nameObj"/>
bean:header Replacement Example The following snippet shows the basic usage of the header tag from the Bean Tag Library:
<bean:header id="browser" name="User-Agent"/>
This example accesses the 'User-Agent' header with a JSTL expression that makes use of the JSTL implicit header object. bean:include Replacement Example
The following snippet shows the basic usage of the include tag from the Bean Tag Library:
<bean:include id="yahooContents" href="http://www.yahoo.com/"/>
bean:parameter Replacement Example The following snippet shows the basic usage of the parameter tag from the Bean Tag Library:
<bean:parameter id="color" name="clr"/>
This example accesses the clr parameter with a JSTL expression that makes use of the JSTL implicit param object. bean:write Replacement Example The following snippet shows the basic usage of the write tag from the Bean Tag Library:
<bean:write name="bizObj"/>
logic:empty Replacement Example The following snippet shows the basic usage of the empty tag from the Logic Tag Library:
<<logic:empty name="results"> Your search yielded no results. </logic:empty>
The following snippet shows the basic usage of the equal tag from the Logic Tag Library:
<logic:equal name="count" value="0"> Count is zero. </logic:equal>
bean:greaterEqual Replacement Example The following snippet shows the basic usage of the greaterEqual tag from the Logic Tag Library:
<logic:greaterEqual name="count" value="5"> Count is greater than or equal to five. </logic:greaterEqual>
logic:greaterThan Replacement Example The following snippet shows the basic usage of the greaterThan tag from the Logic Tag Library:
<logic:greaterThan name="count" value="5"> Count is greater than five. </logic:greaterThan>
logic:iterate Replacement Example The following snippet shows the basic usage of the iterate tag from the Logic Tag Library:
<logic:iterate id="result" collection="<%=results%>"> Result: <%=result%><br> </logic:iterate>
logic:lessEqual Replacement Example The following snippet shows the basic usage of the lessEqual tag from the Logic Tag Library:
<logic:lessEqual name="count" value="5"> Count is less than or equal to five. </logic:lessEqual>
logic:lessThan Replacement Example The following snippet shows the basic usage of the lessThan tag from the Logic Tag Library:
<logic:lessThan name="count" value="5"> Count is less than five. </logic:lessThan>
logic:notEmpty Replacement Example The following snippet shows the basic usage of the notEmpty tag from the Logic Tag Library:
<logic:notEmpty name="results"> Your search returned results! </logic:notEmpty>
logic:notEqual Replacement Example The following snippet shows the basic usage of the notEqual tag from the Logic Tag Library:
Of course, if you only want to use one or two of the libraries, you could place just their taglib definitions in web.xml. Recall from the overview of the web.xml file in Chapter 2 that <taglib-uri> is used to declare the URI (or alias) that will be referenced in each of your JSPs with a taglib directive. The <taglib-location> tag declares the actual location of the Tag Library Descriptor (.tld) file in your Web Archive. The following snippet illustrates how your JSPs will declare their use of the Struts-EL tag libraries with JSP taglib directives:
<%@ taglib uri="/WEB-INF/struts-bean-el.tld" prefix="bean-el" %> <%@ taglib uri="/WEB-INF/struts-html-el.tld" prefix="html-el" %> <%@ taglib uri="/WEB-INF/struts-logic-el.tld" prefix="logic-el" %>
Notice that the uri attributes specified here are the same as those declared with the <taglib-uri> tags in the web.xml file. Also, notice that the prefix attributes are set to 'bean-el', 'html-el', and 'logic-el', respectively. These attributes can be set to whatever you want; however, 'bean-el', 'html-el', and 'logic-el' are the accepted defaults for the Struts-EL tag libraries. The prefix attribute declares the prefix that each tag must have when it is used in the JSP, as shown here:
<bean-el:message key="label.search.name">
Because 'bean-el' was defined as the prefix, the message tag was used as shown. However, if you chose to use a prefix of 'strutsbean-el', the tag would be used in the following way:
<strutsbean-el:message key="label.search.name">
The Struts-EL HTML Tag Library Tags The following table lists each of the tags in the Struts-EL HTML Tag Library and provides a short description of each tag's purpose. Tag base Description Expression language-enabled version of the HTML Tag
Tag button cancel checkbox errors file form frame hidden html image img javascript link messages multibox option options optionsCollection password radio
Description Library's base tag. Expression language-enabled version of the HTML Tag Library's button tag. Expression language-enabled version of the HTML Tag Library's cancel tag. Expression language-enabled version of the HTML Tag Library's checkbox tag. Expression language-enabled version of the HTML Tag Library's errors tag. Expression language-enabled version of the HTML Tag Library's file tag. Expression language-enabled version of the HTML Tag Library's form tag. Expression language-enabled version of the HTML Tag Library's frame tag. Expression language-enabled version of the HTML Tag Library's hidden tag. Expression language-enabled version of the HTML Tag Library's html tag. Expression language-enabled version of the HTML Tag Library's image tag. Expression language-enabled version of the HTML Tag Library's img tag. Expression language-enabled version of the HTML Tag Library's javascript tag. Expression language-enabled version of the HTML Tag Library's link tag. Expression language-enabled version of the HTML Tag Library's messages tag. Expression language-enabled version of the HTML Tag Library's multibox tag. Expression language-enabled version of the HTML Tag Library's option tag. Expression language-enabled version of the HTML Tag Library's options tag. Expression language-enabled version of the HTML Tag Library's optionsCollection tag. Expression language-enabled version of the HTML Tag Library's password tag. Expression language-enabled version of the HTML Tag Library's radio tag.
Description Expression language-enabled version of the HTML Tag Library's reset tag. Expression language-enabled version of the HTML Tag Library's rewrite tag. Expression language-enabled version of the HTML Tag Library's select tag. Expression language-enabled version of the HTML Tag Library's submit tag. Expression language-enabled version of the HTML Tag Library's text tag. Expression language-enabled version of the HTML Tag Library's textarea tag. Expression language-enabled version of the HTML Tag Library's xhtml tag.
The Struts-EL Logic Tag Library Tags The following table lists each of the tags in the Struts-EL Logic Tag Library and provides a short description of each tag's purpose. Tag forward iterate match messagesNotPresent messagesPresent notMatch notPresent present redirect Description Expression language-enabled version of the Logic Tag Library's forward tag. Expression language-enabled version of the Logic Tag Library's iterate tag. Expression language-enabled version of the Logic Tag Library's match tag. Expression language-enabled version of the Logic Tag Library's messagesNotPresent tag. Expression language-enabled version of the Logic Tag Library's messagesPresent tag. Expression language-enabled version of the Logic Tag Library's notMatch tag. Expression language-enabled version of the Logic Tag Library's notPresent tag. Expression language-enabled version of the Logic Tag Library's present tag. Expression language-enabled version of the Logic Tag Library's redirect tag.
The following table lists each of the tags in the Struts-EL Tiles Tag Library and provides a short description of each tag's purpose. Tag add definition get getAsString importAttribute initComponentDefinitions insert put putList useAttribute Description Expression language-enabled version of the Tiles Tag Library's add tag. Expression language-enabled version of the Tiles Tag Library's definition tag. Expression language-enabled version of the Tiles Tag Library's get tag. Expression language-enabled version of the Tiles Tag Library's getAsString tag. Expression language-enabled version of the Tiles Tag Library's importAttribute tag. Expression language-enabled version of the Tiles Tag Library's initComponentDefinitions tag. Expression language-enabled version of the Tiles Tag Library's insert tag. Expression language-enabled version of the Tiles Tag Library's put tag. Expression language-enabled version of the Tiles Tag Library's putList tag. Expression language-enabled version of the Tiles Tag Library's useAttribute tag.
property*)>
This example defines a form-bean tag and the tags that can be nested inside of it. According to the definition, the tag can have nested icon, display-name, description, set-property, and form-property tags. The question mark (?) and asterisk (*) characters following the nested tags names indicate the number of times the nested tag can be nested. The question mark character indicates that the tag can be nested zero or one time. The asterisk character indicates that the tag can be nested zero or more (unlimited) times. A plus (+) character indicates that the tag must be nested at least once and as many times as youd like. The lack of a trailing character means that the tag must be nested exactly once and no more. If no tags can be nested inside the defined tag, EMPTY will be used to denote that:
<!ELEMENT set-property EMPTY>
<param-name>config</param-name> <param-value> /WEB-INF/struts-config.xml, /WEB-INF/struts-config2.xml, /WEB-INF/struts-config3.xml, </param-value> </init-param> When Struts loads the configuration files, if there is any overlap among the files settings, Struts will use the last settings specified. For example, if configuration file A specifies a setting and then configuration file B specifies the same setting with a different value, the setting in configuration file B will override the one in configuration file A, provided that file B is loaded after file A. In addition to support for multiple configuration files, Struts version 1.1 added support for application modules. The module feature allows applications to be broken down into discreet chunks and can be thought of as being almost a mini-application inside of a large application. Using modules requires you to create a separate configuration file for each module, as shown here: <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servletclass> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>config/ModuleA</param-name> <param-value>/WEB-INF/struts-config-moduleB.xml</param-value> </init-param> <init-param> <param-name>config/ModuleB</param-name> <param-value>/WEB-INF/struts-config-moduleA.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> Notice that the second and third init-param definitions specify parameters named config/moduleA and config/ModuleB, respectively. Struts uses the part of the name following the slash (/) as the logical name for the module and loads and associates the specified configuration file with that module. Thus, for a parameter named config/ModuleA, the modules name is ModuleA. For module configuration files, parameter names must begin with config/ in order for Struts to recognize them. When using modules, you still need to define a default configuration file for your application with the config parameter, as you would with a nonmodular application. Note For more information on using Struts modules, refer to Chapter 9.
Table 16-1 lists and describes each of the tags used to configure the Struts configuration file. Table 16-1: Struts Configuration File Tags Tag action Description Maps an application URL either to an Action object that will be executed when the specified URL is requested or to another URL that will be forwarded to. Encapsulates the set of actions the application will have. Defines several global configuration settings for a Struts application. Defines a Java data source that an application can use to access a database or similar resource. Encapsulates the set of data sources the application will have. Defines an exception handler to process a specific exception thrown by an Action. Defines a Form Bean and assigns a logical name to it. Encapsulates the set of Form Beans the application will have. Defines a form property for dynamic Form Beans. Defines a logical name for a URL, thus allowing code to reference the logical name and not the URL itself. Encapsulates a set of exception handlers, defined by exception tags, which are global to the application. Encapsulates a set of forwards, defined by forward tags, which are global to the application. Defines a resource bundle that Struts will use when looking up externalized strings, messages, and labels. Defines a plugin that Struts loads at application startup and unloads at application shutdown. Defines a property and its value. Is the root tag for the Struts configuration file and thus encapsulates all other tags in the file.
action-mappings controller data-source data-sources exception form-bean form-beans form-property forward global-exceptions global-forwards message-resources plug-in set-property struts-config
The remainder of this chapter discusses each tag in detail, including a complete description of the tag, the tags DTD definition, a table that lists each of the tags attributes (if the tag has attributes), and a usage example for the tag. In the tables that describe each tags attributes, pay special attention to the Required column, which denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the Struts framework will not be properly configured and, consequently, will not function properly.
There are three ways you can use the action tag, as listed here and shown later in the Example Usage section. You can use the type attribute to map an org.apache.struts.action.Action subclass to the application URL specified by the path attribute. You can use the forward attribute to specify a URL to forward to with a call to RequestDispatcher.forward( ) when the URL specified by the path attribute is matched. You can use the include attribute to specify a URL to forward to with a call to RequestDispatcher.include( ) when the URL specified by the path attribute is matched.
DTD Definition
Following is the definition for the action tag from the Struts configuration file DTD: <!ELEMENT action (icon?, display-name?, description?, set-property*, exception*, forward*)>
Attributes
Attribute attribute Description Specifies the name of the request- or session-scope attribute under which the Form Bean associated with this action is stored. Normally the name specified with the name attribute is used to look up the Form Bean; however, if this attribute is used, it will be used instead.This attribute is only valid if the name attribute is specified. Specifies the fully qualified class name of the configuration object to instantiate for this Action definition.Defaults to org.apache.struts.config.ActionConfig. Specifies a module-relative URL to forward to when this Action mappings path is matched. Uses RequestDispatcher.forward( ) to perform forward.Only this attribute or one of the include or type attributes can be specified at a time. Specifies a module-relative URL to forward to when this Action mappings path is matched. Uses RequestDispatcher.include( ) to perform forward.Only this attribute or one of the forward or type attributes can be specified at a time. Specifies a module-relative URL to which control will be forwarded if the Form Bean associated with this Action is set to be validated and the validation process fails.This attribute is only valid if the name attribute is specified. Specifies the logical name of a Form Bean, defined with a form-bean tag, which will be associated with this action. Required No
className
No
forward
No
include
No
input
No
name
No
Description Specifies the module-relative URL to map to. Specifies a value that will be passed as a general-purpose configuration parameter to the Action object defined by the type attribute upon each execution of the action. Specifies the prefix to add to request parameter names when populating this Actions associated Form Bean. Thus, a request parameter named username coupled with this attribute set to search would try to call a method called setSearchUsername( ) on the Form Bean.This attribute is only valid if the name attribute is specified. Specifies a comma-delimited list of security roles that are allowed to access this Action. Specifies the scope (request or session) that will be used to access the Form Bean associated with this action with the name attribute.This attribute is only valid if the name attribute is specified. Specifies the suffix to add to request parameter names when populating this Actions associated Form Bean. Thus, a request parameter named username coupled with this attribute set to search would try to call a method called setUsernameSearch( ) on the Form Bean.This attribute is only valid if the name attribute is specified. Specifies the fully qualified class name for the org.apache.struts.action.Action subclass to associate with this Action mapping.Only this attribute or one of the forward or include attributes can be specified at a time. Accepts true or false to denote whether or not this action will be set as the default action for the application. If set to true, any application URLs that dont match another mapping will be handled by this action definition.Only one action definition per module configuration should have this set to true.Defaults to false. Accepts true or false to denote whether or not the Form Bean specified by the name attribute will have its validate( ) method invoked before this Action is executed.This attribute is only valid if the name attribute is specified.Defaults to true.
Required Yes No
prefix
No
roles scope
No No
suffix
No
type
No
unknown
No
validate
No
Example Usage
As mentioned, there are three ways you can use the action tag. The first way, shown here, defines a global exception handler: <action-mappings> <action path="/search" type="com.xyzcorp.app.SearchAction"/> </action-mappings> This example uses the type attribute to specify an Action object that will be executed when the specified path is matched. The second way to use the action tag is shown here: <action-mappings> <action path="/search" forward="/search.jsp"/> </action-mappings> This example uses the forward attribute to specify that the /search.jsp URL will be forwarded by using RequestDispatcher.forward( ) when the specified path is matched. The following snippet shows the third general way to use the action tag: <action-mappings> <action path="/search" include="/search.jsp"/> </action-mappings> This example uses the include attribute to specify that the /search.jsp URL will be forwarded by using RequestDispatcher.include( ) when the specified path is matched.
DTD Definition
Following is the definition for the action-mappings tag from the Struts configuration file DTD: <!ELEMENT action-mappings (action*)>
Attribute
Attribute type Description DeprecatedUse the action tags className attribute instead. Required No
Example Usage
The following example illustrates how to use the action-mappings tag: <action-mappings> <action path="/search" type="com.xyzcorp.app.SearchAction"/> </action-mappings>
DTD Definition
Following is the definition for the controller tag from the Struts configuration file DTD: <!ELEMENT controller (set-property*)>
Attributes
Attribute bufferSize className Description Specifies the input buffer size that will be used for file uploads.Defaults to 4096 bytes. Specifies the fully qualified class name of the configuration object to instantiate for this controller definition.Defaults to org.apache.struts.config.ControllerConfig. Specifies the content type (and optional character encoding) that will be set on each HTTP response. Note that this setting can be overridden by an Action, JSP, or similar resource that a request is forwarded to.Defaults to text/html. DeprecatedConfigure your underlying logging library instead. Specifies the pattern for how the path attribute of forward tags is mapped to URLs.$M Replaced by this modules prefix.$P Replaced by the path attribute of the selected forward.$$ Causes a literal dollar sign ($) to be used.All other $x variables, where x is variable, are reserved for future use and will be silently ignored.Defaults to $M$P. Accepts true or false to denote whether or not the action tags input attribute will be treated as the name of a forward whose path will be used. If set to false (the default), the action tags input attribute will be taken as the literal path.Defaults to false. Accepts true or false to denote whether or not a java.util.Locale object will be stored in users sessions.Defaults to true. Specifies the maximum size, in bytes, for file uploads. Alternatively, if you add K, M, or G to the end of the value, it will be interpreted as kilobytes, megabytes, or gigabytes, respectively.Defaults to 250M. Specifies the maximum size in bytes for file uploads that will be kept in memory. Alternatively, if you add K, M, or G to the end of the value, it will be interpreted as kilobytes, megabytes, or gigabytes, respectively. Files larger than this threshold will be written to disk.Defaults to 256K. Specifies the fully qualified class name of the object to use for handling file uploads.Defaults to org.apache.struts.upload.CommonsMultipartRequestHandle Required No No
contentType
No
debug forwardPattern
No No
inputForward
No
locale
No
maxFileSize
No
memFileSize
No
multipartClass
No
Attribute nocache
Description r. Accepts true or false to denote whether or not HTTP headers will be added to each response to disable browser caching.Defaults to false. Specifies the pattern for how the page attribute of Struts tag library tags is mapped to URLs.$M Replaced by this modules prefix.$P Replaced by the page attribute of the selected tag.$$ Causes a literal dollar sign ($) to be used.All other $x variables, where x is variable, are reserved for future use and will be silently ignored.Defaults to $M$P. Specifies the fully qualified class name for RequestProcessor subclass that will be used for module.Defaults org.apache.struts.action.RequestProcessor. the this to
Required No
pagePattern
No
processorClass
No
tempDir
No
Example Usage
There are many different ways that the controller tag can be used because it has several attributes, all of which are optional. Following is an example usage that specifies the maximum file size for file uploads: <controller maxFileSize="3M"/> This example sets the maximum size for file uploads to three megabytes.
DTD Definition
Following is the definition for the data-source tag from the Struts configuration file DTD: <!ELEMENT data-source (set-property*)>
Attributes
Attribute className Description Specifies the fully qualified class name for the configuration object to instantiate for this data source definition.Defaults to org.apache.struts.config.DataSourceConfig. Specifies the servlet context attribute key under which this data source will be stored. If using application modules, the module prefix will be appended to the key (e.g., Required No
key
No
Attribute
Required
type
Specifies the fully qualified class name for the object that will be instantiated for this data source. Must implement javax.sql.DataSource.
No
Example Usage
The following example illustrates the basic usage of the data-source tag: <data-sources> <data-source> <set-property property="driverClass" value="org.postgresql.Driver"/> </data-source> </data-sources> Typically, you will not have to set any attributes on the data-source tag itself unless you are defining multiple data sources. All you have to do is configure your data source with nested set-property tags. The set-property tags will be used to invoke setter methods on the data source when it is initialized.
DTD Definition
Following is the definition for the data-sources tag from the Struts configuration file DTD: <!ELEMENT data-sources (data-source*)>
Example Usage
The following example illustrates how to use the data-sources tag: <data-sources> <data-source> <set-property property="driverClass" value="org.postgresql.Driver"/> </data-source> </data-sources>
The exception tag is used to define an exception handler to process a specific exception thrown by an Action. This feature allows you to assign a different handler to each type of exception that is thrown by actions. There are two ways you can use the exception tag, as listed here and shown later in the Example Usage section: You can define global exception handlers by placing the exception tags inside the global-exceptions tag. Global exception handlers apply to all actions. You can define action-specific exception handlers by nesting exception tags underneath an action tag. Action-specific exception handlers can only be seen by the enclosing action and will override any global exception handlers with the same target exception.
DTD Definition
Following is the definition for the exception tag from the Struts configuration file DTD: <!ELEMENT exception property*)> (icon?, display-name?, description?, set-
Attributes
Attribute bundle Description Specifies the servlet context attribute key for a resource bundle, defined with the messageresources tag, which will be used to retrieve the message for the key specified by the key attribute. Specifies the fully qualified class name for the configuration object to instantiate for this exception definition.Defaults to org.apache.struts.config.ExceptionConfig. Specifies the fully qualified class name for this exception handler. Specifies the resource bundle message key to use with this handler. Specifies the module-relative URL to redirect to if this exception handler is triggered. Specifies the scope (request or session) that will be used to access the org.apache.struts.action.ActionError object for this exception. Specifies the fully qualified class name of the exception class that this handler is for. Required No
className
No
No Yes No No
type
Yes
Example Usage
As mentioned, there are two ways you can use the exception tag. The first way, shown here, defines a global exception handler: <global-exceptions> <exception type="com.xyzcorp.app.DateFormatException" key="errors.date.format" path="/error.jsp"/> </global-exceptions>
The following is the second way to use the exception tag: <action-mappings> <action path="/search" type="com.xyzcorp.app.SearchAction"> <exception type="com.xyzcorp.app.DateFormatException" key="errors.date.format" path="/searchError.jsp"/> </action> </action-mappings> This example defines an action-specific exception handler. Only the enclosing action can see this exception handler and it will override a global exception handler targeted at the same exception.
DTD Definition
Following is the definition for the form-bean tag from the Struts configuration file DTD: <!ELEMENT form-bean property*, formproperty*)> (icon?, display-name?, description?, set-
Attributes
Attribute className Description Specifies the fully qualified class name of the configuration object to instantiate for this Form Bean definition.Defaults to org.apache.struts.config.FormBeanConfig. DeprecatedThis is now determined based on whether or not the class specified with the type attribute is a subclass of org.apache.struts.action.DynaActionForm. Specifies the logical name for the Form Bean. Required No
dynamic
No
name
Yes
Attribute type
Description Specifies the fully qualified class name for the Form Bean class.
Required Yes
Example Usage
As mentioned, there are two ways you can use the form-bean tag. The first way, shown here, defines a concrete Form Bean: <form-beans> <form-bean name="logonForm" type="com.xyzcorp.app.LogonForm"/> </form-beans> The following is the second way to use the form-bean tag: <form-beans> <form-bean name="logonForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans> This example defines a dynamic Form Bean whose properties are specified with the nested form-property tags. Notice that the type attribute is set to org.apache.struts.action.DynaActionForm. This informs Struts that the Form Beans properties are defined in the configuration file.
DTD Definition
Following is the definition for the form-beans tag from the Struts configuration file DTD: <!ELEMENT form-beans (form-bean*)>
Attribute
Attribute type Description DeprecatedUse the form-bean className attribute instead. tags Required No
Example Usage
The following example illustrates how to use the form-beans tag:
DTD Definition
Following is the definition for the form-property tag from the Struts configuration file DTD: <!ELEMENT form-beans (form-bean*)>
Attributes
Attribute className Description Specifies the fully qualified class name of the FormPropertyConfig subclass to use for this property.Defaults to org.apache.struts.config.FormPropertyConfig. Specifies the initial value of the property. If not specified, primitives will be initialized to 0 and objects will be initialized with their default constructor (thus, Strings will be initialized to ). Specifies the name of the property. Specifies the size of the array to create if the type attribute specifies an array and the initial attribute is omitted. Specifies the fully qualified class name for the propertys underlying field. Optionally, [] can be appended to the type declaration to denote that the field is indexed (e.g., java.lang.String[]). Required No
initial
No
name size
Yes No
type
Yes
Example Usage
The following example illustrates the basic usage of the form-property tag: <form-beans> <form-bean name="logonForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans>
Instead of specifying a concrete class for your Form Bean definition, you set its type to org.apache.struts.action.DynaActionForm or a subclass and then list each of its properties with the form-property tag.
DTD Definition
Following is the definition for the forward tag from the Struts configuration file DTD: <!ELEMENT forward property*)> (icon?, display-name?, description?, set-
Attributes
Attribute className Description Specifies the fully qualified class name for the configuration object to instantiate for this forward definition.Defaults to org.apache.struts.config.ForwardConfig. Accepts true or false to denote whether or not the URL specified with the path attribute will be application-relative when using application modules.Defaults to false. Specifies the logical name for the forward. Specifies the URL for this forward. Accepts true or false to denote whether or not an HTTP redirect will be executed for this forwards URL.Defaults to false. Required No
contextRelative
No
Yes Yes No
Example Usage
As stated, there are two ways you can use the forward tag. The first way, shown here, defines a global forward: <global-forwards> <forward name="success" path="/success.jsp"/> </global-forwards> The second way to use the forward tag is shown here: <action-mappings> <action path="/search" type="com.xyzcorp.app.SearchAction">
<forward name="success" path="/results.jsp"/> </action> </action-mappings> This example defines an action-specific forward. Only the enclosing action can see this forward and it will override a global forward with the same logical name if present.
DTD Definition
Following is the definition for the global-exceptions tag from the Struts configuration file DTD: <!ELEMENT global-exceptions (exception*)>
Example Usage
The following example illustrates how to use the global-exceptions tag: <global-exceptions> <exception type="com.xyzcorp.app.DateFormatException" key="errors.date.format" path="/error.jsp"/> </global-exceptions> This tag simply encapsulates the set of exception handlers that are global to the application.
DTD Definition
Following is the definition for the global-forwards tag from the Struts configuration file DTD: <!ELEMENT global-forwards (forward*)>
Attribute
Attribute Type Description DeprecatedUse the forward tags className Required No
Attribute
Required
Example Usage
The following example illustrates the basic usage of the global-forwards tag: <global-forwards> <forward name="success" path="/success.jsp"/> <forward name="failure" path="/failure.jsp"/> </global-forwards> This tag simply encapsulates the set of forwards that will be global to the application.
DTD Definition
Following is the definition for the message-resources tag from the Struts configuration file DTD: <!ELEMENT message-resources (set-property*)>
Attributes
Attribute className Description Specifies the fully qualified class name of the configuration object to instantiate for this message resources definition.Defaults to org.apache.struts.config.MessageResourcesConfig. Specifies the fully qualified class name of the MessagesResourcesFactory subclass that will be used to create this message resource instance.Defaults to org.apache.struts.util.PropertyMessageResourcesFactor y. Specifies the servlet context attribute key under which this message resource instance will be stored. If using application modules, the module prefix will be appended to the key (e.g., ${key}${prefix}).Defaults to the value specified by the constant org.apache.struts.Globals.MESSAGES_KEY. Accepts true or false to denote whether or not missing messages should return null. Specifies a configuration parameter value that will be passed to the createResources( ) method of the factory object specified by the factory attribute. Required No
factory
No
key
No
null parameter
No Yes
Example Usage
The following example illustrates the basic usage of the message-resources tag: <message-resources
parameter="com.xyzcorp.app.ApplicationResources"/> This example specifies that Struts should use a file called ApplicationResources .properties from the com.xyzcorp.app package as its resource bundle. Notice that the .properties portion of the filename is not specified with the tag; Struts automatically appends that to the name of the file that you specify with the parameter attribute. Sometimes its useful or necessary to have more than one resource bundle. You can accomplish that by using multiple message-resources tags in your configuration file, as shown here: <message-resources parameter="com.xyzcorp.app.ApplicationResources"/> <message-resources parameter="com.xyzcorp.app.AlternateApplicationResources" key="alternate"/> Each instance of the message-resources tag must specify a unique key with the key attribute to identify it, unless its for the default resource bundle, which does not require an explicit key.
DTD Definition
Following is the definition for the plug-in tag from the Struts configuration file DTD: <!ELEMENT plug-in (set-property*)>
Attribute
Attribute className Description Specifies the fully qualified class name for the plugin. This class must implement the org.apache.struts.action.PlugIn interface. Required Yes
Example Usage
The following example illustrates the usage of the plug-in tag: <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/> </plug-in> The plug-in tag is quite simple because it has only one attribute. In most cases, there will be nested set-property tags to dynamically configure the plugin. Each plugin defines its own set of properties that can be configured via set-property tags.
DTD Definition
Following is the definition for the set-property tag from the Struts configuration file DTD: <!ELEMENT set-property EMPTY>
Attributes
Attribute property value Description Specifies the name of the property. Specifies the value of the property. Required Yes Yes
Example Usage
The following example illustrates the basic usage of the set-property tag: <plug-in className="org.apache.struts.tiles.TilesPlugin"> <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/> <set-property property="moduleAware" value="true"/> </plug-in> At run time, when Struts parses a configuration file with a definition similar to this, it will use reflection to look up and invoke the setDefinitionsConfig( ) and setModuleAware( ) methods of the class specified by the plug-in tags className attribute, passing them the defined values.
DTD Definition
Following is the definition for the struts-config tag from the Struts configuration file DTD: <!ELEMENT struts-config exceptions?, global(data-sources?, form-beans?, global-
Example Usage
The following snippet illustrates how to use the struts-config tag:
Metadata Tags
Several of the tags for the Struts configuration file give you the option to nest metadata tags. The metadata tags exist solely for adding extra information to the configuration file that will show up in GUI tools and the like; Struts itself ignores the metadata tags. None of the metadata tags has any attributes; thus, you just add text between opening and closing tags to specify their value, as shown here:
<action path="/search" type="com.xyzcorp.app.SearchAction"> <icon> <small-icon>small.gif</small-icon> <large-icon>large.gif</large-icon> </icon> <display-name>Search Action</display-name> <description>Search Action searches for employees.</description> </action>
The following table lists and describes each of the metadata tags. Tag description display-name icon large-icon small-icon Description Defines descriptive text for the enclosing tag. Defines a short description (or name) for the enclosing tag. Encapsulates an instance of the large-icon and the small-icon tags. Defines the location for a large (32 x 32 pixel) icon to associate to the enclosing tag. Defines the location for a small (16 x 16 pixel) icon to associate to the enclosing tag.
Struts Console is free software and can be downloaded from http://www.jamesholmes.com/struts/ which has all the information you need to configure Struts Console to work with your favorite Java IDE. In addition, this books appendix provides a Struts Console quick reference. Figure 16-1 shows Struts Console running as a stand-alone application.
Note
This example defines a definition tag and the tags that can be nested inside of it. According to the definition, the tag can have nested icon, display-name, description, put, and putList tags. The question mark (?) and asterisk (*) characters following the nested tags names indicate the number of times the nested tag can be nested. The ? character indicates that the tag can be nested zero or one time. The * character indicates that the tag can be nested zero or more (unlimited) times. A plus (+) character indicates that the tag must be nested at least once and as many times as youd like. No trailing character means that the tag must be nested exactly once and no more. If no tags can be nested inside the defined tag, EMPTY is used to denote that, as shown next:
<!ELEMENT set-property EMPTY>
Note that your application's Struts configuration file must conform to the Struts configuration file DTD, which specifies the order in which elements are to appear in the file. Because of this, you must place the Tiles <plug-in> definition in the proper place in the file. The easiest way to ensure that you are properly ordering elements in the file is to use a tool, such as Struts Console, that automatically formats your configuration file so that it conforms to the DTD.
The remainder of this chapter discusses each tag in detail, including a complete description of the tag, the tag's DTD definition, a table that lists each of the tag's attributes (if the tag has attributes), and a usage example for the tag. In the tables that describe each tag's attributes, pay special attention to the Required column, which denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the Tiles framework will not be properly configured and, consequently, will not function properly.
DTD Definition
Following is the definition for the add tag from the Tiles configuration file DTD: <!ELEMENT add (#PCDATA)>
Attributes
Attribute content Description DeprecatedOriginally for compatibility with the now Required No
Attribute
Required
direct
DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the type attribute set to 'string' instead. Specifies the type (string, page, or definition) of the value. If present, it indicates how the value specified with the value attribute is treated. Specifies the value for this entry.
No
type
No
value
No
Example Usage
The following example illustrates the basic usage of the add tag: <definition name="mainLayout" path="/layouts/main.jsp"> <putList name="urls"> <add value="http://www.google.com/"/> <add value="http://www.yahoo.com/"/> </putList> </definition> Each add definition is added to the enclosing list in the order specified.
DTD Definition
Following is the definition for the bean tag from the Tiles configuration file DTD: <!ELEMENT bean (set-property*)>
Attribute
Attribute classtype Description Specifies the fully qualified class name for the bean. Required Yes
Example Usage
The following snippet illustrates how to use the bean tag: <definition name="mainLayout" path="/layouts/main.jsp"> <putList name="items"> <bean classtype="org.apache.struts.tiles.beans.SimpleMenuItem"> <set-property property="link" value="aLink1"/> <set-property property="value" value="aValue1"/>
</bean> </putList> </definition> Beans defined with the bean tag can have their properties initialized at creation by nesting set-property tags with the name of the property and the value to be initialized.
DTD Definition
Following is the definition for the definition tag from the Tiles configuration file DTD: <!ELEMENT definition putList*)> (icon?, display-name?, description?, put*,
Attributes
Attribute controllerClass Description Specifies the fully qualified class name of a controller object that is executed before this definition is inserted. Specifies the URL for a controller that is executed before this definition is inserted. Specifies the name of another definition that this definition is to extend. Specifies the logical name for the definition. DeprecatedUse the path attribute instead. Specifies the URL for the tile. Specifies a role to check against the currently authenticated user. If the user is not in the specified role, this definition will not be inserted. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the path attribute instead. Required No
No No Yes No No No
template
No
Example Usage
The following example illustrates the basic usage of the definition tag: <definition name="mainLayout" path="/layouts/main.jsp"> <put name="header" value="/layouts/header.jsp"/> <put name="footer" value="/layouts/footer.jsp"/> </definition> Each of the attributes nested underneath the definition tag can be accessed by the JSP specified with the path attribute.
DTD Definition
Following is the definition for the item tag from the Tiles configuration file DTD: <!ELEMENT item (#PCDATA)>
Attributes
Attribute classtype Description Specifies the fully qualified class name of the item. If specified, it must be a subclass of org.apache.struts.tiles.beans.MenuItem. Specifies the value to set the bean's icon property to. Specifies the value to set the bean's link property to. Specifies the value to set the bean's tooltip property to. Specifies the value to set the bean's value property to. Required No
No Yes No Yes
Example Usage
The following example illustrates the basic usage of the item tag: <definition name="mainLayout" path="/layouts/main.jsp"> <putList name="items"> <item value="Home" link="/index.jsp"/> <item value="Search" link="/search.jsp"/> </putList> </definition> The values specified with the value and link attributes are used to initialize the corresponding properties on the MenuItem object.
DTD Definition
Following is the definition for the put tag from the Tiles configuration file DTD: <!ELEMENT put (#PCDATA)>
Attributes
Attribute content
Description DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the value attribute instead. DeprecatedOriginally for compatibility with the now defunct Template Tag Library.Use the type attribute set to 'string' instead. Specifies the name for the attribute. Specifies the type (string, page, or definition) of the value. If present, it indicates how the value specified with the value attribute is treated. Specifies the value for the attribute.
Required No
direct
No
name type
Yes No
value
No
Example Usage
The following example illustrates the basic usage of the put tag: <definition name="mainLayout" path="/layouts/main.jsp"> <put name="header" value="/layouts/header.jsp"/> <put name="footer" value="/layouts/footer.jsp"/> </definition> Defining attributes with the put tag is as simple as specifying their names and values.
DTD Definition
Following is the definition for the putList tag from the Tiles configuration file DTD: <!ELEMENT putList ((add*|item*|bean*|putList*)+)>
Attribute
Attribute name Description Specifies the name of the list. Required Yes
Example Usage
The following example illustrates the basic usage of the putList tag: <definition name="mainLayout" path="/layouts/main.jsp"> <putList name="urls"> <add value="http://www.google.com/"/> <add value="http://www.yahoo.com/"/> </putList>
</definition> Each tag nested between opening and closing putList tags will be added to the backing java.util.List instance in the order specified.
DTD Definition
Following is the definition for the set-property tag from the Tiles configuration file DTD: <!ELEMENT set-property EMPTY>
Attributes
Attribute property value Description Specifies the name of the property. Specifies the value of the property. Required Yes Yes
Example Usage
The following example illustrates the basic usage of the set-property tag: <definition name="mainLayout" path="/layouts/main.jsp"> <putList name="items"> <bean classtype="org.apache.struts.tiles.beans.SimpleMenuItem"> <set-property property="link" value="aLink1"/> <set-property property="value" value="aValue1"/> </bean> </putList> </definition> At run time, when the Tiles framework parses a configuration file with a definition similar to this, it will use reflection to look up and invoke the setLink( ) and setValue( ) methods of the class specified by the bean tag's classtype attribute, passing them the defined values.
DTD Definition
Following is the definition for the tiles-definitions tag from the Tiles configuration file DTD: <!ELEMENT tiles-definitions (definition+)>
Example Usage
The following snippet illustrates how to use the tiles-definitions tag: <tiles-definitions> <definition /> <definition /> </tiles-definitions>
Metadata Tags
Several of the tags for the Tiles configuration file give you the option to nest metadata tags. The metadata tags exist solely for adding extra information to the configuration file that will show up in GUI tools and the like; the Tiles framework, itself, ignores the metadata tags. None of the metadata tags has any attributes, thus you just add text between opening and closing tags to specify their value, as shown here:
<action path="/search" type="com.xyzcorp.app.SearchAction"> <icon> <small-icon>small.gif</small-icon> <large-icon>large.gif</large-icon> </icon> <display-name>Search Action</display-name> <description>Search Action searches for employees.</description> </action>
The following table lists each of the metadata tags and its description. Tag description display-name icon large-icon small-icon Description Defines descriptive text for the enclosing tag. Defines a short description (or name) for the enclosing tag. Encapsulates an instance of the large-icon and small-icon tags. Defines the location for a large (3232 pixel) icon to associate to the enclosing tag. Defines the location for a small (1616 pixel) icon to associate to the enclosing tag.
This Web site has all the information for configuring Struts Console to work with your favorite Java IDE. In addition, this book's appendix provides a Struts Console quick reference. Figure 17-1 shows Struts Console running as a stand-alone application.
The Validator configuration files are XML-based and their formats are governed by a Document Type Definition (DTD) file that specifies how the configuration tags must be ordered in each file, what settings are required, and so on. Each Validator configuration file declares its conformance to the DTD by having the following DOCTYPE definition at the top of the file: <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> When Validator reads the configuration file, its XML parser uses the DOCTYPE definition to determine the DTD that the XML file must conform to. If configured to do so, the XML parser will validate the XML file's conformance to the DTD. Note An in-depth discussion of using the Validator framework is found in Chapter 6.
Each tag described in this chapter has a DTD Definition section that lists the tag's definition in the Struts configuration file DTD. The definitions will be similar to the one shown in the following snippet: <!ELEMENT form-bean (icon?, display-name?, property*)> description?, set-property*, form-
This example defines a form-bean tag and the tags that can be nested inside of it. According to the definition, the tag can have nested icon, display-name, description, set-property, and form-property tags. The question mark (?) and asterisk (*) characters following the nested tags' names indicate the number of times the nested tag can be nested. The ? character indicates that the tag can be nested zero or one time. The * character indicates that the tag can be nested zero or more (unlimited) times. A plus (+) character indicates that the tag must be nested at least once and as many times as you'd like. No trailing character means that the tag must be nested exactly once and no more. If no tags can be nested inside the defined tag, EMPTY is used to denote that, as shown next: <!ELEMENT arg1 EMPTY>
Table 18-1: Validator Configuration File Tags Tag arg0 arg1 arg2 arg3 constant constant-name constant-value field form form-validation formset global javascript msg validator var var-name var-value Description Defines the first replacement value, {0}, for a validation's error message. Defines the second replacement value, {1}, for a validation's error message. Defines the third replacement value, {2}, for a validation's error message. Defines the fourth replacement value, {3}, for a validation's error message. Defines a named value that can be used as a replacement parameter within the field tag's nested tags. Defines the constant tag's constant name. Defines the constant tag's constant value. Defines the set of validations that will be applied to a form's field. Defines a Form Bean whose set of fields will be validated based on rules defined by nested field tags. Is the root tag for the Validator configuration file and thus encapsulates all other tags in the file. Defines validations for a set of forms. Encapsulates the set of validations and the set of constants that Validator will use. Defines client-side JavaScript code for a validation. Defines an error message that will override a validation's default message. Defines a validation routine and assigns it a logical name. Defines a variable that will be passed to each of a field's validators at run time. Defines the var tag's variable name. Defines the var tag's variable value.
The remainder of this chapter discusses each tag in detail, including a complete description of the tag, the tag's DTD definition, a table that lists each of the tag's attributes (if the tag has attributes), and a usage example for the tag. In the tables that describe a tag's attributes, pay special attention to the Required column, which denotes whether or not the given attribute is required when using the tag. If an attribute is required and you do not specify it when using the tag, the Validator framework will not be properly configured and, consequently, will not function properly.
The arg tags are used to define values for a validation's error message. There are four arg tags and they are shown here:
The first replacement value, specified by {0} The second replacement value, specified by {1} The third replacement value, specified by {2} The fourth replacement value, specified by {3}
Before the specified validation's error message is generated, it is parsed and any {N} reference is replaced with the message specified by the corresponding tag. Following is an example resource bundle message that contains a {0} reference:
errors.required={0} is a required field
At run time, when Validator uses this error message, it will attempt to replace any parametric references with the values specified by their respective arg0 - arg3 tags. Thus, if 'Username' was specified with the arg0 tag, the preceding message would be turned into the following message:
Username is a required field
DTD Definition Following is the definition for the arg tags from the Validator configuration file DTD:
<!ELEMENT argN EMPTY>
Attributes Attribute key name resource Description Required Specifies a key for a resource bundle message that No will be used as the replacement value. Specifies the logical name of the validation that this No tag will be applied to. Accepts 'true' or 'false' to denote whether or not the No key attribute's value will be taken as a literal value rather than a message key.Defaults to 'true'.
Example Usage The following example illustrates the basic usage of the arg2 tag:
<field property="zipCode" depends="required,mask"> <arg2 key="prompt.zipCode"/> <var> <var-name>mask</var-name> <var-value>>^\d{5}\d*$</var-value> </var> </field>
This example specifies the {2} replacement value to use for each of the validations specified by the field tag's depends attribute. Alternatively, the arg tag can be configured to apply to only a specific validation's error message by using the name attribute, as shown next:
<arg2 name="required" key="prompt.zipCode"/>
In this example, the replacement value will be applied only to the required validation's error message.
Each time a ZIP code needs to be validated with the mask validation, the constant can be used to specify the regular expression to use, instead of having to specify the regular expression itself, as shown next:
<field property="zipCode" depends="required,mask"> <var> <var-name>mask</var-name> <var-value>${zip}</var-value> </var> </field>
To use constants, you simply enclose the constant name with an opening ${ and a closing }. DTD Definition Following is the definition for the constant tag from the Validator configuration file DTD:
<!ELEMENT constant (constant-name, constant-value)>
Example Usage The following snippet illustrates how to use the constant tag:
<global> <constant> <constant-name>zip</constant-name> <constant-value>^\d{5}\d*$</constant-value> </constant>
</global>
The constant tag can be used an unlimited number of times. Each use of the constant tag must have nested constant-name and constant-value tags.
Example Usage The following snippet illustrates how to use the constant-name tag:
<global> <constant> <constant-name>zip</constant-name> <constant-value>^\d{5}\d*$</constant-value> </constant> </global>
Example Usage The following snippet illustrates how to use the constant-value tag:
<global> <constant> <constant-name>zip</constant-name> <constant-value>^\d{5}\d*$</constant-value> </constant> </global>
The field tag is used to define the set of validations that will be applied to a form's field. DTD Definition Following is the definition for the field tag from the Validator configuration file DTD:
<!ELEMENT field (msg|arg0|arg1|arg2|arg3|var)*>
Attributes Attribute depends Description Required Specifies the comma-delimited list of No validations that will be applied to this field. Specifies the name of a collection field No whose elements will be validated. If this attribute is specified, the value specified with the property attribute will be used as the name of the property that will be validated on each object in the collection. Specifies a value that will be compared No against the enclosing Form Bean's 'page' property if it has one. If the value matches, then this field definition's validations will be applied. If not, they will be bypassed. This feature is useful for wizard-style forms where fields need to be conditionally validated based on which page the wizard is currently on. Specifies the name of the form field. Yes
indexedListProperty
page
The following example illustrates the basic usage of the field tag:
<field property="zipCode" depends="required,mask"> <arg0 key="prompt.zipCode"/> <var> <var-name>mask</var-name> <var-value>>^\d{5}\d*$</var-value> </var> </field>
Each validation specified with the depends attribute will be executed in order. Consequently, if a validation fails, the remaining validations will be skipped. Additionally, each validation can be globally or individually customized with nested arg0 - arg3, msg, and var tags.
Attribute Attribute name Example Usage The following snippet illustrates how to use the form tag:
<form name="logonForm"> <field property="username" depends="required"> <arg0 key="prompt.username"/> </field> <field property="password" depends="required"> <arg0 key="prompt.password"/> </field> </form>
Required Yes
The name specified with the name attribute must match the logical name of a Form Bean from the Struts configuration file. Similarly, each of the form's nested field tags must match a property of the Form Bean.
Example Usage The following snippet illustrates how to use the form-validation tag:
Attributes Attribute country language Description Required Specifies the country code that this form set's No definitions will be applied to. Specifies the language code that this form set's No definitions will be applied to.
Example Usage The following example illustrates the basic usage of the formset tag:
<formset> <form name="logonForm"> </form> <form name="searchForm"> </form> </formset>
Because this example omits the country and language attributes, the enclosed validations will be applied to users within any locale unless specifically overridden with other formset definitions. If desired, one or more forms' validations can be overridden by specifying additional form sets for specific locales, as shown next:
<formset language="fr"> <form name="logonForm"> </form> </formset>
This example provides validation settings for all users whose locale has French as its language. The country and language attributes can be used in tandem or individually based on how specific or broad the validation settings will be.
Example Usage The following example illustrates the basic usage of the global tag:
<global> <constant> <constant-name>zip</constant-name> <constant-value>^\d{5}\d*$</constant-value> </constant> </global>
Example Usage The following example illustrates how to use the javascript tag:
<validator name="minlength" classname="org.apache.struts.validator.FieldChecks" method="validateMinLength" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionErrors, javax.servlet.http.HttpServletRequest" msg="errors.minlength"> <javascript> <![CDATA[ function validateMinLength(form) { var isValid = true; var focusField = null; var i = 0; var fields = new Array(); oMinLength = new minlength(); for (x in oMinLength) { var field = form[oMinLength[x][0]]; if (field.type == 'text' || field.type == 'textarea') { var iMin = parseInt(oMinLength[x][2]("minlength")); if ((trim(field.value).length > 0) && (field.value.length < iMin)) { if (i == 0) { focusField = field; } fields[i++] = oMinLength[x][1]; isValid = false; } } } if (fields.length > 0) { focusField.focus(); alert(fields.join('\n')); } return isValid; } ]]> </javascript> </validator>
Notice that the JavaScript code is enclosed in a <![CDATA[ ]]> tag. This is an XML facility that is used to notify XML parsers that the enclosed text should be taken as is and should not be parsed. Normally, parsers would parse the text for other tags or XML entities; however, sometimes it's necessary to specify text that has XML-like references in it, but that should not be parsed. The <![CDATA[ ]]> tag makes that possible.
bundle message key for an error message that will be used when the validation fails. Sometimes, however, it's necessary to use an error message other than the default for a validation. The msg tag makes this possible by allowing for an alternative message to be set for a specific use of the validation. DTD Definition Following is the definition for the msg tag from the Validator configuration file DTD:
<!ELEMENT msg EMPTY>
Attributes Attribute key Description Required Specifies a resource bundle message key that the No specified validation will use for its error message instead of its default message. Specifies the logical name of the validation whose No error message will be overridden. Accepts 'true' or 'false' to denote whether or not the No key attribute's value will be taken as a literal value rather than a message key.Defaults to 'true'.
name resource
Example Usage The following example illustrates the basic usage of the msg tag:
<field property="ssNum" depends="required,mask"> <msg name="mask" key="errors.ssNum"/> <arg0 key="ssNum.prompt"/> <var> <var-name>mask</var-name> <var-value></var-value> </var> </field>
In this example, the mask validation is overridden to use the errors.ssNum message key instead of the one defined by the validation. As you can see, the msg tag is useful for specifying custom error messages for validations.
Following is the definition for the validator tag from the Validator configuration file DTD:
<!ELEMENT validator (javascript?)>
Attributes Attribute classname depends Description Required Specifies the name of the class that houses Yes the validation routine. Specifies a comma-delimited list of other No validations defined by the validator tag that must pass before this validation is executed. Specifies an alternate method name to use No for the JavaScript code generated by this tag if client-side validation is enabled. Specifies the name of the validation routine's Yes method in the class specified by the classname attribute. Specifies the comma-delimited list (in order) Yes of the validation routine's arguments. Specifies a resource bundle message key for Yes the error message that will be generated if this validation fails. Specifies the logical name for the validation. Yes
jsFunctionName
method
methodParams msg
The following example illustrates the basic usage of the validator tag:
<validator name="minlength" classname="org.apache.struts.validator.FieldChecks" method="validateMinLength" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionErrors, javax.servlet.http.HttpServletRequest" msg="errors.minlength"/>
Each validation definition specifies the Java class, method, and method arguments for the validation. Validator uses reflection to instantiate and invoke the validation at run time.
be set using this tag. Additionally, variables defined with the var tag can be used by the arg0 - arg3 and msg tags, as shown here:
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
To reference variables defined by the var tag from other tags, you must use this form: ${var:varName} (where varName is the name of the defined variable). The variable's name and value are defined with nested var-name and var-value tags, respectively. DTD Definition Following is the definition for the var tag from the Validator configuration file DTD:
<!ELEMENT var (var-name,var-value)>
Example Usage The following snippet illustrates how to use the var tag:
<field property="username" depends="required,maxlength"> <arg0 key="prompt.username"/> <var> <var-name>maxlength</var-name> <var-value>16</var-value> </var> </field>
The var tag can be nested underneath the field tag an unlimited number of times. Each use of the var tag must have nested var-name and var-value tags.
Example Usage The following snippet illustrates how to use the var-name tag:
<field property="username" depends="required,maxlength"> <arg0 key="prompt.username"/>
Example Usage The following snippet illustrates how to use the var-value tag:
<field property="username" depends="required,maxlength"> <arg0 key="prompt.username"/> <var> <var-name>maxlength</var-name> <var-value>16</var-value> </var> </field>
Securing
Struts
Most Web applications require certain aspects of the system to be secured in some manner. Security requirements are often specified at both the system and functional levels. System requirements may dictate, for example, that entry of sensitive information should be performed over a secure HTTP connection (HTTPS). On a higher level, functional requirements may dictate that only users with administrative privileges can access certain pages and menu items. From a developer's perspective, the critical task is to identify which of the requirements can be satisfied using standard security mechanisms, and which requirements require a customized security solution. Quite often, security requirements dictate some sort of customization. In some cases, you can use a combination of standard security mechanisms and customization to achieve the desired security policy.
Levels of Security
Security is a fairly broad topic and may encompass everything from encryption to personalization, depending on how 'security' is defined. This chapter focuses on the levels of security that you can implement to secure your Struts applications, beginning in this section with an overview of the various security levels. That is followed by sections that look in depth at using container-managed security and applicationmanaged security, the two primary ways to secure your Struts applications.
Transport-level security using HTTPS Authentication and authorization Role-based access control Container-managed security Application-managed security
Some aspects of personalization will also be covered, specifically some techniques for hiding or displaying content based on a user's authorization.
whereby the user's session can be hijacked. Sensitive user information, such as a credit card number, may be stored in that session. A network snoop could use the session ID to spoof a valid user session. Due to this risk, container-managed security does not support protocol switching. However, if you need protocol switching and can accept the security risks, there are mechanisms for doing so that integrate with Struts, as you will see in 'Integrating Struts with SSL,' later in this chapter.
It is declarative. Authentication and authorization are specified in the web.xml file. Container-specific details, such as the security realm, typically are configured in server-specific XML configuration files. It supports multiple authentication schemes, such as password authentication, FORM-based authentication, authentication using encrypted passwords, and authentication using client-side digital certificates.
Using container-specific security realms, user data can be provided by a variety of stores, including flat files, relational databases, and Lightweight Directory Access Protocol (LDAP) servers. Redirects are handled automatically. In other words, the container determines when a user is accessing a protected URL, prompts for user credentials, and, if authenticated, redirects to the requested page. This is a powerful mechanism, particularly for applications that publish links to protected pages in e-mail communications.
The implementation of container-managed security varies by container. An application using container-managed security generally requires modification at some level when ported from one application server to another. The login flow does not allow easy custom processing of login requests. In other words, additional processing cannot be performed in the authentication process. Authorization can only use a flat, role-based approach. Access to Web pages cannot be granted based on multiple factors, for example, a managerial level and a department number. FORM-based login forces a workflow that uses a separate page for login. This limits the flexibility of the application. Container-managed authentication requires changes to the application server's configuration that may not be allowed in a hosted environment.
These limitations can be overcome by using application-managed security. However, using application-managed security means that custom code must be designed and written. The decision of which approach to take should be driven by the requirements. While container-managed security is simpler to implement, it does restrict the flexibility of your security policy. One container's security implementation may be different from another's, making your application less portable. Also, containermanaged security limits you to a specific login workflow that may not be what you want for your application. Application-managed security, on the other hand, allows you to implement your security policy as needed at the price of requiring more custom code. Struts mitigates this problem by allowing for customized role processing via a custom request processor. Also, servlet filters can be used to apply across-the-board security policies. Cookies can be used to persist user login information between sessions. In addition, there are Struts extensions that permit finer-grained control of the use of HTTPS. One last point: there is a somewhat hybrid approach that allows programmatic access to the methods that are usually only available when using container-managed security. This interesting mechanism will be covered later in the chapter, in the 'Using Servlet Filters for Security' section.
To explain how to use container-managed security, this section describes how you would apply container-managed security to the Mini HR application, introduced in Chapter 2, to fulfill a particular security requirement. Assume that Mini HR resides on ABC, Inc.'s corporate intranet. The application allows anyone to access the employee search. However, only administrators can add or remove employees from the database. If someone attempts to perform an administrator function, that user is prompted to input a username and password. If the password is valid, and the user is an administrator, the application will display the requested page. Although this security requirement can be completely fulfilled without making any modifications to Java code or JSP pages, as you will see, it is often better to group URLs under role-specific path prefixes. Therefore, you must change the location of add.jsp and add.do to /admin/add.jsp and /admin/add.do, respectively. This also means that you need to change the index.jsp page and struts-config.xml. Making these changes may seem burdensome, but it will make implementing security easier. Following are the additions needed to web.xml to implement container-managed security: <web-app> [ snipped ] <security-constraint> <web-resource-collection> <web-resource-name>AdminPages</web-resource-name> <description>Administrator-only pages</description> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>administrator</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>MiniHRRealm</realm-name> </login-config> <security-role> <description>HR Administrator</description> <role-name>administrator</role-name> </security-role> </web-app> The three XML elements (security-constraint, login-config, and security-role) that were added define the security requirements. The security-constraint element associates a collection of pages with a role. The pages are identified using URL patterns. If a user attempts to access a page that matches one of the patterns and the user has the associated role, then the user is allowed access. If the user has not yet been authenticated, the user is prompted to log in according to the settings of the login-config element. If the user authenticates and has the administrator role, then the user is redirected to the requested page. Before discussing the login configurations, a quick detour is necessary to describe URL patterns. These patterns, also known as URL mappings, are dictated by the Java servlet specification. Four types of patterns are searched, in the following order: Explicit mapping No wildcards are used (e.g., /add.jsp or /admin/remove.do).
Path prefix mapping Contains a /, then a path prefix, then a /*. This mapping can be used to specify an entire subbranch of your Web application (e.g., /admin/* or /search/company/*). Extension mapping Contains *. followed by a prefix. This mapping can be used to specify all files of a certain type (e.g., *.jsp). It is also often used to map Struts actions (e.g., *.do). Default mapping / Matches all URLs for the Web application. This mapping matches any URL of the Web application. Any URL beginning with the context path of the Web application will match this pattern. As you can see, these patterns are not very flexible. You cannot, for example, specify a pattern of /add.*. If you did not place the administrative URLs under a specific path prefix, you would have to explicitly list each constrained URL. From a security perspective, partitioning your application using role-based paths makes securing the application much easier. Even if you are not using container-managed security, this approach has benefits, as you will discover later, in the section 'Using Servlet Filters for Security.' If you decide to use Struts modules, then you have already established some partitioning for your application. Each module will be in its own path off the context root. Organizing modules by role is a reasonable approach-therefore, the use of Struts modules generally will make implementing your security policy easier.
Login Configurations
The login-config element indicates the type of authentication to be performed and where the user information can be found. A Web application can have only one login configuration. The auth-method nested element indicates the type of authentication and accepts the values listed and described in the following table. Authentication BASIC Description The browser pops up a dialog box that allows the user to enter a username and password. The username and password are encoded using the Base-64 algorithm and sent to the server. The Base-64 algorithm is a common Web encoding scheme that is often used to encode e-mail attachments and so on. Allows for a custom form to be specified. The form must contain a j_username field for the username and a j_password field for the password. The form must submit to j_security_check. The username and password are Base-64 encoded. Similar to BASIC authentication except that the username and password are encrypted into a message digest value. This configuration may not be supported by all browsers. The client is required to provide a digital certificate for authentication. This is the most secure configuration. However, it also is the most costly. Certificates for production use must be purchased from a certificate authority.
FORM
DIGEST
CLIENT-CERT
In addition to specifying the type of login, the login configuration may also specify a security realm. A security realm is essentially the store from which a Web application retrieves and verifies user credentials. In addition, a realm provides a mechanism for specifying the roles that users may have. For the login configuration, no details of the realm can be provided other than the realm name. That name refers, either directly or indirectly, to a container-specific security realm implementation. DIGEST authentication provides the same basic user experience as BASIC. However, because DIGEST authentication may not be supported by all browsers, it is not discussed further here. There are ways of encrypting the user credentials without requiring DIGEST.
CLIENT-CERT requires the user to have a digital certificate. This chapter discusses only BASIC and FORM-based login because these are the authentication methods that are encountered in most situations.
BASIC Login
The simplest way to get started with container-managed security is to use BASIC authentication, which involves the use of the security realm. The security realm serves as a reference to container-specific security storage. The mechanism for associating this logical realm to the concrete realm varies by container. Typically, realms can be based on flat files (for example, property files or XML files), a relational database, or an LDAP server. Some containers, such as JBoss, provide a mapping between a realm and a Java Authentication and Authorization Service (JAAS) implementation. For simplicity, the security realm for ABC, Inc. will be implemented using the Tomcat UserDatabase realm. By its default configuration, Tomcat supports this realm for all applications. The realm retrieves usernames, passwords, roles, and role assignments from the tomcat-users.xml file. You will add three users to the system to test the functionality-two of the users will be assigned the administrator role. The third user will be defined using a non-administrator role. This third user is defined for testing and illustrative purposes. Add the following four elements (in bold) to the <TOMCAT_HOME>/conf/ tomcat_users.xml file: <tomcat-users> <role name="administrator"/> <user name="bsiggelkow" password="thatsme" roles="administrator" /> <user name="jholmes" <user name="gburdell" </tomcat-users> Now, deploy this new version of the Mini HR application. You need to restart Tomcat to enable the new functionality. Then, browse to the welcome page, index.jsp. You should see no differences here. Next, click the Add an Employee link. Your browser will display a dialog box similar to that shown in Figure 19-1. password="maindude" roles="administrator" password="gotech" roles="employee" /> />
Figure 19-1: Browser-provided authentication dialog box Entering a valid username and password of a user with the administrator role displays the requested page. If the user does not have the administrator role but is otherwise valid, an HTTP status of 403 (Access Denied) is the response. Likewise, if you click Cancel, an HTTP status of 401 (Authentication Required) is returned. If a matching username and password are not found, then the dialog box simply redisplays. Once a user has been authenticated, the Web application can glean useful user data from the HTTP request. The two methods of interest are getUserPrincipal( ), which can be used to acquire the username, and isUserInRole( ), which can be used to determine if a user has a specified role. These methods can be used in the Action classes to perform such things as the following: Load the user's profile and store it in the session Render a specific response or redirect to a certain URL based on the user's role In addition to the programmatic uses of this data, Struts applications can also use this information to do the following: Allow role-based access to Action classes configured in the struts-config.xml file. Dynamically hide or display presentation components (links, buttons, menus, etc.) based on the user's role using the <logic:present> and <logic:notPresent> tags. The first two programmatic uses are available to any JSP/servlet-based application. The Struts-specific uses, however, warrant more discussion. Action mappings in the struts-config.xml file have an optional roles attribute, which accepts a comma-separated list of roles. If a user has any one of those roles, the user can access the action. Otherwise, access is denied. This attribute provides a much more natural way of granting/denying access than using the URL patterns. Note, however, that using the action mappings only restricts access to URLs served through the Struts controller (e.g., *.do). It does not restrict access to JSP pages or static HTML pages. The following snippet from the struts-config.xml file shows the use of the roles attribute: <action path="/add" type="com.jamesholmes.minihr.AddAction"
name="addForm" scope="request" validate="true" input="/add.jsp" roles="administrator"> </action> If your Web application places the JSP pages under the WEB-INF folder and all requests are handled by the Struts controller, the roles attribute can be used to completely control access to Web resources. As mentioned, the rendering of portions of a JSP page can be based on role using the <logic> tags. Considering the main page, it might be worthwhile to display only the Add an Employee link if the user is an administrator. Such a page might look something like the following: <logic:present role="administrator"/> <a href="admin_login.jsp">Administrator Login</a> </logic:present> <ul> <li><html:link forward="add">Add an Employee</html:link></li> <li><html:link Employees</html:link></li> </ul> However, this approach leads to a common problem with container-managed security. You want to show the link only if the user is an administrator, but you don't know if the user is an administrator unless the user selects the link and is authenticated. There are several alternatives that you can use to solve this problem. One alternative is to force all users to log in-obviously leading to disgruntled users if the login does not add value. Another alternative is to provide a means for a user to proactively log in. The proactive login behavior can still be accomplished by using container-managed securityalbeit through some trickery. What you will do is create a link on the main page (index.jsp) to a security-constrained JSP page (/admin/admin_login.jsp). This page will simply redirect back to the main page. Since it will be a protected page, the user will be forced to log in. The admin_login JSP page is as simple as the following: <%@ taglib uri="/WEB-INF/tlds/struts-logic.tld" prefix="logic" %> <logic:redirect page="/index.jsp"/> The redirect tag results in an HTTP redirect response. The page attribute of the tag indicates where to redirect the response. In this example, this will result in a redirect to the main page of the application. Since this page is put under the /admin path, no changes are necessary to the security constraint in the web.xml file. The user experience now is as follows: 1. When the index page is first displayed, the user does not see the Add an Employee link, but can log in. 2. Upon login, the index is redisplayed, this time displaying the administrative link. 3. Upon selecting this link, the user does not have to reauthenticate. forward="search">Search for
FORM-Based Login
FORM-based login is another variant of a container-managed login configuration. With FORM-based login, you supply a Web page (either a JSP or static HTML page) that contains the login form and a page to display if an error occurs. This provides a much more consistent look and feel for your application. The behavior of FORM-based login when a user is trying to access protected resources is similar to the behavior of BASIC login. To implement FORMbased login, you supply a Web page that has a form for login that must follow specific guidelines. The form must submit two parameters with the names j_username and j_password, holding the username and password respectively, to the j_security_check URL. Optionally, you can also specify a Web page that will be displayed if a login error occurs. Here's the login_form.html page: <html> <head> <title>ABC, Inc. Human Resources Portal</title> </head> <body> <font size="+1">ABC, Inc. Human Resources Portal Login</font><br> <hr width="100%" noshade="true"> <form action="j_security_check"> Username: <input type="text" name="j_username"/><br/> Password: <input type="password" name="j_password"/><br/> <input type="submit" value="Login"/> </form> </body> </html> Next, change the login-config element of web.xml to use FORM-based authentication: <login-config> <auth-method>FORM</auth-method> <realm-name>MiniHRRealm</realm-name> <form-login-config> <form-login-page>/login_form.html</form-login-page> <form-error-page>/login_error.html</form-error-page> </form-login-config> </login-config> Now, instead of displaying the standard dialog box, the browser displays the new page (albeit a little bland in this case). Once authenticated, the user is taken back to the index page, where the hidden link is now displayed. A natural progression of these modifications is to place the login form on the main index page, thereby reducing an extra mouse click and page display. This approach is quite common in Web applications. Often, the main pages of applications have a login form on the welcome page. The main page provides lots of information to anyone. When you log in, however, the user experience becomes personalized for you. However, if you move the login form to the Mini HR index page, you will find that it will not work. When you submit your username and password, an error will occur. Tomcat generates the following error: HTTP Status 400 - Invalid direct reference to form login page
Tomcat raises this error because the login page can only be managed by the container. Access by a user to a protected page is the only mechanism that should trigger display of the login page. In the container-managed authentication workflow, once a user is authenticated, the client is then redirected to the requested protected page. If the user browses directly to the login page, the protected resource to redirect to is undefined. In other words, if the user were to submit the form, the container would not know where to redirect the request. There is no good work-around for this problem. This is one of the frustrations with FORMbased authentication and container-managed security-a simple changein requirements (e.g., put the login on the main page) causes you to rethink your security implementation. However, if you and your users can live with the constraints of container-managed security, it is a great way to provide robust security with minimal coding.
can get it to work for you without having to compromise your requirements, then it is worth pursuing.
Application-Managed Security
If container-managed security is not sufficient for your application, then you need consider creating your own security structure. Generally, you need to address the same issues in application-managed security that are addressed in container-managed security: How and when do you authenticate the users? What determines the authorization levels? Is there a guest user and, if so, what privileges does that user have? Does the application need to support HTTPS for transmission of sensitive data? The following sections look at several ways of handling these issues. As you read through them, you will apply your knowledge to the Mini HR application. Most of this discussion applies to Java-based Web applications in general-not just Struts-based applications.
"maindude",
"gotech",
public User authenticate(String username, String password) throws AuthenticationException { User user = (User) users.get(username); if (user == null) throw new AuthenticationException("Unknown user"); boolean passwordIsValid = user.passwordMatch(password); if (!passwordIsValid) throw new AuthenticationException("Invalid password"); return user; } } This interface provides the basic security services. The authenticate( ) method verifies the user's password and returns an object that represents the user. For the authorization needs of the application, there are different alternatives. While it may be tempting to engineer a complete role-based infrastructure, a more pragmatic approach is to provide a mechanism to determine if a user is an administrator. Following is the object that will hold the user data: package com.jamesholmes.minihr.security; import java.io.Serializable; public class User implements Serializable { private String firstName; private String lastName; private String username; private String password; private String[] roles; public User(String name, String fName, String lName, String pwd, String[] assignedRoles) { username = name; firstName = fName; lastName password } = lName; = pwd
roles = assignedRoles;
public String getUsername() { return username; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public boolean passwordMatch(String pwd) { return password.equals(pwd); } public boolean hasRole(String role) { if (roles.length > 0) { for (int i=0; i<roles.length; i++) { if (role.equals(roles[i])) return true; } } return false; } public boolean isAdministrator() { return hasRole("administrator"); } } Notice that some basic information that can be used for personalization, such as the user's first and last name, has been included. In addition, the isAdministrator( ) method indicates whether or not the user is an administrator. Also a hasRole( ) method has been implemented that will indicate whether or not a user has been assigned a given role. This method will be useful with customized Struts role processing. To tie this custom security service into the application, you also need to create a LoginAction that will process the user login, as shown in the following code. A LogoutAction that allows a user to log out of the application is also created by the following code. A logout action typically needs to just invalidate the user's session. While you cannot force a user to log out, it is important to provide this feature, particularly if your application is accessible from a public terminal. package com.jamesholmes.minihr; import javax.servlet.http.*; import org.apache.commons.beanutils.PropertyUtils; import org.apache.struts.action.*; import com.jamesholmes.minihr.security.*;
public final class LoginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { HttpSession session = request.getSession(); String username = (String) PropertyUtils.getSimpleProperty(form, "username"); String password = (String) PropertyUtils.getSimpleProperty(form,"password"); SecurityService service = new SecurityServiceImpl(); User user = service.authenticate(username, password); session.setAttribute("User", user); // Forward control to this Action's success forward return mapping.findForward("success"); } } First, the action gets the username and password from the login form. Then, it calls the authenticate method of the security service. The returned user is then stored in the session. The exception handling is performed using Struts' declarative exception handling. Back at the index page, you need to change it from the container-managed implementation as shown in the following example. In this case, you want to check the administrator bean property of the user and display the link only if the value is true. Note also that you must explicitly check whether the User object is in the session. This handles the case where the user has not yet been authenticated. <%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/tlds/struts-logic.tld" prefix="logic" %> <html> <head> <title>ABC, Inc. Human Resources Portal</title> </head> <body> <font size="+1">ABC, Inc. Human Resources Portal</font><br> <logic:notPresent name="user" scope="session"> <hr width="100%" noshade="true"> <html:form action="/login"> Username: <html:text property="username"/><br/> Password: <html:password property="password"/><br/> <html:submit value="Login"/> </html:form>
<html:errors/> </logic:notPresent> <hr width="100%" noshade="true"> <ul> <logic:present name="user" scope="session"> <logic:equal name="user" property="administrator" value="true"> <li><html:link forward="add">Add an Employee</html:link></li> </logic:equal> </logic:present> <li><html:link forward="search">Search for Employees</html:link></li> </ul> </body> </html> The important modification to this index page is the use of the <logic:present> tag to hide or show the login form. In addition, the <logic:equal> tag is used to hide or show the Add an Employee link. You are not finished, however. First, remove (or comment out) the containermanaged security sections from the web.xml file. Then, remove the roles attribute from the add.do action mapping. You are not done yet, however. Notice that there is now nothing to prevent someone who is not an administrator from adding a new employee. Anyone can browse directly to the Add an Employee page and submit the form. In addition, although securing the add.jsp page is not critical, it is imperative that you secure the AddAction (add.do). What you need to do is implement the checks that were in place with container-managed security. There are several alternatives to consider.
a user has a particular role. This can be used with container-managed security. For application-managed security, you need to override this method in a custom RequestProcessor. For your implementation, you will get the User object from the session and then call the User.hasRole( ) method. The custom request processor with the overridden method is shown here: package com.jamesholmes.minihr; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache.struts.action.*; import com.jamesholmes.minihr.security.User; public class CustomRequestProcessor extends RequestProcessor { protected boolean processRoles(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException { // Is this action protected by role requirements? String roles[] = mapping.getRoleNames(); if ((roles == null) || (roles.length < 1)) { return (true); } // Check the current user against the list of required roles HttpSession session = request.getSession(); User user = (User) session.getAttribute("user"); if (user == null) { return false; } for (int i = 0; i < roles.length; i++) { if (user.hasRole(roles[i])) { return (true); } } response.sendError(HttpServletResponse.SC_BAD_REQUEST, getInternal().getMessage("notAuthorized", mapping.getPath())); return (false); } } Now, you can add the roles attribute back to your action mapping and the action will be protected. Of course, you also need to plug the customized request processor into the Struts configuration file. That still leaves the somewhat nagging problem of unauthorized users gaining access to the JSP pages. One solution is to place all the JSP pages under WEB-INF and route all JSP page requests through the ActionServlet by using ForwardActions. This
is an appealing solution, because you can then use the roles attribute on these actions. This does mean a fairly drastic change to the organization of a Web application and may require substantial modification of your JSP pages. However, as you will see in the following section, servlet filters can be used to implement security policies that can be applied to related Web resources. In addition to the processRoles( ) method, there is a more generic processPreprocess( ) method. That method should return true to continue standard processing or false to indicate that the response is complete. That method is a good place to put security checks that cannot be implemented using roles alone.
} onErrorUrl = filterConfig.getInitParameter("onError"); if (onErrorUrl == null || "".equals(onErrorUrl)) { onErrorUrl = "/index.jsp"; } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; HttpSession session = req.getSession(); User user = (User) session.getAttribute("user"); ActionErrors errors = new ActionErrors(); if (user == null) { errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.authentication.required")); } else { boolean hasRole = false; for (int i=0; i<roleNames.length; i++) { if (user.hasRole(roleNames[i])) { hasRole = true; break; } } if (!hasRole) { errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.authorization.required")); } } if (errors.isEmpty()) { chain.doFilter(request, response); } else { req.setAttribute(Globals.ERROR_KEY, errors); req.getRequestDispatcher(onErrorUrl).forward(req, res); } }
public void destroy() { } } First, notice that the AuthorizationFilter class implements Filter. Thus, it must implement the init( ), doFilter( ), and destroy( ) methods. The comma-separated list of roles and the URL of the error page to forward to are retrieved from the initialization parameters in the init( ) method. The doFilter( ) method first checks if there is a User in the session. If not, an appropriate ActionError is created and no further checks are performed. Otherwise, it iterates through the list of roles to determine if the user has any of them. If not, an ActionError is created. If any errors were created, then a RequestDispatcher is created to forward to the given URL; otherwise, the doFilter( ) method calls chain.doFilter( ) to continue normal processing. This implementation also demonstrates the ability to integrate filters with Struts. You could have provided a more generic implementation, by calling the sendError( ) method of the HttpServletResponse class with an appropriate HTTP status code (e.g., 401). Filters are configured and deployed like servlets. In the web.xml file, you can specify the filter name and class, and initialization parameters. Then, associate the filter with a URL pattern in a filter mapping. The following is the snippet from the web.xml file that shows the necessary changes for this filter: <filter> <filter-name>adminAccessFilter</filter-name> <filter-class> com.jamesholmes.minihr.security.AuthorizationFilter </filter-class> <init-param> <param-name>roles</param-name> <param-value>administrator</param-value> </init-param> <init-param> <param-name>onError</param-name> <param-value>/index.jsp</param-value> </init-param> </filter> <filter-mapping> <filter-name>adminAccessFilter</filter-name> <url-pattern>/admin/*</url-pattern> </filter-mapping> As you can see, the AuthorizationFilter is fairly generic. There is, in fact, an open-source security filter known as SecurityFilter that is worth considering. This filter permits implementation of a custom security policy, yet still allows programmatic access to role and user information (i.e., the request.isUserInRole( ) and request.getUserPrincipal( ) methods) that is generally only available when using container-managed authentication. It performs this magic by using wrapper classes around the HttpServletRequest. SecurityFilter is configured using a separate configuration file that is very similar to the standard security-constraint element of the web.xml file. It provides the benefits of application-managed security while still enabling standard role processing without modification. There is no need to extend the Struts RequestProcessor for role handling, and the <logic:equal> tag with the role attribute will also work correctly. SecurityFilter is
implemented similarly to AuthorizationFilter. You will want to understand how the filter works and its limitations before migrating your application. Like servlets and Struts actions, filters are extremely powerful. You have complete access to the Java API that can be applied as needed using URL patterns. However, to truly integrate filters with Struts, you may need to delve into Struts implementation details as was shown in the AuthorizationFilter.
Using Cookies
A cookie consists of name-value data that can be sent to a client's browser and then read back again at a later time. Persistent cookies are stored by the client's browser. Cookies can be read only by the same server or domain that originated them. Also, a cookie can have an expiration period. Cookies are supported by most major browsers. However, cookies are often considered a privacy risk and can be disabled by the client. A good approach is to design your Web application to use cookies to improve the user experience, but not to require or force users to allow cookies. For application-managed security, you can use cookies to allow users automatic logins. Specifically, you can create a persistent cookie that contains the user's username and password. Then, when a user accesses the application, you can check for those cookie values. If present, the values can be used to log the user in without requiring them to fill out a login form. Using a servlet filter, or some JavaScript, you could log in a user automatically. Alternatively, you may want to just prepopulate the login form with the values from the cookie. To illustrate the use of cookies, Mini HR will be changed to use them as follows: 1. Once a user logs in, Mini HR creates persistent cookies containing the username and password. 2. Mini HR uses the cookie support of the Struts tags to set the initial values for the login form. For the login action, this means adding the following lines after the authentication check has been performed: Cookie usernameCookie = new Cookie("MiniHRUsername", username); usernameCookie.setMaxAge(60 * 60 * 24 * 30); // 30 day expiration response.addCookie(usernameCookie); Cookie passwordCookie = new Cookie("MiniHRPassword", password); passwordCookie.setMaxAge(60 * 60 * 24 * 30); // 30 day expiration response.addCookie(passwordCookie); This code creates cookies for holding the username and password. Each cookie has an expiration of 30 days. Each cookie is then added to the response. Next, use the Struts bean tags to retrieve the cookie values and write the values to the login form: <logic:notPresent name="user" scope="session"> <bean:cookie id="uname" name="MiniHRUsername" value=""/> <bean:cookie id="pword" name="MiniHRPassword" value=""/> <hr width="100%" noshade="true"> <html:form action="/login"> Username: <html:text property="username" value="<%=uname.getValue()%>"/><br/> Password: <html:password property="password" value="<%=pword.getValue()%>"/><br/>
<html:submit value="Login"/> </html:form> <html:errors/> </logic:notPresent> The cookie tags retrieve the cookie values from the request and store them in page scope variables. These variables are then used as the initial values for the login form fields. However, this example is too simplistic for production use. Generally, using cookies without input from the user is considered overly presumptuous. A good Web application lets the user specify whether they want their user data stored as a cookie. It is also reasonable to let the user specify the length of time before the cookie expires. This type of information is easily gathered and stored by an application. Typically, this information is collected at registration time and stored as part of the user's profile. In addition, the data sent in the cookies should be secured or encrypted. A simple encryption scheme, such as MD5 or a variant of the Secure Hash Algorithm (SHA), can be used to encrypt the cookie value when it is created. Since the server creates the cookie and is the only party to legitimately use the data, it can encrypt and decrypt the data using the algorithm of its own choosing. Alternatively, cookies can be configured to only be transmitted over HTTPS-thereby providing encryption/decryption at the transport level.
http://sslext.sourceforge.net. It is the recommended approach for integrating Struts with SSL processing. Its features include: The ability to declaratively specify in the Struts configuration file whether or not an action mapping should be secure. This feature allows your application to switch protocols between actions and JSP pages. Extensions of the Struts JSP tags that can generate URLs that use the https protocol. SSLEXT consists of a plug-in class for initialization, a custom extension to the Struts RequestProcessor, and a custom extension of the Struts ActionMapping. In addition, custom JSP tags, which extend the Struts tags, are provided for protocol-specific URL generation. SSLEXT also includes an additional JSP tag that lets you specify whether an entire JSP page is secure. SSLEXT depends on the Java Secure Socket Extension (JSSE), which is included with JDK 1.4 and later. Finally, you need to enable SSL for your application server. For Tomcat, this can be found in the Tomcat SSL How-To documentation. SSLEXT works by intercepting the request in its SecureRequestProcessor. If the request is directed toward an action that is marked as secure, the SecureRequestProcessor generates a redirect. The redirect changes the protocol to https and the port to a secure port (e.g., 443 or 8443). This sounds simple enough; however, a request in a Struts application usually contains request attributes. These attributes are lost on a redirect. SSLEXT solves this problem by temporarily storing the request attributes in the session. SSLEXT does not include a lot of documentation but it comes with a sample application that demonstrates its use and features. To try SSLEXT, you can modify Mini HR to use it by changing the login behavior so that the LoginAction occurs over HTTPS. Once logged in, the protocol should be switched back to HTTP. Take the following steps to set up SSLEXT for the Mini HR application: 1. Copy the sslext.jar file into the MiniHR19\WEB-INF\lib folder. 2. Copy the sslext.tld file into the MiniHR19\WEB-INF\tlds folder. 3. Add a taglib declaration in the web.xml for the sslext tag library as follows: 4. 5. 6. <taglib> <taglib-uri>/WEB-INF/tlds/sslext.tld</taglib-uri> <taglib-location>/WEB-INF/tlds/sslext.tld</taglib-location> </taglib> Now, make the following changes to the struts-config.xml file: 1. Add the type attribute to the action-mappings element to specify the custom secure action mapping class as follows: <action-mappings type="org.apache.struts.config.SecureActionConfig"> 2. Add the controller element configured to use the SecureRequestProcessor. If you are already using a custom request processor, change it to extend the SecureRequestProcessor. 3. <controller
processorClass="org.apache.struts.action.SecureRequestProcessor "/> 4. Add the plug-in declaration to load the SSLEXT code: 5. 6. 7. 8. 9. <plug-in className="org.apache.struts.action.SecurePlugIn"> <set-property property="httpPort" value="8080"/> <set-property property="httpsPort" value="8443"/> <set-property property="enable" value="true"/> <set-property property="addSession" value="true"/> </plug-in>
10. Set the secure property to true for the login action mapping by adding the following element: 11. <set-property property="secure" value="true"/> 12. Finally, you need to configure the index.jsp page to always run on http, not https. Otherwise, after you log in, the protocol will remain on https. Add the following taglib directive and custom tag to the index.jsp page (after the existing taglib directives): 13. <%@ taglib uri="/WEB-INF/tlds/sslext.tld" prefix="sslext"%> <sslext:pageScheme secure="false"/> 14. This tag is only needed for those JSP pages that are not accessed through your actions. Now all you need to do is rebuild and redeploy the application. When you click the login link, the protocol will switch to https and the port will switch to 8443. After you log in, you should be redirected back to the index.jsp page and the protocol and port should switch back to http and 8080. You should experiment with using the <sslext:link> tag to create links to secure actions. You will find that using SSLEXT is much easier than using the user-data-constraint subelement of the web.xml file. It gives you fine-grained control where you need it through the tags. At the same time, it leverages the struts-config.xml file to enable simple declarative configuration for secure request processing.
Types of Testing
There are different levels, or types, of testing. Each type serves different purposes. Traditionally, testing is viewed as a quality control function. However, unit testing is considered to be in the development domain. This chapter covers testing from both of these perspectives. However, the primary focus will be on the types of tests run by developers.
Unit Testing
Unit testing refers to independent testing of individual software units. In Java development, a unit is typically associated with a Java class. Unit testing is normally performed by the developer, not the quality assurance or quality control department. The goal of unit testing is to ensure that the unit under test does what it is supposed to do. More specifically, the developer specifies in a unit test what the expected output or behavior of individual methods is for a given set of inputs. Unit testing is typically associated with the xUnit family of software testing frameworks led by the JUnit framework. The folks at the Cactus project (http://jakarta.apache.org/cactus), a testing framework covered in the Using Cactus for Integration Unit Testing section, refer to the following three specific types of unit testing:
Code logic testing The traditional view of unit testing, where the unit is completely isolated. Integration unit testing Testing of a unit that is not in isolation; that is, the unit communicates with other units. Functional unit testing An integrated unit test that verifies the output of a unit for a known input. The unit in this case is typically not a software unit per say, but an application unit (for example, a Web page). Functional unit testing can be considered black-box integration testing.
This chapter covers several tools and techniques that assist with unit testing.
Functional Testing
Functional testing is driven by requirements and use cases and is usually performed by a quality control group. Functional tests ensure that the application performs the functions specified by the requirements. Functional tests are typically focused on verifying that the application or components of the application respond with the expected output when given known input. For Web applications, functional tests can be automated using testing tools.
System Testing
Performance is an important aspect of most applications. System testing is concerned with measuring and verifying the response of the system when the application is run under varying loads. System responses include such things as performance/latency, throughput, and memory consumption. At the end of this chapter, two open-source tools are presented that can measure and analyze this type of data.
super.tearDown(); } public void testSearchByName() { String name ="Jim"; ArrayList l = service.searchByName(name); assertEquals("Number of results", 2, l.size()); } public void testSearchBySSN() { String ssn ="333-33-3333"; ArrayList l = service.searchBySsNum(ssn); assertEquals("Number of results", 1, l.size()); Employee e = (Employee) l.get(0); assertEquals("SSN", ssn, e.getSsNum()); } public void testSearchByUnknownSSN() { String ssn ="999099-49493939"; ArrayList l = service.searchBySsNum(ssn); assertEquals("Number of results", 0, l.size()); } } First, notice that the test class is in the same package as the EmployeeSearchService class and uses the Class under testTest naming convention. If the test class were in a different package, only public methods could be tested. The next thing to notice is the setUp( ) method, which is used to prepare each test. Here, this method creates the EmployeeSearchService object and stores a reference to it in an instance variable. As a corollary, a tearDown( ) method can be implemented to perform any test cleanup. That would be the place, for example, to close a database connection or file resource used by the test. A JUnit test runner actually runs the test. JUnit includes three test runner user interfaces-one based on Java AWT, one based on Java Swing, and a text-based runner that can be run from the command line. In addition, many Java IDEs provide some level of integration with JUnit. The test runner first creates a new instance of the test class. Then, for each test method (i.e., each method that begins with 'test'), the runner calls setUp( ), then the test method itself, and then tearDown( ). This approach allows for the creation of independent, repeatable tests. Each test method should be capable of running separately, with the setUp( ) and tearDown( ) methods performing the initialization and cleanup. The actual verification part of the test is encapsulated in the individual test methods. Generally speaking, there should be at least one test method for each method of the external interface of a class under test. The test method should create the input values, call the appropriate method, and then verify the results. This verification may include examining any returned values as well as checking any expected state changes. Each verification is implemented using one or more of the JUnit assert( ) methods. For example, if a search by social security number is performed using a value for which there is no match, then an assertion can be made that the returned collection should be empty. This type of negative test is illustrated in the EmployeeSearchServiceTest class in the testSearchByUnknownSSN( ) method.
To run this test, ensure that the junit.jar file and the application classes are on your classpath. Then, simply execute the following command: java junit.swingui.TestRunner This displays the JUnit graphical user interface (GUI), shown in Figure 20-1, which allows you to select the test to run. You can then run the test and view the results. If you get a green bar, then all tests succeeded. If the test fails, you can change the code to make it work-then rerun the test. Usually, you do not need to restart the JUnit Swing user interface because it incorporates a mechanism for reloading classes.
Figure 20-1: A successful test run using the JUnit Swing test runner Now that you've seen how to write a unit test, you need to integrate unit testing into the build process. Apache Ant (http://ant.apache.org) is a Java-based build tool that provides support for integrating testing into an application's build cycle. An Ant build is driven by an XML-based build file. The build file includes specific targets that correspond to steps in the build process, such as compiling classes, creating the distribution (such as a .jar file), and deploying the application. Testing can be added to the build cycle by including testing targets in the application's Ant build script. This chapter will not go into complete details of integrating JUnit with Ant; however, it is fairly straightforward and the Ant support for unit testing is well documented in Ant's documentation set. Following is the Ant build script, build.xml, that will be used and expanded throughout this chapter. The test target is the last target in this file. <project name="MiniHR20" default="dist" basedir="."> <property environment="env"/> <property name="build.compiler" value="javac1.4"/> <!-- set global properties for this build --> <property name="src" location="src"/> <property name="webinf.dir" location="web/WEB-INF"/> <property name="build.dir" location="build"/> <property name="dist.dir" location="dist"/> <property name="server.dir" location="${env.CATALINA_HOME}"/> <property name="servlet.jar" <property name="test.dir" location="test"/>
<property name="deploy.dir" location="${server.dir}/webapps"/> <target name="clean"> <delete dir="${build.dir}"/> <delete dir="${dist.dir}"/> </target> <target name="init"> <mkdir dir="${build.dir}"/> <mkdir dir="${dist.dir}"/> </target> <target name="compile" depends="init" description="compile the source "> <javac srcdir="${src}:${test.dir}/src" destdir="${build.dir}" debug="on"> <classpath> <pathelement path="${classpath}"/> <pathelement location="${servlet.jar}"/> <fileset dir="${webinf.dir}/lib"> <include name="**/*.jar"/> </fileset> </classpath> </javac> <copy todir="${build.dir}"> <fileset dir="${src}"> <include name="**/*.properties"/> </fileset> </copy> </target> <target name="dist" depends="compile"> <war destfile="${dist.dir}/${ant.project.name}.war" webxml="${webinf.dir}/web.xml"> <webinf dir="${webinf.dir}"> <include name="struts-config.xml"/> <include name="validation.xml"/> <include name="validator-rules.xml"/> </webinf> <classes dir="${build.dir}"/> <fileset dir="web" excludes="WEB-INF/**.*"/> </war>
</target> <target name="deploy" depends="dist"> <unjar src="${dist.dir}/${ant.project.name}.war" dest="${deploy.dir}/${ant.project.name}"/> </target> <target name="test" depends="compile"> <mkdir dir="${test.dir}/results"/> <junit printsummary="yes" fork="no" haltonfailure="no" errorProperty="test.failed" failureProperty="test.failed"> <formatter type="xml" /> <classpath> <pathelement path="${build.dir}"/> </classpath> <batchtest todir="${test.dir}/results"> <fileset dir="${build.dir}"> <include name="**/*Test.class" /> </fileset> </batchtest> </junit> <junitreport todir="${test.dir}/reports"> <fileset dir="${test.dir}/results"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="${test.dir}/reports"/> </junitreport> <fail message="JUnit if="test.failed"/> </target> </project> The test target in the preceding build script integrates testing into the build. The junit task is used to run tests and generate the results. The junitreport task is used to accumulate and generate an HTML report from those results. These tests are run by simply typing ant test at a command prompt. The reports are generated into the test/reports directory. The generated report includes information about which tests passed, which ones failed, and the specific assertions that failed. If an exception was thrown, the stack trace is included with the report. In addition, some basic timing information is also recorded. As mentioned previously, testing the model should be the top priority when determining what aspects of the system to test first. tests failed. Check reports."
Handles cases properly where required input is missing or invalid Calls the model passing the correct data Marshals and transforms as needed the data from the model Stores the data in the appropriate context or scope Returns the correct action response These types of tests can be performed in a running container (i.e., a servlet engine such as Tomcat) or outside of the container in a normal client JVM. This is an important distinction. Test execution outside the container allows you to isolate the class under test from outside dependencies. When the test fails, you know that the failure is in the class under test, not a problem with the container. In-container tests are a form of integrated unit tests. Integrated unit tests can be valuable for verifying that the class being tested will work in an actual deployed environment. Practically speaking, in-container tests are more complex to set up and run and generally take more CPU time than tests run independently of a container. On the other hand, outside-thecontainer tests often require creation of mock objects to stand in for container-provided software entities such as the servlet request and response objects, the servlet context, and the HTTP session.
Using StrutsTestCase
StrutsTestCase (http://strutstestcase.sourceforge.net) is an open-source JUnit extension that provides a framework of Java classes specifically for testing Struts actions. It provides support for unit testing Struts actions outside of a servlet container using mock objects. These objects are provided as stand-ins for the servlet request and response, and context components such as the servlet context and the HTTP session. Test cases using the mock objects use information from your application's configuration files-specifically the web.xml file and the struts-config.xml file. In addition, StrutsTestCase also supports in-container testing through integration with the Cactus framework. In-container testing will be covered in 'Using Cactus for Integration Unit Testing' later in this chapter. Since StrutsTestCase extends JUnit, the tests can be run using the JUnit test-running tools. Consider a unit test for the SearchAction class of the sample application. This action performs the following steps: 1. Gets the entered name from the form. 2. Determines whether the search is by name (i.e., a name was entered) or by social security number. 3. Performs the search using EmployeeSearchService. 4. Stores the results back in the form. 5. Forwards the request back to the input page. Here's a unit test that verifies this behavior: package com.jamesholmes.minihr; import servletunit.struts.MockStrutsTestCase; public class SearchActionTest extends MockStrutsTestCase { public SearchActionTest(String testName) { super(testName); } public void setUp() throws Exception { super.setUp(); setRequestPathInfo("/search"); }
public void testSearchByName() throws Exception { addRequestParameter("name","Jim"); actionPerform(); assertNotNull("Results are null", ((SearchForm) getActionForm()).getResults()); verifyInputForward(); } } First, notice that the class inherits from servletunit.struts.MockStrutsTestCase, which in turn inherits from the JUnit TestCase (junit.framework.TestCase). This relationship allows access to all the assertion methods provided by JUnit. Next, in the setUp( ) method, the parent's setUp() method is called and the request path is set. Both of these steps are critical. If you override setUp( ), you must call super.setUp( ). Setting the request path ensures that the desired action is called. Notice that the .do extension mapping is not included in the request path. StrutsTestCase uses the web.xml file to determine this. Note also that there is no reference in the test to the SearchAction class itself. StrutsTestCase actually relies on the action mappings in the struts-config.xml file to determine which action to call. So, in reality, you are testing not just the SearchAction class, but the entire action configuration. The test method itself is relatively simple. First, a request parameter is added that represents the data that would be submitted from the HTML form on the search.jsp page. Next, actionPerform( ) actually executes the action. After that, the resultant behavior is verified. Specifically, the results property of the SearchForm object is checked for null. In addition, the forward back to the input page (search.jsp) is verified using the verifyInputForward( ) method. To run this test, some changes need to be made to the test target of the Ant script. Since StrutsTestCase relies on the XML configuration files of the application, the parent directory of the application's WEB-INF directory must be on the classpath. The following three entries should be added to the classpath element of the junit task: <fileset dir="${webinf.dir}/lib"/> <pathelement location="${servlet.jar}"/> <pathelement path="web"/> The first entry adds all .jar files in the WEB-INF/lib folder. The second element adds the servlet.jar file. Finally, the third element specifies the directory that contains the WEB-INF directory. The use of StrutsTestCase for mock testing is extremely powerful. The underlying mock implementation allows your test to focus on the specific unit being tested. In addition, the tests can be run without requiring a running container with the application deployed. If you are in an environment where the container is running on a different server, likely even a different operating system, you can still unit test your actions on your local development computer. That being said, these unit tests do not take into account deployment issues, container dependencies, and other run-time behaviors that can only be tested with a running application. Fortunately, there is a tool that can help with this testing also.
services such as transactions and persistence are not easily mocked up. Another case where integration tests are invaluable is with regression testing. Deployments to different application servers and different operating systems can be verified using integrated unit tests. Cactus was developed to provide these types of unit tests. It was originally developed to test Enterprise JavaBeans but is equally up to the task of testing servlets, servlet filters, and JSPs. In fact, Cactus can be used to test any type of behavior that relies on a J2EE/servlet container. As will be shown later in this section, Struts actions can be tested through interaction with Struts' ActionServlet. However, these tests do come at a cost of increased complexity and slower performance. Cactus tests are fairly easy to write, but configuration and deployment can be complex. If you are running your unit tests frequently as part of your build process, you may find that your build takes far too long. The reason Cactus tests take longer is that Cactus starts your application server every time it runs its suite of tests. A good option is to only run Cactus periodically, such as on nightly automated builds. Here are some guidelines to help you decide what type of testing to use and when: Concentrate on unit tests for the model, first. That is where most of the business logic is and that should be the focus. Use the StrutsTestCase with mock objects to test your Action classes. Tests using mock objects can be run much faster, and do not require a servlet container. Use Cactus testing for those classes that rely on container-provided services. For example, if you are using JNDI, or testing behavior based on container-managed security, you should use Cactus. In addition, the Cactus site itself includes an even-handed comparison of mock object testing to Cactus in-container tests. To get started with Cactus, you need to download the distribution. As of this writing, Cactus 1.5 was the latest release build. Cactus can run tests via integration with your IDE; however, the preferred approach is via Ant. First, you need to define the task definitions for the Ant tasks that Cactus provides: <path id="cactus.classpath"> <fileset dir="cactus/lib"> <include name="*.jar"/> </fileset> </path> <taskdef resource="cactus.tasks" classpathref="cactus.classpath"/> This defines a path containing the Cactus .jar files and creates the Cactus task definitions. Next, add the Cactus classpath to the classpath of the compile target. Use cactus .classpath as the reference ID for the path element. As stated, Cactus tests will make the build slower; therefore, create a separate test.cactus target. In addition, provide a test wrapper target for running both test sets. The test.cactus target performs the following: 1. Instruments the application's .war file for testing by creating a new .war file. This process is referred to as cactifying the .war file. 2. Runs the unit tests. This is done using the cactus task. The containerset element indicates the container to run the tests against. This task extends the junit task. It uses the same subelements for specifying the results format and the set of tests to run. 3. Creates an HTML report of the results. This is done using the junitreport task that was shown previously. Now the test case needs to be written. Cactus is an extension of JUnit. Cactus provides base classes that extend junit.framework.TestCase. If you have written a JUnit test, Cactus tests will be familiar. However, since Cactus runs on both the client and server, there are some additional APIs that are of use. Before delving into test code, a review of how Cactus works is in order.
One of the most important aspects of Cactus to understand is that a Cactus test runs in two JVMs. Specifically, there is a copy of the test on the client JVM, and a copy on the server JVM (that is, the JVM running the J2EE/servlet container). From Figure 20-2 you can see that Cactus provides hooks on both the server and client side. The server-side hooks are the standard JUnit fixture methods-setUp( ) and tearDown( ). These methods are run, on the server, before and after execution of each testYourMethod( ) method. The begin( ) and end( ) methods are unique to Cactus. These methods are executed on the client side-before the request is sent, and after the response is received. Since there are two copies of the test, instance variables set in the begin( ) method are not set on the server side. Likewise, variables set in setUp( ) and tearDown( ) are not available in the client-side begin( ) and end( ) methods.
Figure 20-2: Cactus architecture These optional methods can be used to simulate the request and interrogate the response. Think of these methods as the browser side of the test. To make things concrete, the SearchActionTest class, implemented before as a MockStrutsTestCase, is reimplemented in the following code as a Cactus ServletTestCase. As mentioned, Cactus does not provide a direct way of testing a Struts action, as StrutsTestCase does. Struts actions are tested indirectly by interacting with the ActionServlet. Here is the Cactus unit test for testing the SearchAction class: package com.jamesholmes.minihr; import org.apache.cactus.ServletTestCase; import org.apache.cactus.WebRequest; import org.apache.struts.action.ActionServlet; public class SearchActionCactusTest extends ServletTestCase { private ActionServlet servlet; public SearchActionCactusTest(String theName) { super(theName); } public void setUp() throws Exception { servlet = new ActionServlet(); servlet.init(config); }
public void beginSuccess(WebRequest request) { request.setURL(null, // server name (e.g. 'localhost:8080') null, // context (e.g. '/MiniHR20') "/search.do", // servlet path null, "name=Jim"); } public void testSuccess() throws Exception { servlet.doGet(request, response); SearchForm form request.getAttribute("searchForm"); assertNotNull(form.getResults()); } public void tearDown() throws Exception { servlet.destroy(); } } There are key differences between this test and the previous mock test. First, notice that the setUp() method creates an instance of the Struts ActionServlet itself. Then, in the beginSuccess( ) method, the URL of the request object is set to the page to access and the query string. The SearchAction class is executed indirectly by calling the doGet( ) method of the ActionServlet. Essentially, the test is emulating the behavior of the container. Also notice that the request path must include the .do extension-Cactus knows nothing about Struts or the ActionServlet's mapping. Finally, in the testSuccess( ) method, the request is queried for the form. Note that the attribute name 'searchForm' had to be hard-coded into the test. Using StrutsTestCase, this information was pulled from the struts-config.xml file. Simply changing the name of the form in the struts-config.xml file will break this Cactus test. As you might suspect, these tests can become rather brittle. The tests are dependent on configuration settings that are outside the scope of the test. It would be preferable if there were a base class that could get configuration information from Struts. Fortunately, StrutsTestCase provides just that. = (SearchForm) // extra path info // query string
Figure 20-3: JUnit/StrutsTestCase/Cactus relationships A MockStrutsTestCase can easily be turned into a container-driven Cactus test case by simply changing the class it extends to the CactusStrutsTestCase. You may find that you will want to have both forms of these tests. In fact, you could probably refactor the common test behavior (that is, the setup, assertions, and verifications) into a common class that is used by both the mock test and the in-container test. The previous StrutsTestCase can be changed to run under Cactus by simply changing it to extend from CactusStrutsTestCasethe rest is the same. package com.jamesholmes.minihr; import servletunit.struts.CactusStrutsTestCase; public class SearchActionStrutsTest extends CactusStrutsTestCase { public SearchActionStrutsTest(String testName) { super(testName); } public void setUp() throws Exception { super.setUp(); setRequestPathInfo("/search"); } public void testSearchByName() throws Exception { addRequestParameter("name","Jim"); actionPerform();
assertNotNull("Results are null", ((SearchForm) getActionForm()).getResults()); verifyInputForward(); } } As you can see, the code is much simpler and easier to maintain than the same test written using the Cactus framework alone. Moreover, because it extends the Cactus classes, the test can easily be run as an in-container test that more closely tests actual deployment. Now that you know how to test actions both in and out of the container, you should consider the additional verifications that StrutsTestCase provides. Both MockStrutsTestCase and CactusStrutsTestCase extend TestCase and therefore offer all the standard assertion methods that are available with the JUnit API. In addition, several Struts-specific assertions are available in both of the StrutsTestCase base classes. All of these methods begin with verify: verifyActionErrors( ) Verifies that a specific set of error messages, identified by key, were sent verifyNoActionErrors( ) Verifies that no errors were sent verifyActionMessages( ) Verifies that a specific set of action messages, identified by key, were sent verifyNoActionMessages( ) Verifies that no action messages were sent verifyForward( ) Verifies that the controller forwarded to a specified logical forward name, global or local, as specified in the Struts configuration file verifyForwardPath( ) Verifies that the controller forwarded to a specified absolute path verifyInputForward( ) Verifies that the controller forwarded to the path identified by the action mappings input attribute verifyTilesForward( ) Verifies that the controller forwarded to a specified logical forward name from the Struts configuration and a Tiles definition name from the Tiles configuration verifyInputTilesForward( ) Verifies that the controller forwarded to the defined input of a specified Tiles definition Before leaving the subject of in-container tests, it is worth mentioning that Cactus also provides a base class for testing servlet filters. This could be used, for example, to test the authorization filter used in Chapter 19.
testing of Web sites. It provides Java APIs for accessing all aspects of the HTTP interaction. HttpUnit refers to this as the Web conversation. One aspect of Cactus that has not been demonstrated is use of the WebResponse object provided by the endTest( ) method. Cactus supports two versions of this method. The first method provides access to a simple object, org.apache.cactus .WebResponse, which represents the HTTP response. With this object you can check the HTTP response status code and headers, check cookies, and assert actions based on the content. The content of the response is returned as a simple string. For a complex Web site, the returned content could be quite large. The other accepted version of the endTest( ) method receives an HttpUnit WebResponse object, com.meterware .httpunit.WebResponse. Contrary to the Cactus WebResponse, this object provides a rich API for accessing the HTML content of the returned response. Through this integration with HttpUnit, Cactus tests can navigate through the response in an object-oriented fashion, verifying that the response contains specific HTML details such as HTML tables, links, and forms. Consider a unit test for the index.jsp page of the Mini HR application. The dynamic aspects of this page are conditioned on the presence of a User object in the session. An additional link is rendered if the user is an administrator, enabling the administrator to add an employee. A unit test for this JSP allows for these assertions. Here is a Cactus unit test that includes a test method that performs these assertions. This test allows the JSP to be tested in isolation. package com.jamesholmes.minihr; import org.apache.cactus.JspTestCase; import com.jamesholmes.minihr.security.User; import com.meterware.httpunit.WebLink; import com.meterware.httpunit.WebResponse; public class IndexJspTest extends JspTestCase { public IndexJspTest(String theName) { super(theName); } public void testAdministratorAccess() throws Exception { User user = new User("bsiggelkow","Bill", "Siggelkow", "thatsme", new String[] {"administrator"}); session.setAttribute("user", user); pageContext.forward("/index.jsp"); } public void endAdministratorAccess(WebResponse response) throws Exception { // verify that the login form is not displayed assertNull("Login form should not have rendered", response.getFormWithName("loginForm")); //verify that the proper links are present WebLink[] links = response.getLinks(); assertTrue("First link is admin/add.jsp", links[0].getURLString().startsWith("/test/admin/add.jsp"));
assertTrue("Second link is search.jsp", links[1].getURLString().startsWith("/test/search.jsp")); } } The first thing you should notice is that the test imports the HttpUnit classes. HttpUnit is bundled with Cactus. (You may also want to get the latest HttpUnit distribution and documentation at http://httpunit.sourceforge.net.) Note also that the class extends JspTestCase instead of ServletTestCase. JspTestCase, which inherits from ServletTestCase, allows a JSP to be tested in isolation. The JSP page context can be accessed by the test class as needed. The test method was named to indicate that it is testing the display of the index page for a logged-in user with administrative privileges. Other conditions should also be modeled as test methods as appropriate. The first thing that the testAdministratorAccess( ) method does is instantiate a User object that is assigned the administrator role. This object is placed in the HttpSession under the name by which it will be accessed on the page. The page is then rendered by forwarding to the index.jsp page. The implicit PageContext object performs the job of forwarding the request. The actual test assertions and verifications are now performed in the endAdministratorAccess( ) method. Remember that this method is executed in the clientside JVM after the request is processed. If a User object is in the session, the login form should not be displayed. The first assertion verifies this by asserting that the response does not contain a form named loginForm. Next, the set of links contained in the response is retrieved. Assertions are made that the first link is to the Add an Employee page (/admin/add.jsp), and that the second link is to the Search page (search.jsp). Initially, these assertions were written using the assertEquals( ) method as follows: assertEquals("Check first link", "/test/admin/add.jsp", links[0].getURLString()); At first glance, this appears correct; however, if you run the test using this assertion, it most likely will fail, because the servlet container may append a session identifier string to the link. In this case, the actual value of the URL was the following: /test/admin/add.jsp;jsessionid=0E5EFB6F64C01749EE94E3A57BDEBD21 Therefore, the assertion was changed to verify that the URL starts with the expected result. While the tests just demonstrated certainly are useful, they are getting more into the realm of functional testing than unit testing. HttpUnit can be used for broader functional testing-not just unit testing JSPs. HttpUnit provides full support for the entire Web conversation. The API provides methods for constructing requests, following links, and populating and submitting forms. Tests can be written using this API that mimic expected usage scenarios of the Web application. However, the amount of code to support such a test can be extensive. Also, this code can be extremely sensitive to simple changes in presentation. In the following section, a better approach for this type of functional testing is presented.
Use-Case-Driven Testing
The classical view of testing, from a quality control perspective, is use-case driven. That is, test cases are derived from use cases. For example, the following is a use case for the Mini HR 'Search by Name' feature. Precondition: The user has browsed to the Mini HR main page.
Primary Flow: 1. Click the Search link on the main page. 2. On the Search page, choose to Search by Name. 3. Enter all or part of an employee's name. 4. Submit the form.If a username was not entered, a validation error is displayed. 5. All employees that match the input name are displayed on the page. For each user, the employee name and social security number are listed.If no employees match, a message is displayed indicating that no matches exist. This use case is straightforward and easy to understand. It is complete in that it specifies what must happen prior to the scenario (preconditions), all steps that the user must take, and the results of those steps.
application was deployed to the QA server. The next section demonstrates a tool for automating these types of tests.
passing the build filename using the -buildfile testfile.xml (or -f testfile.xml) option. Likewise, if your IDE provides Ant integration (as most IDEs do), you can run the tests from your IDE in the same manner that you run an Ant build script. <?xml version="1.0"?> <!DOCTYPE project SYSTEM "WebTest.dtd"[ <!ENTITY definition SYSTEM "includes/definition.xml"> <!ENTITY config SYSTEM "includes/config.xml"> SYSTEM "includes/getSearchPage.xml">]> <!ENTITY getSearchPage
<project name="MiniHR20-SearchTest" basedir="." default="all"> <property name="webtest.home" value="../../canoo"/> &definition; <target name="all" nameSearchNoResults"/> depends="nameSearchWithResults,
<target name="nameSearchWithResults"> <testSpec name="Perform a Search by Name"> &config; <steps> &getSearchPage; <setinputfield stepid="Enter 'Jim' into Name field" name="name" value="Jim"/> <clickbutton stepid="Submit the 'search by' form" label="Submit"/> <verifyxpath stepid="Verify xpath 'Jim Smith'" xpath="/html/body/table/tr/td[1]" text="[Jj][Ii][Mm]" regex="true"/> </steps> </testSpec> </target> <target name="nameSearchNoResults"> <testSpec name="Perform a Search by Name with No Results"> &config; <steps> &getSearchPage; <setinputfield stepid="Enter 'Morty' into Name field" name="name" value="Morty"/> <clickbutton stepid="Submit the 'search by' form" label="Submit"/> <verifytext stepid="Verify 'No Employees Found'" text="No Employees Found"/> </steps> </testSpec> </target> </project>
As you can see in the DOCTYPE declaration, this test makes extensive use of XML entities to bring in shared XML fragments. The first entity, definition, defines an entity that contains the task definitions for Canoo. includes/definition.xml: <taskdef file="${webtest.home}/webtestTaskdefs.properties"> <classpath> <pathelement path="${webtest.home}/lib"/> <fileset dir="${webtest.home}" includes="**/lib/*.jar"/> </classpath> </taskdef> This fragment is generic to most any use of Canoo WebTest. The second entity, config, is specific to the application being tested. In this case, the XML fragment defines the config element specifically for accessing the Mini HR application running on Tomcat. includes/config.xml: <config host="" port="8080" basepath="/MiniHR20" summary="false" verbose="true" saveresponse="false" haltonfailure="true" protocol="http"/> You can reuse this element for any test specification of your application. The final entity, getSearchPage, is specific to this test set. It defines the steps that are necessary to get the Search page. These steps include accessing the application's main page, clicking the Search link, and then verifying that the Search page is displayed. While the steps could have been duplicated for each target, creating the entity allows reuse of this functionality for additional tests that need to get the Search page. Those tests may be added to this set of specifications or to new sets of specifications. includes/getSearchPage.xml: <invoke stepid="Get main page" url="/index.jsp" /> <verifytitle stepid="Check that title is correct" text="ABC, Inc. Human Resources Portal"/> <clicklink stepid="Click Search for Employees link" label="Search for Employees"/> <verifytext stepid="Find 'Employee Search' text" text="Employee Search"/> The first step of this fragment, Get main page, uses the invoke task to access the page of the application. The URL is relative to the settings in the config element. Next, the title of the displayed page is verified. Following that, the Search link is clicked. Finally, the text 'Employee Search' is searched for and verified. This XML fragment is used in both of the test targets. The test case to Search by Name uses these entities. First, the config entity is referenced to indicate the basic configuration. Next, the getSearchPage entity is used. At this point in the test, the Search page should be displayed. Next, the setinputfield element is used to emulate a user typing 'Jim' into the name field. The clickbutton task is then used to submit the form. The last step is the most interesting. Canoo allows you to use XPath expressions to identify and verify portions of the expected response. In this case, the XPath attribute of the verifyxpath task refers to the first table data cell of the first row in the table. The text attribute
indicates what the expected value should be. The text attribute can be either normal text to search for or a regular expression. Here, a regular expression is used to indicate that the table data cell referenced by the XPath expression should contain the text 'JIM' regardless of case. The second test target, nameSearchNoResults, verifies proper behavior when the search results in no matches for the entered name. This target reuses the getSearchPage steps. Also, it uses the simpler verifytext task to check if the resulting response contains the expected message-No Employees Found. As you can see, Canoo WebTest is very powerful. Test cases map to automated test specifications in a natural and easy-to-understand fashion. The test specification tasks are well documented and can be written by nondevelopers. WebTest also provides the capability to generate test reports for a test specification. These reports, generated in XML, can then be rendered in HTML using an XSLT stylesheet. Check the Canoo WebTest samples for examples of how to utilize this functionality. Finally, the use of Ant as the underlying framework means easier integration with cross-platform build processes. The one aspect that WebTest does not easily provide verifications for is presentation and layout. These verifications can be made using XPath; however, wholesale use of XPath to verify the presentation of a page can get extensive and difficult to maintain. Depending on the size of your application, you may want to consider a testing tool that provides record and playback capability. Such tools are usually not open source or free; however, they are usually quite powerful and may be necessary for large sites- particularly if those sites use a lot of JavaScript and other Dynamic HTML techniques.
< Day Day Up >
This document is created from a CHM file automatically by an unregistered copy of CHM-2-Word. The content of this chapter is skipped, please register CHM-2-Word to get full features. For registration information, please refer to: http://www.macrobject.com
Timed tests Measure the amount of time that a test takes. Assertions can be made about the maximum amount of time that the test should take. If the test takes longer than that, then the assertion fails. Load tests Allow the test writer to indicate the number of concurrent users (threads) that are running the test and optionally the number of times to execute the test.
To start, the following code defines a timed test for the simple JUnit EmployeeSearchServiceTest class that was created earlier in the Testing the Model section. Of course, the test execution should be integrated into the Ant build.
package com.jamesholmes.minihr; import com.clarkware.junitperf.TimedTest; import junit.framework.Test; import junit.framework.TestSuite; public class EmployeeSearchServicePerfTest { public static final long toleranceInMillis = 10; public static Test suite() { long maxElapsedTimeInMillis = 10 + toleranceInMillis; Test timedTest = new TimedTest( new TestSuite( EmployeeSearchServiceTest.class ), maxElapsedTimeInMillis); TestSuite suite = new TestSuite(); suite.addTest(timedTest); return suite; } }
As you can see, this class is somewhat atypical of previous unit tests in that it does not extend TestCase. It does, however, provide a suite method that returns a Test. This method creates a test suite of a JUnitPerf TimedTest by wrapping a test suite created from the original EmployeeSearchServiceTest. The maximum time that the test should take to run is passed in as the second parameter to the TimedTest. This test can be run just as any other JUnit testthat is, using the JUnit test runners, IDE/JUnit integration, or via an Ant task. Another tool, in the open-source domain, for testing and measuring the performance of Web applications is Apache JMeter (http://jakarta.apache.org/jmeter). JMeter allows a developer to create and run test plans against running applications. It was originally designed for testing Web sites so it is well suited for most Struts applications. JMeter provides a Java Swing user interface for creating and running test plans. Performance metrics are gathered and can be visualized graphically as well as in other formats. To get started, you need to first download JMeter and install it. Follow the provided documentation to create a test plan. You can create thread groups that simulate a number of concurrent users. Then, you can specify the details of an HTTP request to test. Figure 20-4 shows an HTTP request configured to access the Search by Name feature of the Mini HR application.
Figure 20-4: JMeter HTTP request The request is configured to access the URL http://localhost:8080/MiniHR20/ search.do. In addition, request parameters can be specified. In this case, the query string name=Jim will be added to the request. The test can then be run against the running server and the results are accumulated for each result listener. Figure 20-5 shows the display using the Graph Results listener. The thread group, in this case, was configured to run with 20 concurrent users, each making 25 requests to the Search pagea total of 500 data points.
Figure 20-5: JMeter Graph Results From the results, you can see that on average the search could be performed in a bit over 50 milliseconds, with some requests taking as long as 120 milliseconds. JMeter is a powerful tool and has a great deal more capabilities than described here. It is well documented and has many additional features that you may want to use. Performance is an area of your application that should not be overlooked particularly if the application is targeted for high-volume, high-performance Web sites. As was shown above, JUnitPerf and Apache JMeter can be used to test the
performance of your Web application under simulated loads. These tools can be used to help identify those areas of your application that are bottlenecks. Once identified, profiling tools should be used to focus the performance analysis.
Part V: Appendix
Chapter List
Appendix: Struts Console Quick Reference
can be run on any platform that supports Java GUI applications (e.g., Microsoft Windows, Macintosh, Linux, and so on). In its original release, Struts Console could be run only as a stand-alone application. Subsequently, Struts Console has been updated to support being run as a plugin in most of the major Java IDEs. Following is the list of IDEs that Struts Console can be used with: Borland JBuilder (versions 4.0 and later) Eclipse (versions 1.0 and later) IBM WebSphere Studio Application Developer (WSAD) (versions 4.0.3 and later) IntelliJ IDEA (versions 3.0 build 668 and later) NetBeans (versions 3.2 and later) Sun Java Studio (previously known as Forte or Sun ONE Studio) (versions 3.0 and later) Oracle JDeveloper (versions 9i and later) The following sections explain how to acquire, install, and use Struts Console, both as a stand-alone application and as a plugin inside all the supported IDEs. Because IDEs are continually evolving, some of the instructions for using Struts Console inside the IDEs may change over time. If any of the instructions found in this appendix do not work for you, visit my Web site at http://www.jamesholmes.com/struts/ for the most up to date instructions.
Most often, Struts Console is used as an IDE plugin because of the convenience of working seamlessly inside one tool. However, Struts Console can just as easily be run as a standalone application. Before running Struts Console as a stand-alone application, you must set the JAVA_HOME environmental variable. Many JDKs set this for you when you install them; however, if it is not set on your machine, you have to set it before you run Struts Console. The JAVA_HOME variable must be set to the directory where you have your JDK installed. For example, if you have JDK 1.4.1_03 installed at c:\java\j2sdk1.4.1_03, your JAVA_HOME variable should be set to c:\java\ j2sdk1.4.1_03. Once you have set the JAVA_HOME environmental variable, you are ready to run Struts Console. Simply navigate to the directory where you installed Struts Console (e.g., c:\java\struts-console-4.4) and then navigate into the bin directory. The bin directory contains two files: console.bat and console.sh. If you are on a Microsoft Windows machine, run console.bat. However, if you are on Linux, Unix, or Macintosh, run console.sh. Once Struts Console is up and running, you can open an existing configuration file or create a new configuration file for editing. Figure A-1 shows Struts Console in action as a stand-alone application. Notice that open files have an Editor and a Source tab at the bottom. The Source tab shows you the source XML for the configuration file. Unlike many of the Struts Console IDE plugins, you cannot edit the XML source directly. This, however, may be a new feature in the future.
Figure A-1: Struts Console in action as a stand-alone application Note On Linux, Unix, and Macintosh you have to set the executable flag on the console.sh file before it can be executed.
4.
Copy the xerces.jar file from the Struts Console lib directory (e.g., c:\java\ strutsconsole-4.4\lib) into the JBuilder lib directory (e.g., c:\Program Files\JBuilder\lib), if and only if xerces.jar does not already exist in the JBuilder lib directory. Note This step should only be necessary for JBuilder version 4.
5.
Navigate into the ext directory (e.g., c:\Program Files\JBuilder\lib\ext) from the JBuilder lib directory. 6. Copy the struts-console.jar file from the Struts Console lib directory into the JBuilder ext directory.
After you have installed the Struts Console JBuilder plugin, you must restart JBuilder. Once JBuilder is running, to use Struts Console, simply open a valid configuration file supported by Struts Console. Struts Console will be a tab option for the file, as shown in Figure A-2. You can edit the configuration file using Struts Console or you can edit the file by hand from the Source tab. Changes made in either tab are automatically reflected in the other tab.
Figure A-2: Struts Console inside JBuilder Struts Console also allows you to modify some of its configuration settings from inside JBuilder. To access the Struts Console configuration settings, select Tools | IDE Options. The following illustration shows the Struts Console IDE Options dialog box.
For more information on these configuration settings, see the 'Configuring the Struts Console Output Options' section later in this appendix.
Once Eclipse is running, to use Struts Console, simply right-click on a valid configuration file and select Open With | Struts Console, as shown here:
For Eclipse versions 3.0m7 and later, after you have opened the file, it will load into the Struts Console editor inside of Eclipse, as shown in Figure A-3. You can edit the configuration file using Struts Console or you can edit the file by hand from the Source tab. Changes made in either tab are automatically reflected in the other tab. For versions of Eclipse prior to 3.0m7, the file will load into a separate Struts Console window, as shown in Figure A-4.
Figure A-4: Struts Console editor in a separate window Note The Struts Console Eclipse plugin requires that your configuration files have specific filenames in order for the plugin to recognize them. Struts configuration files must be named struts-config.xml; Tiles configuration files must be named tiles.xml; and Validator configuration files must be named validation.xml. JSP TLD files only need to have a file extension of .tld. Struts Console also allows you to modify some of its configuration settings from inside Eclipse. To access the Struts Console configuration settings, select Window | Preferences. The following illustration shows the Struts Console Preferences dialog box.
For more information on these configuration settings, see the Configuring the Struts Console Output Options section later in this appendix.
Struts Console can be run as a plugin inside IBM WebSphere Studio Application Developer (WSAD) versions 4.0.3 and later. To do so, you first have to install the Struts Console Eclipse plugin. Following is the list of steps for installing the Struts Console WSAD plugin: 1. Shut down WSAD if it is currently running. 2. Navigate to the directory in which you have WSAD installed (e.g., c:\Program Files\IBM\WebSphere Studio) and then navigate into the eclipse directory and then into the plugins directory. 3. In another window, navigate to the directory in which you installed Struts Console (e.g., c:\java\struts-console-4.4). 4. Copy the com.jamesholmes.console.struts directory from the Struts Console installation directory into the WSAD plugins directory. After you have installed the Struts Console WSAD plugin, you must restart WSAD. Once WSAD is running, to use Struts Console, simply right-click on a valid configuration file and select Open With | Struts Console, as shown here:
After you have opened the file, it will load into a separate Struts Console window, as shown in Figure A-5.
Figure A-5: Struts Console editor in a separate window Note The Struts Console WSAD plugin requires that your configuration files have specific filenames in order for the plugin to recognize them. Struts configuration files must be named struts-config.xml; Tiles configuration files must be named tiles.xml; and Validator configuration files must be named validation.xml. JSP TLD files only need to have a file extension of .tld. Struts Console also allows you to modify some of its configuration settings from inside WSAD. To access the Struts Console configuration settings, select Window | Preferences. The following illustration shows the Struts Console Preferences dialog box.
For more information on these configuration settings, see the Configuring the Struts Console Output Options section later in this appendix.
After you have opened the file, it will load into the Struts Console editor, as shown in Figure A-6.
Figure A-6: Struts Console editor inside IDEA Struts Console also allows you to modify some of its configuration settings from inside IDEA. To access the Struts Console configuration settings, select Options | IDE Settings. Here is the Struts Console IDE Settings dialog box:
For more information on these configuration settings, see the 'Configuring the Struts Console Output Options' section later in this appendix.
3. Using the file chooser, navigate to the directory in which you installed Struts Console (e.g., c:\java\struts-console-4.4) and then navigate into the com.jamesholmes.console.struts directory and then into the lib directory. 4. Select the struts-console.jar file and then click the Install button, as shown here.
After you have installed the Struts Console NetBeans plugin, you must restart NetBeans. Once NetBeans is running, to use Struts Console, simply open a valid configuration file supported by Struts Console. The file will load into the Struts Console editor inside of NetBeans, as shown in Figure A-7.
3.
In another window, navigate to the directory in which you installed Struts Console (e.g., c:\java\struts-console-4.4) and then navigate into the com.jamesholmes.console.struts directory and then into the lib directory. 4. Copy the struts-console.jar file from the Struts Console lib directory into the JDeveloper ext directory.
After you have installed the Struts Console JDeveloper plugin, you must restart JDeveloper. Once JDeveloper is running, to use Struts Console, simply right-click on a valid configuration file and select Struts Console Editor, as shown here:
After you have opened the file, it will load into the Struts Console editor inside of JDeveloper, as shown in Figure A-8.
Figure A-8: Struts Console editor inside JDeveloper Struts Console also allows you to modify some of its configuration settings from inside JDeveloper. To access the Struts Console configuration settings, select Tools | Preferences. Here is the Struts Console Preferences dialog box:
For more information on these configuration settings, see the next section, 'Configuring the Struts Console Output Options.'
Following is an explanation of each of the output options: Enable Pretty Output Enables and disables the use of Pretty Output options. Newlines Specifies the type of newline character to use. System Default is the default and recommended setting. System Default defaults to the newline character for the given system on which the application is running. For example, if run on Windows, System Default uses Windows-style newline characters. Lines Between Elements Specifies the number of new lines to place in between tags. Indent Elements Specifies the number and type of indenting to use. Indenting is used for tags and is hierarchical. Each level in the tag hierarchy is a level of indenting. Attributes on New Lines Specifies whether a tag's attributes should each be placed on a new line or if each should be placed on the same line as the tag. Expand Empty Elements Specifies whether or not empty tags (that is, tags without nested tags) should be expanded (e.g., <tag/> versus <tag></tag>). Bottom pane Shows an example of how the output will look with the current output options applied. This changes as you change the output options. Note Enabling Pretty Output removes any XML comments (e.g., <!-- -->) and formatting from your original configuration files.
The Pretty Output options can be configured in the stand-alone version of Struts Console as well as from inside IDEs that Struts Console plugs into. (For information on how to access the configuration settings for the Pretty Output options in a supported IDE, see that IDE's section in this appendix.) To configure the Pretty Output options in the stand-alone version of Struts Console, select Options | Output. This opens the dialog box shown here with options for specifying the details of how XML files should be formatted when they are saved.
Enable Pretty Output Enables and disables the use of Pretty Output options. Newlines Specifies the type of newline character to use. System Default is the default and recommended setting. System Default defaults to the newline character for the given system on which the application is running. For example, if run on Windows, System Default uses Windows-style newline characters. Lines Between Elements Specifies the number of new lines to place in between tags. Indent Elements Specifies the number and type of indenting to use. Indenting is used for tags and is hierarchical. Each level in the tag hierarchy is a level of indenting. Attributes on New Lines Specifies whether a tag's attributes should each be placed on a new line or if each should be placed on the same line as the tag. Expand Empty Elements Specifies whether or not empty tags (that is, tags without nested tags) should be expanded (e.g., <tag/> versus <tag></tag>). Bottom pane Shows an example of how the output will look with the current output options applied. This changes as you change the output options. Note Enabling Pretty Output removes any XML comments (e.g., <!-- -->) and formatting from your original configuration files.