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

advertisement

AddThis Social Bookmark Button

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

Now that you can define the chain in an XML file, and retrieve the chain, which is itself a command, the possibilities for extension and flexibility are limitless. Suppose that the "Arrange financing" process was actually handled by a completely separate department of the business. This department wants to set up its own workflow for that part of the sale. Chain supports this concept through the use of nested chains. Since a chain is a command, you can replace a single use of a command with a reference to another chain. Here's the chain configuration with this new flow added to the mix:


<catalog name="auto-sales">
   <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
		 className="org.apache.commons.chain.generic.LookupCommand"
	     catalogName="auto-sales"
		      name="arrange-financing"
		  optional="true"/>
	 <command   id="CloseSale"
		 className="com.jadecove.chain.sample.CloseSale"/>
   </chain>
   <chain name="arrange-financing">
	 <command   id="ArrangeFinancing"
		 className="com.jadecove.chain.sample.ArrangeFinancing"/>
   </chain>
</catalog>

Related Reading

Jakarta Struts Cookbook
By Bill Siggelkow

Commons Chain provides the general-use command LookupCommand for discovering and executing another chain. The optional attribute controls how LookupCommand handles the case when the nested chain is not found in the specified catalog. If optional=true, then the process continues, even if the chain to execute cannot be found. Otherwise, LookupCommand throws an IllegalArgumentException, indicating that the command could not be found.

There are three ways of ending a command chain:

  1. A command returns true from its execute method.
  2. The end of the chain is reached.
  3. A command throws an exception.

A command should return true if the chain has completely handled the process. This notion is the basis of the Chain of Responsibility. Processing is handed off from command to command, until a command handles the command. If no process returns true, before reaching the end of the command sequence, the chain is assumed to have completed normally.

A chain ends abnormally when any exception is thrown by a command. With Commons Chain, if a command throws an exception, the chain is broken. The exception, be it a runtime exception or application exception, will bubble up to the original caller of the chain. But many applications need explicit exception handling that is defined external to any commands. Commons Chain provides a facility for this using the Filter interface. Filter extends Command, adding a postprocess method.


public boolean postprocess(Context context,
                           Exception exception);

Commons Chain guarantees that the postprocess method will be called if the Filter's execute method is called, regardless of any thrown exceptions. Like servlet filters, Chain Filters are executed in the order that they appear in the command sequence. Likewise, each Filter's postprocess method is called in reverse order. You can use this feature of Chain to implement exception handlers. Here's a Filter that traps exceptions that may occur in the sample chain.


package com.jadecove.chain.sample;

import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;

public class SellVehicleExceptionHandler implements Filter {

	public boolean execute(Context context) throws Exception {
		System.out.println("Filter.execute() called.");
		return false;
	}

	public boolean postprocess(Context context,
                                 Exception exception) {
		if (exception == null) return false;
		System.out.println("Exception "
                              + exception.getMessage()
                              + " occurred.");
		return true;
	}
}

You define the Filter in the chain configuration file just as you would a normal Command.


<chain name="sell-vehicle">
  <command   id="ExceptionHandler"
     className =
           "com.jadecove.chain.sample.SellVehicleExceptionHandler"/>
  <command   id="GetCustomerInfo"
      className="com.jadecove.chain.sample.GetCustomerInfo"/>

The Filter's execute method is called in sequence. However, the postprocess method is not called until the chain reaches its end or a command throws an exception. If an exception was raised, then the postprocess method handles the exception and returns true, indicating that the exception was handled. The chain does terminate at this point, but the exception is essentially caught and does not propagate further. If the postprocess method returns false, the exception bubbles up, causing abnormal termination of the chain.

Let's suppose that the ArrangeFinancing command threw an exception because a customer had bad credit. The SellVehicleExceptionHandler would catch that exception, resulting in output like the following:


Filter.execute() called.
Get customer info
Test drive the vehicle
Negotiate sale
Exception Bad credit occurred.

By combining techniques like filtering and sub-chains, you can model some fairly complicated workflows.

Commons Chain is a promising framework that is currently undergoing active development. It's still very new and features are being added quite frequently. In the next article on Commons Chain, we'll take a look at how it's being used in Struts 1.3. Struts 1.3 completely replaces its existing HTTP-request-processor class with one driven by Commons Chain. If you have ever had to write a custom Struts request processor, you can appreciate the flexibility that Commons Chain brings to the table.


In February 2005, O'Reilly Media, Inc., released Jakarta Struts Cookbook.

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


Return to ONJava.com.