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


A Look at Commons Chain, Part 2

by Bill Siggelkow, author of Jakarta Struts Cookbook
03/09/2005

Commons Chain, as discussed in part one of this two-part series, provides a Java-based framework and API for representing sequential processes. Developed under the umbrella of the Jakarta Commons project, Commons Chain is being put to test in the latest development release of Struts (which I will refer to as Struts 1.3). In part two today, I'll cover how Struts leverages Chain to facilitate the processing of HTTP requests.

Commons Chain allows you to define sequential sets of command. Each set of commands, a chain, is responsible for fulfilling some process. Each command in the chain specifies a Java class that implements the Command interface. This interface contains a single execute method that receives a Context instance. The execute method returns true to indicate that the request was processed completely and the chain should end. If false is returned, processing moves on to the next command in the chain.

You can define a chain using the Commons Chain API or via an XML file. Using an XML file provides the most flexibility, as you can change the chain definition at deployment time. Here's a simple chain configuration file taken from my first article:


<catalog>
  <chain name="sell-vehicle">
    <command   id="GetCustomerInfo"
        className="com.jadecove.chain.sample.GetCustomerInfo"/>
    <command   id="TestDriveVehicle"
        className="com.jadecove.chain.sample.TestDriveVehicle"/>
    <command   id="NegotiateSale"
        className="com.jadecove.chain.sample.NegotiateSale"/>
    <command   id="ArrangeFinancing"
        className="com.jadecove.chain.sample.ArrangeFinancing"/>
    <command   id="CloseSale"
        className="com.jadecove.chain.sample.CloseSale"/>
  </chain>
</catalog>

Struts uses Chain to replace its traditional HTTP-request processing, which was handled by the RequestProcessor class. The Struts ActionServlet determines the RequestProcessor to use based on the struts-config.xml file. If not specified explicitly, the ActionServlet uses org.apache.struts.action.RequestProcessor. It acquires an instance of the RequestProcessor, calls its init method, and then its process method.

Here's the init method of the RequestProcessor:


public void init(ActionServlet servlet,
                 ModuleConfig moduleConfig)
    throws ServletException {

    synchronized (actions) {
         actions.clear();
    }
        
    this.servlet = servlet;
    this.moduleConfig = moduleConfig;
}

Nothing magical here--the method simply clears its cache of Action instances and sets some instance variables. The heart of the RequestProcessor lies in its process method. This method defines a sequential algorithm for processing the request and response.


public void process(HttpServletRequest request,
                    HttpServletResponse response)
        throws IOException, ServletException {

    // Wrap multipart requests with a special wrapper
    request = processMultipart(request);

    // Identify the path component we will use to select a mapping
    String path = processPath(request, response);
    if (path == null) {
        return;
    }
        
    // Select a Locale for the current user if requested
    processLocale(request, response);

    // Set the content type and no-caching headers if requested
    processContent(request, response);
    processNoCache(request, response);

    // General purpose preprocessing hook
    if (!processPreprocess(request, response)) {
        return;
    }
        
    this.processCachedMessages(request, response);

    // Identify the mapping for this request
    ActionMapping mapping = processMapping(request, response, path);
    if (mapping == null) {
        return;
    }

    // Check for any role required to perform this action
    if (!processRoles(request, response, mapping)) {
        return;
    }

    // Process any ActionForm bean related to this request
    ActionForm form = processActionForm(request, response, mapping);
    processPopulate(request, response, form, mapping);
    if (!processValidate(request, response, form, mapping)) {
        return;
    }

    // Process a forward or include specified by this mapping
    if (!processForward(request, response, mapping)) {
        return;
    }
        
    if (!processInclude(request, response, mapping)) {
        return;
    }

    // Create or acquire the Action instance to process this request
    Action action = processActionCreate(request, response, mapping);
    if (action == null) {
        return;
    }

    // Call the Action instance itself
    ActionForward forward =
            processActionPerform(request, response,
                                 action, form, mapping);

    // Process the returned ActionForward instance
    processForwardConfig(request, response, forward);

}

This process is tailor-made for Commons Chain. (It's no coincidence that some of the Struts committers are also committers on Chain.) The process consists of a series of steps, each represented by a processFoo method. The inputs primarily consist of the request and response objects. Some of the methods return Struts objects like the ActionMapping and ActionForm. If these objects are null, then false is returned, indicating that the process cannot continue. Other methods directly return a true/false value, with false indicating that the process should not continue and that the request has been handled.

Struts 1.3 provides a new request processor class, ComposableRequestProcessor, that extends the original RequestProcessor and overrides the init and process methods to utilize Commons Chain. The init method of the ComposableRequestProcessor loads the request processing chain from a chain catalog. By default, it looks under the catalog named struts and looks for the command named servlet-standard.


public void init(ActionServlet servlet,
                     ModuleConfig moduleConfig)
           throws ServletException {

    super.init(servlet, moduleConfig);

    ControllerConfig controllerConfig = 
        moduleConfig.getControllerConfig();

    String catalogName = controllerConfig.getCatalog();
    catalog = CatalogFactory.getInstance().getCatalog(catalogName);
    if (catalog == null) {
        throw new ServletException("Cannot find catalog '" +
                                       catalogName + "'");
    }

    String commandName = controllerConfig.getCommand();
    command = catalog.getCommand(commandName);
    if (command == null) {
        throw new ServletException("Cannot find command '" +
                                       commandName + "'");
    }
}

The ComposableRequestProcessor overrides the process method so that it can run the chain (that is, execute the command):


public void process(HttpServletRequest request,
                        HttpServletResponse response)
        throws IOException, ServletException {

    // Wrap the request in the case of a multipart request
    request = processMultipart(request);
        
    // Create and populate a Context for this request
    ActionContext context = contextInstance(request, response);

    // Create and execute the command.
    try {
        command.execute(context);
    } catch (Exception e) {
        // Execute the exception processing chain??
        throw new ServletException(e);
    }

    // Release the context.
    context.release();
}

Where did all the steps go? An XML configuration file, chain-config.xml, defines the sequence of commands to execute. (The exception here is the processMultipart method. This method, implemented in the ComposableRequestProcessor, wraps the request if its content type is multipart/form-data). Let's dig into the chain-config.xml file to see how it works. First, Struts takes advantage of Commons Chain support for sub-chains, which can represent entire chain as a command, using the LookupCommand. The define element is used as a convenience mechanism for defining sub-chains using the LookupCommand.


<define name= "lookup"
   className="org.apache.commons.chain.generic.LookupCommand"/>

The ComposableRequestProcessor executes the chain named servlet-standard. This chain is composed of three commands:

servlet-exception A Chain filter for handling exceptions. A filter is a special command that implements a postprocess method. The postprocess method of the filter is called after every command in the chain (actually, its after every command that follows the filter's declaration).
process-action The main sequence for processing the request and executing the appropriate action.
process-view Handles forwarding to the view (e.g., a JSP page).


<chain name="servlet-standard">

    <!-- Establish exception handling filter -->
    <command
          className="org.apache.struts.chain.commands.ExceptionCatcher"
        catalogName="struts"
   exceptionCommand="servlet-exception"/>

    <lookup
        catalogName="struts"
               name="process-action"
           optional="false"/>

    <lookup
        catalogName="struts"
               name="process-view"
           optional="false"/>

</chain>

The process-action chain defines the commands for processing the request and calling your actions.


<chain name="process-action">
  <lookup catalogName="struts"
                 name="servlet-standard-preprocess"
             optional="true"/>

  <command className=
    "org.apache.struts.chain.commands.servlet.SelectLocale"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.RequestNoCache"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.SetContentType"
  />        
  <command className=
    "org.apache.struts.chain.commands.servlet.SelectAction"
  />
  <command className= 
    "org.apache.struts.chain.commands.servlet.AuthorizeAction"
  />    
  <command className=
    "org.apache.struts.chain.commands.servlet.CreateActionForm"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.PopulateActionForm"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.ValidateActionForm"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.SelectInput"
  />
  <command className=
    "org.apache.struts.chain.commands.ExecuteCommand"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.SelectForward"
  />
  <command className=
    "org.apache.struts.chain.commands.SelectInclude"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.PerformInclude"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.CreateAction"
  />
  <command className=
    "org.apache.struts.chain.commands.servlet.ExecuteAction"
  />
</chain>

The processFoo methods of the original RequestProcessor have been re-implemented as classes that implement the Chain Command interface. In Struts, each Command implementation extends from an abstract base class. This base class implements the execute method of the Command interface. The execute method then calls methods of the concrete class that perform the actual work.

Consider how the locale is retrieved from the HTTP request and stored. First, here's implementation of processLocale from the original RequestProcessor:


protected void processLocale(HttpServletRequest request,
                             HttpServletResponse response) {

    // Are we configured to select the Locale automatically?
    if (!moduleConfig.getControllerConfig().getLocale()) {
        return;
    }

    // Has a Locale already been selected?
    HttpSession session = request.getSession();
    if (session.getAttribute(Globals.LOCALE_KEY) != null) {
        return;
    }

    // Use the Locale returned by the servlet container (if any)
    Locale locale = request.getLocale();
    if (locale != null) {
        session.setAttribute(Globals.LOCALE_KEY, locale);
    }
}

The new chain implementation processes the locale using two classes, AbstractSelectLocale and SelectLocale. The abstract class implements the execute method:


public boolean execute(Context context) throws Exception {
    ActionContext actionCtx = (ActionContext) context;

    // Are we configured to select Locale automatically?
    ModuleConfig moduleConfig = actionCtx.getModuleConfig();
    if (!moduleConfig.getControllerConfig().getLocale()) {
        return (false);
    }

    // Retrieve and cache appropriate Locale for this request
    Locale locale = getLocale(actionCtx);
    actionCtx.setLocale(locale);

    return (false);
}

SelectLocale extends AbstractSelectLocale and implements the abstract getLocale method:


protected Locale getLocale(ActionContext context) {

    ServletActionContext saContext = (ServletActionContext) context;

    // Has a Locale already been selected?
    HttpSession session = saContext.getRequest().getSession();
    Locale locale = (Locale) session.getAttribute(Globals.LOCALE_KEY);
    if (locale != null) {
        return (locale);
    }

    // Select and cache the Locale to be used
    locale = saContext.getRequest().getLocale();
    if (locale == null) {
        locale = Locale.getDefault();
    }
    session.setAttribute(Globals.LOCALE_KEY, locale);
    return (locale);

}

Both the abstract and concrete classes make extensive use of the received context. In Commons Chain, a Context object acts as the shared store for commands in the chain. Unlike the RequestProcessor methods, which had direct access to the HTTP servlet request and response, the Commands have to do a bit more work to find these objects. The abstract commands downcast the context to an ActionContext. The ActionContext provides explicit access to Struts-related properties, such as message resources, the Action being executed, and request and session resources and services. However, the ActionContext does not depend on the Servlet API. The concrete command classes, however, will downcast the context to the ServletActionContext. This class implements the ActionContext interface and wraps a Commons Chain ServletWebContext. The ServletWebContext contains properties for the servlet objects such as the HttpServletRequest, HttpServletResponse, and ServletContext.

The following class diagram shows how the context classes and interfaces used by Struts relate to those provided by Chain.


Figure 1.

The developers of Struts 1.3 have endeavored to reduce coupling with the Servlet API. By structuring its use of the Chain Context as shown here, Struts separates its reliance on the Servlet API to the lowest level, the concrete command implementations. In fact, you will find that the Struts commands follow a very consistent pattern. These commands are implemented such that:

  1. An abstract command implements the Command.execute method and only deals with the ActionContext.

  2. Within execute, this abstract base class calls a custom abstract method to perform servlet-specific work.

  3. The concrete subclass implements the abstract method, downcasts the ActionContext to a ServletActionContext, and uses objects such as the HttpServletRequest and HttpSession to perform the particular work.

  4. Based on the returned result of the abstract method, the abstract command returns false (to continue the chain), or true (to preempt the chain).

Related Reading

Jakarta Struts Cookbook
By Bill Siggelkow

Understanding the new ComposableRequestProcessor and chain configuration is important if you want to customize its behavior. Commons Chain affords the developer multiple ways of customizing Struts' request processing. If you wanted to customize the locale processing, for example, you could use any of the following techniques:

If you have ever customized the Struts RequestProcessor, you may have overridden the processPreprocess method to perform custom request processing. Struts 1.3 uses Chain to provide a similar hook. The first command in the process-action chain is defined as:


<!-- Look up optional preprocess command -->
<lookup catalogName="struts"
               name="servlet-standard-preprocess"
           optional="true"/>

This element declares a sub-chain that will be executed as the first step in the process- action chain. The optional=true attribute ensures that the parent chain will not fail if the sub-chain is not defined.

In the Jakarta Struts Cookbook, I showed how you can check if a user is logged in by overriding the processPreprocess method in a custom RequestProcessor. You can implement this same behavior in Struts 1.3 by defining a pre-processing chain instead of extending the RequestProcessor. For illustrative purposes, let's see how you would implement this behavior for the Struts Mail Reader sample application. Here's the command that checks if the User object is bound to the session. The checkUser parameter indicates if the check should be made or not.

 
package com.jadecove.chain.commands;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.struts.apps.mailreader.Constants;
import org.apache.struts.chain.contexts.ActionContext;
import org.apache.struts.chain.contexts.ServletActionContext;

public class CheckUser implements Command {

  public boolean execute(Context ctx) throws Exception {
    ActionContext context = (ActionContext) ctx;
    Object user = context.getSessionScope().get(Constants.USER_KEY);
    if (user == null &&
        context.getParameterMap().containsKey("checkUser")) {
	HttpServletResponse response = 
        ((ServletActionContext) ctx).getResponse();
	response.sendError(403, "User not logged in.");
	return true;
    }
    return false;
  }

}

Now we need to declare the chain containing this command. But where should you add the XML for the new chain? There are a couple of options here; first, you could add the command to the chain-config.xml file provided by Struts. This is the most straightforward approach. However, when you upgrade Struts, you will have to ensure that you preserve the changes you made to this file. A better alternative is to create a separate file for your custom sub-chains, then tell Struts to use your chain configuration file along with the Struts file. First, create a file in your application's WEB-INF folder, named custom- chain-config.xml. Add the following chain declaration to the file:


<?xml version="1.0" ?>
<catalog name="struts">
    <chain name="servlet-standard-preprocess">
        <command className="com.jadecove.chain.commands.CheckUser"/>
    </chain>
</catalog>

Then you configure the ActionServlet to use your custom chain configuration file along with the Struts-provided file, much like you do for specifying struts-config.xml files.


<!-- Action Servlet Configuration -->
<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>
      org.apache.struts.action.ActionServlet
    </servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>
            /WEB-INF/struts-config.xml,
            /WEB-INF/struts-config-registration.xml
        </param-value>
    </init-param>
    <init-param>
        <param-name>chainConfig</param-name>
        <param-value>
            /WEB-INF/chain-config.xml, 
            /WEB-INF/custom-chain-config.xml
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

When you access the Mail Reader's welcome page, it will look like Figure 2:


Figure 2.

But when you add the checkUser parameter, the CheckUser command generates the response and terminates the chain, as shown in Figure 3.


Figure 3.

If you'd rather replace a command then create a separate chain, simply make the change in the chain-config.xml file.

Before I put a wrap on Struts and Chain, let's take a look at how Struts uses a Chain filter for exception handling. Struts defines an exception catching command as the first element in the servlet-standard chain:


<!-- Establish exception handling filter -->
<command  className="org.apache.struts.chain.commands.ExceptionCatcher"
        catalogName="struts"
   exceptionCommand="servlet-exception"/>

We have already seen how the command element uses the className attribute; however the catalogName and exceptionCommand attributes are new. In fact, there is nothing magical about these two attributes. They have no intrinsic meaning to Chain. When Commons Chain parses the chain configuration (using Commons Digester under the covers), any attributes not explicitly handled by Chain are treated as JavaBean property setters. In other words, when Chain parses the command above, it will acquire an instance of the ExceptionCatcher class, and then set JavaBean properties as if the following code was used:


exceptionCatcher.setCatalogName("struts");
exceptionCatcher.setExceptionCommand("servlet-exception");

The ExceptionCatcher implements the filter. Classes that implement the filter must 
implement the following two methods:

// (From the Command interface) Called when the Filter is first
// processed in sequence 
public boolean execute(Context context); 

// Called after a chain command throws an exception
// or the chain reaches its end
public boolean postprocess(Context context, Exception exception);

In the ExceptionCatcher, the execute method simply resets the command, setting the current exception in the ActionContext to null and returning false (instructing the chain to continue). The postprocess method is where all the interesting stuff happens. If this method receives a non-null exception, it uses the catalogName and exceptionCommand properties to find and execute a command (which can itself be a chain of commands).


public boolean postprocess(Context context, Exception exception) {
    // Do nothing if there was no exception thrown
    if (exception == null) {
        return (false);
    }

    // Store the exception in the context
    ActionContext actionCtx = (ActionContext) context;
    actionCtx.setException(exception);

    // Execute the specified command
    try {
        Command command = lookupExceptionCommand();
        if (command == null) {
            throw new IllegalStateException
                ("Cannot find exceptionCommand '" +
                     exceptionCommand + "'");
        }
        command.execute(context);
    } catch (Exception e) {
        throw new IllegalStateException
           ("Exception chain threw exception");
    }
    return (true);
}

Where's the good old Struts exception handler? Well, it's encapsulated in the configuration of the servlet-exception chain.


<!-- ========== Servlet Exception Handler Chain ============= -->    
<chain     name="servlet-exception">
    <!-- Execute the configured exception handler (if any) -->
    <command className=
        "org.apache.struts.chain.commands.servlet.ExceptionHandler"
    />
    <!-- Follow the returned ForwardConfig (if any) -->
    <command className=
        "org.apache.struts.chain.commands.servlet.PerformForward"
    />
</chain>

The classes used in this chain perform the same function as the Struts 1.2 exception handler. Using the ExceptionHandler class, which extends AbstractExceptionHandler, the first command locates the declared Struts exception handler (if any) and calls that handler's execute method. The returned ActionForward is stored in the ActionContext and subsequently processed by the general-purpose PerformForward command.

I hope that I have been able to give you a better idea of how Struts uses chains. Both Commons Chain and Struts 1.3 are still in flux, so things may change. I encourage you to download the latest source for Struts and take a good look at how it uses Chain. I think you will find that the implementation is well-thought-out; adding custom behavior to Struts request processing is now much easier and less obtrusive than ever before.

Bill Siggelkow is an independent consultant specializing in software design, development, and technical training.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.