ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

A Look at Commons Chain, Part 2
Pages: 1, 2, 3, 4

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:

  • Provide your own custom extension of AbstractSelectLocale that implements the getLocale method. Specify this new class as the className in the chain-config.xml file.

  • Completely replace the command class with your own class the implements the Command interface. Specify this new class as the className in the chain-config.xml file.

  • Finally, utilizing the LookupCommand, you could replace the single command for locale processing with an entire sub-chain.

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>

Pages: 1, 2, 3, 4

Next Pagearrow