apache-commons-chain
Apache Commons Chain
1. Introduction
Apache Commons Chain is a library that uses the Chain of Responsibility pattern – generally used for organizing complex processing flows in which multiple receivers can process a request.
In this quick article, we’ll go through an example representing a withdrawal from an ATM.
2. Maven Dependency
<dependency>
<groupId>commons-chain</groupId>
<artifactId>commons-chain</artifactId>
<version>1.2</version>
</dependency>
To check for the most recent version of this library – go here.
3. Example Chain
4. Chain Context
The context represents the current state of an application, storing information about the transaction.
For our ATM withdrawal request, the information we need is:
-
Total amount to be withdrawn
-
Number of 100 denomination notes
-
Number of 50 denomination notes
-
Number of 10 denomination notes
-
Amount left to be withdrawn
This state is defined in a class:
public class AtmRequestContext extends ContextBase {
int totalAmountToBeWithdrawn;
int noOfHundredsDispensed;
int noOfFiftiesDispensed;
int noOfTensDispensed;
int amountLeftToBeWithdrawn;
// standard setters & getters
}
5. Command
We’ll implement each of the steps mentioned above as a Command:
public class HundredDenominationDispenser implements Command {
@Override
public boolean execute(Context context) throws Exception {
intamountLeftToBeWithdrawn = (int) context.get("amountLeftToBeWithdrawn);
if (amountLeftToBeWithdrawn >= 100) {
context.put("noOfHundredsDispensed", amountLeftToBeWithdrawn / 100);
context.put("amountLeftToBeWithdrawn", amountLeftToBeWithdrawn % 100);
}
return false;
}
}
The Commands for FiftyDenominationDispenser & TenDenominationDispenser are similar.
6. Chain
A Chain is a collection of commands to be executed in a specified order. Our Chain will consist of the above Commands and also an AuditFilter at the end:
public class AtmWithdrawalChain extends ChainBase {
public AtmWithdrawalChain() {
super();
addCommand(new HundredDenominationDispenser());
addCommand(new FiftyDenominationDispenser());
addCommand(new TenDenominationDispenser());
addCommand(new AuditFilter());
}
}
When any Command in the Chain returns true, it forces the Chain to end.
7. Filter
A filter is also a Command but with a postProcess method that is called after the execution of the Chain.
Our Filter will send a notification to the customer & the bank:
public class AuditFilter implements Filter {
@Override
public boolean postprocess(Context context, Exception exception) {
// send notification to bank and user
return false;
}
@Override
public boolean execute(Context context) throws Exception {
return false;
}
}
8. Chain Catalog
In our case, our Catalog will contain the AtmWithdrawalChain.
public class AtmCatalog extends CatalogBase {
public AtmCatalog() {
super();
addCommand("atmWithdrawalChain", new AtmWithdrawalChain());
}
}
9. Using the Chain
Let’s see how we can use the above Chain to process a withdrawal request. We’ll first create a Context and then pass it the Chain. The Chain will process the Context.
We’ll write a test case to demonstrate our AtmWithdrawalChain:
public class AtmChainTest {
@Test
public void givenInputsToContext_whenAppliedChain_thenExpectedContext() throws Exception {
Context context = new AtmRequestContext();
context.put("totalAmountToBeWithdrawn", 460);
context.put("amountLeftToBeWithdrawn", 460);
Catalog catalog = new AtmCatalog();
Command atmWithdrawalChain = catalog.getCommand("atmWithdrawalChain");
atmWithdrawalChain.execute(context);
assertEquals(460, (int) context.get("totalAmountToBeWithdrawn"));
assertEquals(0, (int) context.get("amountLeftToBeWithdrawn"));
assertEquals(4, (int) context.get("noOfHundredsDispensed"));
assertEquals(1, (int) context.get("noOfFiftiesDispensed"));
assertEquals(1, (int) context.get("noOfTensDispensed"));
}
}
10. Conclusion
In this tutorial, we explored a practical scenario using the Apache’s Apache Commons Chain library – which you can read more about here.
And, as always, the code for this article is available over on Github.