The Jakarta Commons project encompasses several subprojects, which contain reusable Java components. Of these, the Logging, Validator, and Digester subprojects are the best known, if only because Struts uses them. A fairly new Commons subproject, still in development, is Chain. The Chain framework is an implementation of the Chain of Responsibility pattern, used to execute a flow of sequential process steps. In the near future, Chain will also be used by Struts (not entirely coincidental—some Struts committers are also Chain committers).
In this article, I explain Chain's usage, advantages, and current imperfections.
In the Chain framework, units of work within a sequential flow of steps are represented by commands. A sequence of commands
forms a chain. The chain itself is also a command that can be executed. Chains are kept in catalogs, from which they can be
retrieved at the time of execution. For this purpose, the Chain framework offers the interfaces Chain, Command, and Catalog, see the figure below for a class diagram.

Class diagram of some Chain interfaces
The Chain framework itself contains ready-to-use implementations of Chain and Catalog. You still must implement the commands containing the functionality that forms a unit of work. See Listing 1 for a simple
example.
Listing 1. An implementation of a command
public class ACommand1 implements Command {
public boolean execute(Context ctx) throws Exception {
//Add functionality for ACommand1
String property = (String) ctx.get("property");
System.out.println("A command1: " + property);
return false; //The chain will resume
}
}
An application using the Chain framework must instantiate a Catalog with chains and commands. This can be done directly by using the Chain API or by using a configuration file. The latter option offers the most flexibility. See Listing 2 for a simple configuration
file.
Listing 2. A chain-config.xml file
<catalog>
<chain name="a-chain">
<command className="chain.test.ACommand1"/>
<command className="chain.test.ACommand2"/>
</chain>
</catalog>
From this point, applying the Chain framework to execute the configured commands isn't hard, as Listing 3 demonstrates.
Listing 3. The execution of a chain
public class ChainStart {
public static void main(String[] args) {
ChainStart cs = new ChainStart();
//Get the catalog
Catalog catalog = cs.parseConfigFile();
//Get the command
Command command = catalog.getCommand("a-chain");
try {
//Create a context
YourContext ctx = new YourContext();
ctx.setProperty("a-value");
//Execute the command
command.execute(ctx);
} catch (Exception exc) {
System.out.println(exc.toString());
}
}
private Catalog parseConfigFile() {
//Parse the configuration file
ConfigParser parser = new ConfigParser();
String fileLocation = "/chain/test/chain-config.xml";
try {
parser.parse(this.getClass().getResource(fileLocation));
} catch (Exception exc) {
System.out.println(exc.toString());
}
return CatalogFactoryBase.getInstance().getCatalog();
}
}
The ConfigParser can be used to instantiate the Catalog. The Catalog is stored in the singleton CatalogFactoryBase. Because the Catalog doesn't have a name attribute in the configuration file, it automatically is CatalogFactory's default catalog. Therefore, it can be retrieved by the getCatalog() method. Only chains or commands that are direct children of Catalog can be retrieved by the getCommand() method. So, the command ACommand1 cannot be retrieved directly from Catalog. If desired, you can configure ACommand1 as a direct child of Catalog. In that case, Catalog should also have a name attribute.
Archived Discussions (Read only)