Package javawebparts.misc.chain

This package contains a Chain Of Responsibility (CoR) pattern implementation which offers a great deal of flexibility.

See:
          Description

Interface Summary
Command This is the interface that a Command must implement.
 

Class Summary
Catalog This class represents a Catalog.
Chain This class represents a Chain.
ChainContext This class represents a ChainContext.
ChainManager This class is the top of the hierarchy, and all client application interaction with the Chain implementation should occur through this class.
CommandConfig This is the class that represents the configuration for a Command.
Result This class is returned by a Command or Chain.
 

Package javawebparts.misc.chain Description

This package contains a Chain Of Responsibility (CoR) pattern implementation which offers a great deal of flexibility. Note that this implementation IS NOT web-specific, that is, you can use it outside a webapp without any problem.

As defined by the Gang Of Four (GoF - Gamma, Helm, Johnson, and Vlissides), the CoR pattern can be described like so:

"Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it."

Yeah, whatever :) In short, we're talking about defining a series of atomic operations that are "chained" together and executed in sequence, giving each step the opportunity to do something or not.

As an example, take the simple mathematical equation:

1 + 2 + 3 + 4 = 10

We can break this into a series of individual steps. In this description, the letter A represents the accumulation of the result after each step:

Step 1. A = 1 + 2
Step 2. A = A + 3
Step 3. A = A + 4

Now, imagine you defined each of those steps as a unique Java class. Further imagine that you declared that those three steps, done in sequence, is called a Chain, and this Chain represents the larger operation of the equation. In short, that's the CoR pattern.

Now, on to the details of this implenentation...

Simply put, you will create an XML file something like the following:

<chainConfig>
  <catalog id="myCatalog">
    <chain id="myChain">
      <command id="step1" className="my.app.Step1" />
      <command id="step2" className="my.app.Step2" />
      <command id="step3" className="my.app.Step3" />
    </chain>
  </catalog>
</chainConfig>

The config file must be in the classpath accessible to the ChainManager class! In a webapp, the easiest way to do this is to place the file in WEB-INF/classes. By default, the name of the file MUST be chain_config.xml. You can override this in two ways: (a) pass the filename (which is technically a path) to the ChainManager's constructor, or (b) set an environment variable named JWP_CHAIN_CONFIG_FILE_NAME to the value you desire. If you have any question about how to set this value, look up the getResourceAsStream() method of the Class class in the Java SDK. The rules enumerated there are what you must follow.

The config file defines three elements: Catalogs, Chains and Commands. Catalogs are nothing but collections of Chains who's sole purpose is to allow you to organize things better when you are defining multiple Chains, and a Chain is of course an ordered series of Commands to be executed.

The thing you as a developer will be primarily concerned with are the Commands. These define individual steps in a Chain, and this is what you will implement in code. Let's take a look at the three Commands above, which correspond to the three steps in that equation from before:

package my.app;

import javawebparts.misc.chain.ChainContext;
import javawebparts.misc.chain.Command;
import javawebparts.misc.chain.Result;

public class Step1 implements Command {

  public Result init(ChainContext chainContext) {
    return new Result(Result.SUCCESS);
  }

  public Result execute(ChainContext chainContext) {
    int a = 1 + 2;
    chainContext.setAttribute("a", new Integer(a));
    return new Result(Result.SUCCESS);
  }

  public Result cleanup(ChainContext chainContext) {
    return new Result(Result.SUCCESS);
  }

}

Every Command you write will implement the Command interface, which defines three methods: init(), execute() and cleanup().

You can think of init() exactly like you do a constructor in that it will always be called before anything else. You can do whatever you like here... grab a database connection, call some remote system, initialize some fields, etc. Note that Commands do not have to be thread-safe because a new instance of it is instantiated every time it is needed. Of course, this means you cannot store things in the instance between Command executions. You can however store them between the execution of these three methods, you don't have to resort to static fields unless you want to.

The cleanup() method can be thought of like finalize() except that it is more deterministic in that it will always be called as the last thing done, regardless of what happens elsewhere in the Command (well, except for exceptions, which could cause it to not execute).

The execute() method is where the actual work of your Command should go.

Each of the three methods receives a reference to a ChainContext object. The ChainContext is how information is communicated between Commands of the Chain. Conceptually it is just like an HttpRequest object. In fact, you work with it in the same way via setAttribute() and getAttribute() methods, which takes a key and an Object value to store. You can put whatever you like in the context, at any time (i.e., from any of the three methods, and/or before and after the Chain executes). You can also find some standard pieces of information in the context, such as the last Result that was produced by a Command (or a Chain, if a Command is a subchain... more on this later) as well as the ID of the Catalog and Chain the Command belongs to.

Each of the three methods returns an instance of the Result class. This defines what happened within the Command. You cnn construct a Result object in one of three ways:

Result r = new Result(Result.xxxx);
In this case, you are just returning one of the allowed result codes as defined by the static fields in the Result class, represented by the xxxx above. Those values are: Result r = new Result(Result.xxxx, "yyyy");
The result code is the first parameter and is still as described above. The "yyyy" parasmeter is any arbitrary extra info you would like to return. This can be helpful if the next Command should do something in a variable way based on what the result of the previous Command was. Note that if your intention is to return some extra info to the caller executing the Chain, the extra info can only be returned by the last Command in the Chain because subsequent Commands would effectively overwrite any previous extra info. Alternatively, each Command in your Chain would have to replicate the extra info along the Chain so it is always present for the last Command to return.

Result r = new Result(Result.xxxx, "yyyy", "zzzz");
The result code and extra info parameters are still as described above. The "zzzz" parameter is the ID of a Command. This is only applicable when the return code is JUMP_TO_COMMAND.

Let's look at the next Command in the chain:

package my.app;

import javawebparts.misc.chain.ChainContext;
import javawebparts.misc.chain.Command;
import javawebparts.misc.chain.Result;

public class Step2 implements Command {

  public Result init(ChainContext chainContext) {
    return new Result(Result.SUCCESS);
  }

  public Result execute(ChainContext chainContext) {
    Integer a = (Integer)chainContext.getAttribute("a");
    a = new Integer(a.intValue() + 3);
    chainContext.setAttribute("a", a);
    return new Result(Result.SUCCESS);
  }

  public Result cleanup(ChainContext chainContext) {
    return new Result(Result.SUCCESS);
  }

}

Still very straight-forward. No sense wasting the space to show the third Command, it's the same except we're adding 4 to A in it.

So, at this point we've coded up the Commands, defined them to form a Chain in the config file, and added the Chain to a Catalog. All that's left is using it. Very simple:

ChainManager cm = new ChainManager();
ChainContext ct = cm.createContext();
cm.executeChain("myCatalog/myChain, ct);
Result cr = ct.getResult();
if (cr.getCode() == Result.SUCCESS) {
  System.out.println(ct.getAttribute("a"));
} else {
  System.out.println("Chain FAIL or ABORT");
}

All interaction with Chains are done through the ChainManager. So, we need to instantiate one. The first time a ChainManager is instantiated, the config file will be read and static objects created to represent it. Subsequent ChainMaager instantiations will not incur this overhead. Once we have a ChainManager, we ask it for a new ChainContext object. You can at this point add whatever initial data to it tha your Chain needs. Then, we simply ask it to execute a given Chain in a given Catalog, handing it the ChainContext. Once it completes, we interrogate the ChainContext to see what the result of the execution was, and act accordingly.

And that, in a nutshell, is the Chain implementation in Java Web Parts!

Ok, so that's the basic. How about the more advanced stuff?

That about covers it. I hope you find the CoR implementation useful! There is some further advanced functionality I intend to add, but for now, this is it. Enjoy!

This package depends on the following extra packages to compile and run: None.



Copyright © 2005 Frank W. Zammetti