Struts User's Guide

$Id: users_guide.html,v 1.12 2001/01/10 07:35:18 craigmcc Exp $

0. Deprecation Note

ALL OF THE INFORMATION IN THIS DOCUMENT IS DEPRECATED, BECAUSE IT IS SPECIFIC TO STRUTS 0.5. BE SURE TO REFER TO THE STRUTS 1.0 VERSION OF THE USER'S GUIDE, WHICH IS AVAILABLE AS PART OF THE STRUTS 1.0 RELEASE DOCUMENTATION.

1. Introduction

1.1 The Model-View-Controller (MVC) Design Pattern

FIXME - Need a general introduction to the pattern.

1.2 Mapping MVC Concepts to Struts Components

The Struts architecture implements the concepts of the Model-View-Controller design pattern by mapping them to web application components and concepts, as outlined in the following diagram:



Each major component of this architecture will be discussed in more detail below.

1.3 The Model: System State and Business Logic JavaBeans

The Model portion of an MVC-based system can be subdivided into concepts -- the internal state of the system, and the actions that can be taken to change that state. In grammatical terms, we might think about state information as nouns (things) and actions as verbs (changes to the state of those things).

Generally, your application will represent the internal state of the system as a set of one or more JavaBeans, with properties that represent the details of the state. Depending on your application's complexity, these beans may be self contained (and know how to save their state information persistently somehow), or they may be facades that know how to retrieve information from external sources (such as a database) when it is requested. Entity Enterprise JavaBeans (EJBs) are also commonly used to represent internal state.

Large-scale applications will often represent the business logic actions that are possible in a system as methods that can be called on the beans maintaining the state information. For example, you might have a shopping cart bean, stored in session scope for each current user, with properties that represent the current set of items that the user has decided to purchase. This bean might also have a checkOut() method that authorizes the user's credit card, and sends the order to the warehouse to be picked and shipped. Other systems will represent the available actions separately, perhaps as Session Enterprise JavaBeans (EJBs).

In some smaller scale applications, on the other hand, the available actions might be embedded within the Action classes that are part of the Controller role. This is appropriate when the logic is very simple, or where reuse of the business logic in other environments is not contemplated. The Struts framework supports any of these approaches, but recommends separating the business logic ("what to do") from the role that Action classes play ("deciding what to do").

1.4 The View: JSP Pages and Presentation Components

The View portion of a Struts-based application is generally constructed using JavaServer Pages (JSP) technology. JSP pages can contain static HTML (or XML) text called "template text", plus the ability to insert dynamic content based on the interpretation (at page request time) of special action tags. The JSP environment includes a set of standard action tags, such as <jsp:useBean> whose purpose is described in the JavaServer Pages Specification. In addition, there is a standard facility to define your own tags, which are organized into "custom tag libraries."

Struts includes an extensive custom tag library that facilitates creating user interfaces that are fully internationalized, and that interact gracefully with ActionForm beans that are part of the Model portion of the system. The use of these tags is discussed in detail later.

In addition to JSP pages and the action and custom tags they contain, it is often necessary for business objects to be able to render themselves in HTML (or XML), based on their current state at request time. The rendered output from such objects can be easily included in a resulting JSP page by using the <jsp:include> standard action tag.

1.5 The Controller: ActionServlet and ActionMapping

The Controller portion of the application is focused on receiving requests from the client (typically a user running a web browser), deciding what business logic function is to be performed, and then delegating responsibility for producing the next phase of the user interface to an appropriate View component. In Struts, the primary component of the controller is a servlet of class ActionServlet. This servlet is configured by defining a set of mappings (described by a Java interface ActionMapping). Each mapping defines a path that is matched against the request URI of the incoming request, and the fully qualified class name of an Action class (that is, a Java class implementing the Action interface) which is responsible for performing the desired business logic, and then dispatching control to the appropriate View component to create the response.

Struts also supports the ability to use ActionMapping classes that have additional properties beyond the standard ones required to operate the framework. This allows you to store additional information specific to your application, but still utilize the remaining features of the framework. In addition, Struts lets you define logical "names" to which control should be forwarded so that an action method can ask for the "Main Menu" page (for example), without knowing what the actual name of the corresponding JSP page is. These features greatly assist you in separating the control logic (what do I do next) with the view logic (what is the name of the corresponding page).

2. Building the Model Components

2.1 Overview

The application requirements document that you are using will likely have focused on the user interface to be created. However, you should ensure that the processing required for each submitted request is also clearly defined. In general, the developer of the Model components will be focusing on the creation of JavaBeans classes that support all of the functional requirements. The precise nature of the beans required by a particular application will vary widely depending on those requirements, but they can generally be classified into several categories discussed below. However, a brief review of the concept of "scope" as it relates to beans is useful first.

2.2 JavaBeans and Scope

Within a web-based application, JavaBeans can be stored in (and accessed from) a number of different collections of "attributes". Each collection has different rules for the lifetime of that collection, and the visibility of the beans stored there. Together, the rules defining lifetime and visibility are called the scope of those beans. The JavaServer Pages (JSP) Specification defines scope choices using the following terms (with the equivalent servlet API concept defined in parentheses):

It is important to remember that JSP pages and servlets in the same web application share the same sets of bean collections. For example, a bean stored as a request attribute in a servlet like this:

    MyCart mycart = new MyCart(...);
    request.setAttribute("cart", mycart);

is immediately visible to a JSP page which this servlet forwards to, using a standard action tag like this:

    <jsp:useBean id="cart" scope="request"
     class="com.mycompany.MyApp.MyCart"/>

2.3 ActionForm Beans

The Struts framework generally assumes that you have created an ActionForm bean (that is, a Java class implementing the ActionForm interface) for each input form required in your application. If you define such beans in your ActionMapping configuration file (see "Building the Controller Components"), the Struts controller servlet will automatically perform the following services for you, before invoking the appropriate Action method:

When you code your ActionForm beans, keep the following principles in mind:

You should note that a "form", in the sense discussed here, does not necessarily correspond to a single JSP page in the user interface. It is common in many applications to have a "form" (from the user's perspective) that extends over multiple pages. Think, for example, of the wizard style user interface that is commonly used when installing new applications. Struts encourages you to define a single ActionForm bean that contains properties for all of the fields, no matter which page the field is actually displayed on. Likewise, the various pages of the same form should all be submitted to the same Action Class. If you follow these suggestions, the page designers can rearrange the fields among the various pages, with no changes required to the processing logic in most cases.

2.4 System State Beans

The actual state of a system is normally represented as a set of one or more JavaBeans classes, whose properties define the current state. A shopping cart system, for example, will include a bean that represents the cart being maintained for each individual shopper, and will (among other things) include the set of items that the shopper has currently selected for purchase. Separately, the system will also include different beans for the user's profile information (including their credit card and ship-to addresses), as well as the catalog of available items and their current inventory levels.

For small scale systems, or for state information that need not be kept for a long period of time, a set of system state beans may contain all the knowledge that the system ever has of these particular details. Or, as is often the case, the system state beans will represent information that is stored permanently in some external database (such as a CustomerBean object that corresponds to a particular row in the CUSTOMERS table), and are created or removed from the server's memory as needed. Entity Enterprise JavaBeans are also used for this purpose in large scale applications.

2.5 Business Logic Beans

You should encapsulate the functional logic of your application as method calls on JavaBeans designed for this purpose. These methods may be part of the same classes used for the system state beans, or they may be in separate classes dedicated to performing the logic. In the latter case, you will usually need to pass the system state beans to be manipulated to these methods as arguments.

For maximum code re-use, business logic beans should be designed and implemented so that they do not know they are being executed in a web application environment. If you find yourself having to import a javax.servlet.* class in your bean, you are tying this business logic to the web application environment. Consider rearranging things so that your Action classes (part of the Controller role, as described below) translate all required information from the HTTP request being processed into property setter calls on your business logic beans, after which a call to an execute() method can be made. Such a business logic class can be reused in environments other than the web application for which they were initially constructed.

Depending on the complexity and scope of your application, business logic beans might be ordinary JavaBeans that interact with system state beans passed as arguments, or ordinary JavaBeans that access a database using JDBC calls. For larger applications, these beans will often be stateful or stateless Enterprise JavaBeans (EJBs) instead.

2.6 An Aside: Accessing Relational Databases

Many web applications will utilize a relational database (accessed through a JDBC driver) to store the persistent data associated with the application. Others will use Entity Enterprise JavaBeans for this purpose, which delegates the decisions for how to maintain persistent state to the EJBs themselves. If you are using Enterprise JavaBeans for this purpose, follow the client design patterns described in the Enterprise JavaBeans specification.

For web applications based on direct database access, a common design problem is how to make an appropriate JDBC Connection object available when access to the underlying database is required. There are several possible approaches to this problem -- the following principles describe a recommended approach to this issue:

Following the design pattern recommended above means that you can code your business logic classes without having to worry about how they acquire a JDBC Connection to use -- simply include a Connection argument in any method call that needs to access the database. When your business logic class is utilized within a web application, it will be the responsibility of the Action class to allocate and release an appropriate connection. When you use the same business logic class, for example, in a batch job, it will be the responsibility of that application to provide an appropriate connection (which will not need to be acquired from a pool, because most batch jobs run in a single-threaded environment).

3. Building the View Components

3.1 Overview

This chapter focuses on the task of building the View components of the application, which will primarily be created using JavaServer Pages (JSP) technology. In particular, Struts provides support for building internationalized applications, as well as for interacting with input forms. Several other topics related to the View components are briefly discussed.

3.2 Internationalized Messages

A few years ago, application developers could count on having to support only residents of their own country, who are used to only one (or sometimes two) languages, and one way to represent numeric quantities like dates, numbers, and monetary values. However, the explosion of application development based on web technologies, as well as the deployment of such applications on the Internet and other broadly accessible networks, have rendered national boundaries invisible in many cases. This has translated (if you will pardon the pun) into a need for applications to support internationalization (often called "i18n" because 18 is the number of letters in between the "i" and the "n") and localization.

Struts builds upon the Java platform to provide assistance for building internationalized and localized applications. The key concepts to become familiar with are:

For an internationalized application, follow the steps described in the Internationalization document in the JDK documentation bundle for your platform to create a properties file containing the messages for each language. An example will illustrate this further.

Assume that your source code is created in package com.mycompany.mypackage, so it is stored in a directory (relative to your source directory) named com/mycompany/mypackage. To create a resource bundle called com.mycompany.mypackage.MyResources, you would create the following files in the com/mycompany/mypackage directory:

When you configue the controller servlet in the web application deployment descriptor, one of the things you will need to define in an initialization parameter is the base name of the resource bundle for the application. In the case described above, it would be com.mycompany.mypackage.MyResources.

3.3 Forms and FormBean Interactions

At one time or another, most web developers have built forms using the standard capabilities of HTML, such as the <input> tag. Users have come to expect interactive applications to have certain behaviors, and one of these expectations relates to error handling -- if the user makes an error, the application should allow them to fix just what needs to be changed -- without having to re-enter any of the rest of the information on the current page or form.

Fulfilling this expectation is tedious and cumbersome when coding with standard HTML and JSP pages. For example, an input element for a username field might look like this (in JSP)

	<input type="text" name="username"
	 value="<%= loginBean.getUsername() %>">
which is difficult to type correctly, confuses HTML developers who are not knowledgeable about programming concepts, and can cause problems with HTML editors. Instead, Struts provides a comprehensive facility for building forms, based on the Custom Tag Library facility of JSP 1.1. The case above would be rendered like this using Struts:
	<struts:text name="username"/>
with no need to explicitly refer to the JavaBean from which the initial value is retrieved. That is handled automatically by the framework.

3.3.1 Building Forms With Struts

A complete example of a login form will illustrate how Struts makes dealing with forms much less painful than using straight HTML and standard JSP facilities. Consider the following page (from the example application included with Struts) named logon.jsp:

  <%@ page language="java" %>
  <%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>

  <html>
  <head>
  <title><struts:message key="logon.title"/></title>
  <body bgcolor="white">

  <struts:errors/>

  <struts:form action="logon.do" name="logonForm"
   type="org.apache.struts.example.LogonForm"/>
  <table border="0" width="100%">
    <tr>
      <th align="right">
        <struts:message key="prompt.username"/>
      </th>
      <td align="left">
        <struts:text name="username" size="16"/>
      </td>
    </tr>
    <tr>
      <th align="right">
        <struts:message key="prompt.password"/>
      </th>
      <td align="left">
        <struts:password name="password" size="16"/>
      </td>
    </tr>
    <tr>
      <td align="right">
        <struts:submit>
          <struts:message key="button.submit"/>
        </struts:submit>
      </td>
      <td align="right">
        <struts:reset>
          <struts:message key="button.reset"/>
        </struts:reset>
      </td>
    </tr>
  </table>
  </struts:form>

  </body>
  </html>

The following items illustrate the key features of form handling in Struts, based on this example:

3.3.2 Input Field Types Supported

Struts defines tags for all of the following types of input fields, with hyperlinks to the corresponding reference information.

In every case, a field tag must be nested within a form tag, so that the field knows what bean to use for initializing displayed values.

3.3.3 Other Useful Presentation Tags

There are several other tags in the Struts library that are useful in creating user interfaces:

3.3.4 Automatic Form Validation

In addition to the form and bean interactions described above, Struts offers an additional facility if your bean knows how to validate the input fields it has received. To utilize this feature, have your bean class implement the ValidatingActionForm interface, rather than the ActionForm interface. A ValidatingActionForm adds an additional method signature:

    public String[] validate()

for a method that is called by the controller servlet after the bean properties have been populated, but before the corresponding action class's perform() method is invoked. The validate() method has the following options:

As mentioned earlier, this feature is entirely optional. If your form bean implements only the ActionForm interface, the controller servlet will assume that any required validation is done by the action class.

3.4 Other Presentation Techniques

Although the look and feel of your application can be completely constructed based on the standard capabilities of JSP and the Struts custom tag library, you should consider employing other techniques that will improve component reuse, reduce maintenance efforts, and/or reduce errors. Several options are discussed in the following sections.

3.4.1 Application-Specific Custom Tags

Beyond using the custom tags provided by the Struts library, it is easy to create tags that are specific to the application you are building, to assist in creating the user interface. The example application included with Struts illustrates this principle by creating the following tags unique to the implementation of this application:

The source code for these tags is in the src/example directory, in package org.apache.struts.example, along with the other Java classes that are used in this application.

3.4.2 Page Composition With Includes

Creating the entire presentation of a page in one JSP file (with custom tags and beans to access the required dynamic data) is a very common design approach, and was employed in the example application included with Struts. However, many applications require the display of multiple logically distinct portions of your application together on a single page.

For example, a portal application might have some or all of the following functional capabilities available on the portal's "home" page:

The development of the various segments of this site is easier if you can divide up the work, and assign different developers to the different segments. Then, you can use the include capability of JavaServer Pages technology to combine the results into a single result page. There are two types of include available, depending on when you want the combination of output to occur:

3.4.3 Image Rendering Components

Some applications require dynamically generated images, like the price charts on a stock reporting site. Two different approaches are commonly used to meet these requirements:

4. Building the Controller Components

4.1 Overview

Now that we understand how to construct the Model and View components of your application, it is time to focus on the Controller components. Struts includes a servlet that implements the primary function of mapping a request URI to an Action class. Therefore, your primary responsibilities related to the Controller are:

4.2 Action Classes

The Action interface defines a single method that must be implemented by an Action class, as follows:

    public ActionForward perform(ActionServlet servlet,
                                 ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
      throws IOException, ServletException;

The goal of an Action class is to process this request, and then to return an ActionForward object that identifies the JSP page (if any) to which control should be forwarded to generate the corresponding response. In the Model 2 design pattern, a typical Action class will implement the following logic in its perform() method:

Design issues to remember when coding Action classes include the following:

In addition, you will want to guard against Action classes that are too large. The easiest way for this to happen is to embed your functional logic in the Action class itself, rather than coding it in separate business logic beans. Besides making the Action class itself hard to understand and maintain, this approach also makes it harder to re-use the business logic code, because it is embedded inside a component (the Action class) that is tied to being executed in a web application environment.

The example application included with Struts stretches this design principle somewhat, because the business logic itself is embedded in the Action classes. This should be considered something of a bug in the design of the sample application, rather than an intrinsic feature of the Struts architecture, or an approach to be emulated.

4.3 The ActionMapping Implementation

In order to operate successfully, the Struts controller servlet needs to know several things about how each request URI should be mapped to an appropriate Action class. The required knowledge has been encapsulated in a Java interface named ActionMapping, which has the following properties:

Struts includes a convenient implementation of the ActionMapping interface, in a class named ActionMappingBase. If you do not need to define any additional properties for your own mappings, feel free to use this class as your ActionMapping class, configured as described in the following section. However, it is also possible to define an ActionMapping implementation (perhaps extending the ActionMappingBase class) that contains additional properties. The controller servlet knows how to configure these custom properties automatically, because it uses the Struts Digester module to read the configuration file.

In the example application included with Struts, this feature is used to define two additional properties:

Using these two extra properties, the Action classes in the example application are almost totally independent of the actual names of the JSP pages that are used by the page designers. The pages can be renamed (for example) during a redesign, with negligible impact on the Action classes themselves. If the names of the "next" JSP pages were hard coded into the Action classes, all of these classes would also need to be modified.

4.4 The Action Mappings Configuration File

How does the controller servlet learn about the mappings you want? It would be possible (but tedious) to write a small Java class that simply instantiated new ActionMapping instances, and called all of the appropriate setter methods. To make this process easier, Struts includes a Digester module that is capable of reading an XML-based description of the desired mappings, creating the appropriate objects along the way. See the API documentation for more information about Digester.

The developer's responsibility is to create an XML file named action.xml, and place it in the WEB-INF directory of your application. (Note that no DTD for this file is required, because the actual attributes used can be different for different users.) The outermost XML element must be <action-mappings>, and within this element are nested zero or more <action> elements -- one per mapping that you wish to define.

The action.xml file from the example application includes the following mapping entry for the "log on" function, which we will use to illustrate the requirements:

  <action-mappings>

    <forward name="logon" path="/logon.jsp"/>

    <action   path="/logon"
       actionClass="org.apache.struts.example.LogonAction"
     formAttribute="logonForm"
         formClass="org.apache.struts.example.LogonForm"
         inputForm="/logon.jsp">
      <forward name="success" path="/mainMenu.jsp"/>
    </action>

  </action-mappings>

As you can see, this mapping matches the path /logon (actually, because the example application uses extension mapping, the request URI you specify in a JSP page would end in /logon.do). When a request that matches this path is received, an instance of the LogonAction class will be created (the first time only) and used. The controller servlet will look for a session scoped bean under key logonForm, creating and saving a bean of the specified class if needed.

This action element also defines a logical name "success", which is used within the LogonAction class to identify the page to be used when a user successfully logs on. Using a logical name like this allows the action class to be insulated from any changes in the page names that might occur due to a site redesign.

There is a second forward element that is declared outside of any action, so it is globally available to all actions. In this case, it defines a logical name for the logon page. In your action code, when you call mapping.findForward(), Struts first looks at the locally defined logical names for this action. If it does not find one, it then searches the global definitions for you automatically.

4.5 The Web Application Deployment Descriptor

The final step in setting up the application is to configure the application deployment descriptor (stored in file WEB-INF/web.xml) to include all the Struts components that are required. Using the deployment descriptor for the example application as a guide, we see that the following entries need to be created or modified.

4.5.1 Configure the Action Servlet Instance

Add an entry defining the action servlet itself, along with the appropriate initialization parameters. Such an entry might look like this:

    <servlet>
      <servlet-name>action</servlet-name>
      <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
      <init-param>
        <param-name>application</param-name>
        <param-value>org.apache.struts.example.ApplicationResources</param-value>
      </init-param>
      <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/action.xml</param-value>
      </init-param>
      <init-param>
        <param-name>debug</param-name>
        <param-value>2</param-value>
      </init-param>
      <init-param>
        <param-name>mapping</param-name>
        <param-value>org.apache.struts.example.ApplicationMapping</param-value>
      </init-param>
      <load-on-startup>2</load-on-startup>
    </servlet>

The initialization parameters supported by the controller servlet are described below, which is copied from the Javadocs for the ActionServlet class. Square brackets describe the default values that are assumed if you do not provide a value for that initialization parameter.

4.5.2 Configure the Action Servlet Mapping

There are two common approaches to defining the URLs that will be processed by the controller servlet -- prefix matching and extension matching. An appropriate mapping entry for each approach will be described below.

Prefix matching means that you want all URLs that start (after the context path part) with a particular value to be passed to this servlet. Such an entry might look like this:

    <servlet-mapping>
      <servlet-name>action</servlet-name>
      <url-pattern>/execute/*</url-pattern>
    </servlet-mapping>

which means that a request URI to match the /logon path described earlier might look like this:

    http://www.mycompany.com/myapplication/execute/logon

where /myapplication is the context path under which your application is deployed.

Extension mapping, on the other hand, matches request URIs to the action servlet based on the fact that the URI ends with a period followed by a defined set of characters. For example, the JSP processing servlet is mapped to the *.jsp pattern so that it is called to process every JSP page that is requested. To use the *.do extension (which implies "do something"), the mapping entry would look like this:

    <servlet-mapping>
      <servlet-name>action</servlet-name>
      <url-pattern>*.do</url-pattern>
    </servlet-mapping>

and a request URI to match the /logon path described earlier might look like this:

    http://www.mycompany.com/myapplication/logon.do

4.5.3 Configure the Struts Tag Library

Next, you must add an entry defining the Struts tag library. The entry should look like this:

  <taglib>
    <taglib-uri>/WEB-INF/struts.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts.tld</taglib-location>
  </taglib>

which tells the JSP system where to find the tag library descriptor for this library (in your application's WEB-INF directory, instead of out on the Internet somewhere).

4.5.4 Add Struts Components To Your Application

To use Struts when your application executes, you must copy the struts.tld file to your WEB-INF directory, and the struts.jar file to your WEB-INF/lib directory.