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

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.