Struts User's Guide
Quick Links
Home
Table of Contents
Introduction
Model Components
View Components
Controller Components
Resources
Who We Are
4. Building 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:

  • Write an Action class for each logical request that may be received (extend org.apache.action.Action).
  • Configure a ActionMapping (in XML) for each logical request that may be submitted. The XML configuration file is usually named struts-config.xml.
  • Update the web application deployment descriptor file (in XML) for your application to include the necessary Struts components.
  • Add the appropriate Struts components to your application.
4.2 Action Classes

The Action class defines two methods that could be executed depending on your servlet environment:

public ActionForward perform(ActionMapping mapping,
                             ActionForm form,
                             ServletRequest request,
                             ServletResponse response)
  throws IOException, ServletException;

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

Most projects would only use the "HttpServletRequest" version.

The goal of an Action class is to process a request, via its perform() method, and return an ActionForward object that identifies where control should be forwarded (e.g. a JSP) to provide the appropriate response. In the MVC/Model 2 design pattern, a typical Action class will often implement logic like the following in its perform() method:

  • Validate the current state of the user's session (for example, checking that the user has successfully logged on). If the Action class finds that no logon exists, the request can be forwarded to the JSP page that displays the username and password prompts for logging on. This could occur because a user tried to enter an application "in the middle" (say, from a bookmark), or because the session has timed out, and the servlet container created a new one.
  • If validation is not complete, validate the form bean properties as needed. If a problem is found, store the appropriate error message keys as a request attribute, and forward control back to the input form so that the errors can be corrected.
  • Perform the processing required to deal with this request (such as saving a row into a database). This can be done by logic code embedded within the Action class itself, but should generally be performed by calling an appropriate method of a business logic bean.
  • Update the server-side objects that will be used to create the next page of the user interface (typically request scope or session scope beans, depending on how long you need to keep these items available).
  • Return an appropriate ActionForward object that identifies the JSP page to be used to generate this response, based on the newly updated beans. Typically, you will acquire a reference to such an object by calling findForward() on either the ActionMapping object you received (if you are using a logical name local to this mapping), or on the controller servlet itself (if you are using a logical name global to the application).

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

  • The controller servlet creates only one instance of your Action class, and uses it for all requests. Thus, you need to code your Action class so that it operates correctly in a multi-threaded environment, just as you must code a servlet's service() method safely.
  • The most important principle that aids in thread-safe coding is to use only local variables, not instance variables, in your Action class. Local variables are created on a stack that is assigned (by your JVM) to each request thread, so there is no need to worry about sharing them.
  • The beans that represent the Model of your system may throw exceptions due to problems accessing databases or other resources. You should trap all such exceptions in the logic of your perform() method, and log them to the application logfile (along with the corresponding stack trace) by calling:
    servlet.log("Error message text", exception);
  • As a general rule, allocating scarce resources and keeping them across requests from the same user (in the user's session) can cause scalability problems. You should strive to release such resources (such as database connections) prior to forwarding control to the appropriate View component -- even if a bean method you have called throws an exception.

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.

An Action can be factored into several local methods, so long as all properties needed are passed in the method signatures. The JVM handles such properties using the stack, and so they are thread-safe.

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, the most important properties are as follows:

  • type - Fully qualified Java class name of the Action implementation class used by this mapping.
  • name - The name of the form bean defined in the config file that this action will use
  • path - The request URI path that is matched to select this mapping. See below for examples of how matching works.
  • unknown - Set to true if this action should be configured as the default for this application, to handle all requests not handled by another action. Only one action can be defined as a default within a single application.
  • validate - Set to true if the validate() method of the action associated with this mapping should be called.
  • forward - The request URI path to which control is passed when his mapping is invoked. This is an alternative to declaring a type property.
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 struts-config.xml, and place it in the WEB-INF directory of your application. This format of this document is constrained by it's definition in "struts-config_1_0.dtd". The outermost XML element must be <struts-config>.

Inside of the <struts-config> element, there are two important elements that are used to describe your actions:

<form-beans>
This section contains your form bean definitions. You use a <form-bean> element for each form bean, which has the following important attributes:
  • name: A unique identifier for this bean, which will be used to reference it in corresponding action mappings. Usually, this is also the name of the request or session attribute under which this form bean will be stored.
  • type: The fully-qualified Java classname of your form bean.
<action-mappings>
This section contains your action definitions. You use an <action> element for each of your actions you would like to define. Each action element requires the following attributes to be defined:
  • path: The application context-relative path to the action
  • type: The fully qualified java classname of your Action class
  • name: The name of your <form-bean> element to use with this action

The struts-config.xml file from the example application includes the following mapping entry for the "log on" function, which we will use to illustrate the requirements. Note that the entries for all the other actions are left out:

<struts-config>
  <form-beans>
    <form-bean
      name="logonForm"
      type="org.apache.struts.example.LogonForm" />
  </form-beans>      
  <global-forwards
      type="org.apache.struts.action.ActionForward" />
    <forward name="logon" path="/logon.jsp"
         redirect="false" /> 
  </global-forwards>      
  <action-mappings>     
    <action
        path="/logon" 
        type="org.apache.struts.example.LogonAction"
        name="logonForm"
       scope="request"
       input="/logon.jsp"
     unknown="false"
    validate="true" />          
  </action-mappings>
</struts-config>

First the form bean is defined. A basic bean of class "org.apache.struts.example.LogonForm" is mapped to the logical name "logonForm". This name is used as a session or request attribute name for the form bean.

The "global-forwards" section is used to create logical name mappings for commonly used jsp pages. Each of these forwards is available through a call to your action mapping instance, i.e. actionMappingInstace.findForward("logicalName").

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.

Optional but very useful are the local "forward" elements. In the example application, many actions include a local "success" and/or "failure" forward as part of an Action mapping.

<!-- Edit mail subscription -->
<action    path="/editSubscription"
  type="org.apache.struts.example.EditSubscriptionAction"
  name="subscriptionForm"
  scope="request"
  validate="false">
  <forward name="failure" path="/mainMenu.jsp"/>
  <forward name="success" path="/subscription.jsp"/>
  </action>

Using just 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. Of course, you can define whatever local forward properties makes sense for your own application.

One more section of good use is the <data-sources> section, which specifies data sources that your application can use.This is how you would specify a basic data source for your application inside of struts-config.xml:

<struts-config>
  <data-sources>
    <data-source
      autoCommit="false"
     description="Example Data Source Description"
     driverClass="org.postgresql.Driver"
        maxCount="4"
        minCount="2"
        password="mypassword"
             url="jdbc:postgresql://localhost/mydatabase"
            user="myusername"/>
  </data-sources>
</struts-config>

For information on how to retrieve the data source, see the Accessing Relational Databases section.

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/struts-config.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. (You can also find these details in 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.

  • application - Java class name of the application resources bundle base class. [NONE]
  • bufferSize - The size of the input buffer used when processing file uploads. [4096]
  • config - Context-relative path to the XML resource containing our configuration information. [/WEB-INF/struts-config.xml]
  • content - Default content type and character encoding to be set on each response; may be overridden by a forwarded-to servlet or JSP page. [text/html]
  • debug - The debugging detail level for this servlet, which controls how much information is logged. [0]
  • detail - The debugging detail level for the Digester we utilize in initMapping(), which logs to System.out instead of the servlet log. [0]
  • factory - The Java class name of the MessageResourcesFactory used to create the application MessageResources object. [org.apache.struts.util.PropertyMessageResourcesFactory]
  • formBean - The Java class name of the ActionFormBean implementation to use [org.apache.struts.action.ActionFormBean].
  • forward - The Java class name of the ActionForward implementation to use [org.apache.struts.action.ActionForward]. Two convenient classes you may wish to use are:
    • org.apache.struts.action.ForwardingActionForward - Subclass of org.apache.struts.action.ActionForward that defaults the redirect property to false (same as the ActionForward default value).
    • org.apache.struts.action.RedirectingActionForward - Subclass of org.apache.struts.action.ActionForward that defaults the redirect property to true.
  • locale - If set to true, and there is a user session, identify and store an appropriate java.util.Locale object (under the standard key identified by Action.LOCALE_KEY) in the user's session if there is not a Locale object there already. [true]
  • mapping - The Java class name of the ActionMapping implementation to use [org.apache.struts.action.ActionMapping]. Two convenient classes you may wish to use are:
    • org.apache.struts.action.RequestActionMapping - Subclass of org.apache.struts.action.ActionMapping that defaults the scope property to "request".
    • org.apache.struts.action.SessionActionMapping - Subclass of org.apache.struts.action.ActionMapping that defaults the scope property to "session". (Same as the ActionMapping default value).
  • maxFileSize - The maximum size (in bytes) of a file to be accepted as a file upload. Can be expressed as a number followed by a "K" "M", or "G", which are interpreted to mean kilobytes, megabytes, or gigabytes, respectively. [250M]
  • multipartClass - The fully qualified name of the MultipartRequestHandler implementation class to be used for processing file uploads. [org.apache.struts.upload.DiskMultipartRequestHandler]
  • nocache - If set to true, add HTTP headers to every response intended to defeat browser caching of any response we generate or forward to. [false]
  • null - If set to true, set our application resources to return null if an unknown message key is used. Otherwise, an error message including the offending message key will be returned. [true]
  • tempDir - The temporary working directory to use when processing file uploads. [The working directory provided to this web application as a servlet context attribute]
  • validate - Are we using the new configuration file format? [true]
  • validating - Should we use a validating XML parse to process the configuration file (strongly recommended)? [true]
4.5.2 Configure the Action Servlet Mapping

Note: The material in this section is not specific to Struts. The configuration of servlet mappings is defined in the Java Servlet Specification. This section describes the most common means of configuring a Struts application.

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. There are currently four taglibs that Struts is packaged with.

The struts-bean taglib contains tags useful in accessing beans and their properties, as well as defining new beans (based on these accesses) that are accessible to the remainder of the page via scripting variables and page scope attributes. Convenient mechanisms to create new beans based on the value of request cookies, headers, and parameters are also provided.

The struts-html taglib contains tags used to create struts input forms, as well as other tags generally useful in the creation of HTML-based user interfaces.

The struts-logic taglib contains tags that are useful in managing conditional generation of output text, looping over object collections for repetitive generation of output text, and application flow management.

The struts-template taglib contains tags that define a template mechanism.

Below is how you would define all taglibs for use within your application, in reality you would only specify the taglib's that your application will use:

<taglib>
  <taglib-uri>
    /WEB-INF/struts-bean.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-bean.tld
  </taglib-location>
</taglib>
<taglib>
  <taglib-uri>
    /WEB-INF/struts-html.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-html.tld
  </taglib-location>
</taglib>
<taglib>
  <taglib-uri>
    /WEB-INF/struts-logic.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-logic.tld
  </taglib-location>
</taglib>
<taglib>
  <taglib-uri>
    /WEB-INF/struts-template.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-template.tld
  </taglib-location>
</taglib>

This 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, you must copy the .tld files that you require into your WEB-INF directory, and copy struts.jar (and all of the commons-*.jar files) into your WEB-INF/lib directory.

Next: Struts Resources


Copyright (c) 2000-2002, Apache Software Foundation