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
To start, we’ll import the latest version of this library using Maven:
<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
The ATM takes a number as an input and passes it down to handlers responsible for performing different actions. Those involve calculating numbers of bank notes to be dispensed, and sending a notification to the bank and customer about the transaction.
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
The Command takes the Context as an input and processes it.
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
It is a collection of Chains and Commands with their logical names.
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.