Drools JBoss Rules 5.0 Flow (Part 2)

0
83
8 min read

Transfer Funds work item

We’ll now jump almost to the end of our process. After a loan is approved, we need a way of transferring the specified sum of money to customer’s account. This can be done with rules, or even better, with pure Java as this task is procedural in nature. We’ll create a custom work item so that we can easily reuse this functionality in other ruleflows. Note that if it was a once-off task, it would probably be better suited to an action node.

The Transfer Funds node in the loan approval process is a custom work item. A new custom work item can be defined using the following four steps (We’ll see how they are accomplished later on):

  1. Create a work item definition. This will be used by the Eclipse ruleflow editor and by the ruleflow engine to set and get parameters. For example, the following is an extract from the default WorkDefinitions.conf file that comes with Drools. It describes ‘Email‘ work definition. The configuration is written in MVEL. MVEL allows one to construct complex object graphs in a very concise format. This file contains a list of maps—List<map<string, Object>>. Each map defines properties of one work definition. The properties are: name, parameters (that this work item works with), displayName, icon, and customEditor (these last three are used when displaying the work item in the Eclipse ruleflow editor). A custom editor is opened after double-clicking on the ruleflow node.
    import org.drools.process.core.datatype.impl.type.StringDataType;
    [
    [
    "name" : "Email",
    "parameters" : [
    "From" : new StringDataType(),
    "To" : new StringDataType(),
    "Subject" : new StringDataType(),
    "Body" : new StringDataType()
    ],
    "displayName" : "Email",
    "icon" : "icons/import_statement.gif",
    "customEditor" : "org.drools.eclipse.flow.common.editor.
    editpart.work.EmailCustomEditor"
    ]]

    Code listing 13: Excerpt from the default WorkDefinitions.conf file.

    Work item’s parameters property is a map of parameterName and its value wrappers. The value wrapper must implement the org.drools.process.core.datatype.DataType interface.

  2. Register the work definitions with the knowledge base configuration. This will be shown in the next section.
  3. Create a work item handler. This handler represents the actual behavior of a work item. It will be invoked whenever the ruleflow execution reaches this work item node. All of the handlers must extend the org.drools.runtime.process.WorkItemHandler interface. It defines two methods. One for executing the work item and another for aborting the work item. Drools comes with some default work item handler implementations, for example, a handler for sending emails: org.drools.process.workitem.email.EmailWorkItemHandler. This handler needs a working SMTP server. It must be set through the setConnection method before registering the work item handler with the work item manager (next step). Another default work item handler was shown in code listing 2 (in the first part)-SystemOutWorkItemHandler.
  4. Register the work item handler with the work item manager.

After reading this you may ask, why doesn’t the work item definition also specify the handler? It is because a work item can have one or more work item handlers that can be used interchangeably. For example, in a test case, we may want to use a different work item handler than in production environment.

We’ll now follow this four-step process and create a Transfer Funds custom work item.

Work item definition

Our transfer funds work item will have three input parameters: source account, destination account, and the amount to transfer. Its definition is as follows:

import org.drools.process.core.datatype.impl.type.ObjectDataType;
[
[
"name" : "Transfer Funds",
"parameters" : [
"Source Account" : new ObjectDataType("droolsbook.bank.
model.Account"),
"Destination Account" : new ObjectDataType("droolsbook.bank.
model.Account"),
"Amount" : new ObjectDataType("java.math.BigDecimal")
],
"displayName" : "Transfer Funds",
"icon" : "icons/transfer.gif"
]]

Code listing 14: Work item definition from the BankingWorkDefinitions.conf file.

The Transfer Funds work item definition from the code above declares the usual properties. It doesn’t have a custom editor as was the case with email work item. All of the parameters are of the ObjectDataType type. This is a wrapper that can wrap any type. In our case, we are wrapping Account and BigDecimal  types. We’ve also specified an icon that will be displayed in the ruleflow’s editor palette and in the ruleflow itself. The icon should be of the size 16×16 pixels.

Work item registration

First make sure that the BankingWorkDefinitions.conf file is on your classpath. We now have to tell Drools about our new work item. This can be done by creating a drools.rulebase.conf file with the following contents:

drools.workDefinitions = WorkDefinitions.conf BankingWorkDefinitions.conf

Code listing 15: Work item definition from the BankingWorkDefinitions.conf file (all in one one line).

When Drools starts up, it scans the classpath for configuration files. Configuration specified in the drools.rulebase.conf file will override the default configuration. In this case, only the drools.workDefinitions setting is being overridden. We already know that the WorkDefinitions.conf file contains the default work items such as email and log. We want to keep those and just add ours. As can be seen from the code listing above, drools.workDefinitions settings accept list of configurations. They must be separated by a space. When we now open the ruleflow editor in Eclipse, the ruleflow palette should contain our new Transfer Funds work item.

If you want to know more about the file based configuration resolution process, you can look into the org.drools.util.ChainedProperties class.

Work item handler

Next, we’ll implement the work item handler. It must implement the org. drools.runtime.process.WorkItemHandler interface that defines two methods: executeWorkItem and abortWorkItem. The implementation is as follows:

/**
* work item handler responsible for transferring amount from
* one account to another using bankingService.transfer method
* input parameters: 'Source Account', 'Destination Account'
* and 'Amount'
*/
public class TransferWorkItemHandler implements
WorkItemHandler {
BankingService bankingService;

public void executeWorkItem(WorkItem workItem,
WorkItemManager manager) {
Account sourceAccount = (Account) workItem
.getParameter("Source Account");
Account destinationAccount = (Account) workItem
.getParameter("Destination Account");
BigDecimal sum = (BigDecimal) workItem
.getParameter("Amount");

try {
bankingService.transfer(sourceAccount,
destinationAccount, sum);
manager.completeWorkItem(workItem.getId(), null);
} catch (Exception e) {
e.printStackTrace();
manager.abortWorkItem(workItem.getId());
}
}

/**
* does nothing as this work item cannot be aborted
*/
public void abortWorkItem(WorkItem workItem,
WorkItemManager manager) {
}

Code listing 16: Work item handler (TransferWorkItemHandler.java file).

The executeWorkItem method retrieves the three declared parameters and calls the bankingService.transfer method (the implementation of this method won’t be shown). If all went OK, the manager is notified that this work item has been completed. It needs the ID of the work item and optionally a result parameter map. In our case, it is set to null. If an exception happens during the transfer, the manager is told to abort this work item.

The abortWorkItem method on our handler doesn’t do anything because this work item cannot be aborted.

Please note that the work item handler must be thread-safe. Many ruleflow instances may reuse the same work item instance.

Work item handler registration

The transfer work item handler can be registered with a WorkItemManager as follows:

TransferWorkItemHandler transferHandler = 
new TransferWorkItemHandler();
transferHandler.setBankingService(bankingService);
session.getWorkItemManager().registerWorkItemHandler(
"Transfer Funds", transferHandler);

Code listing 17: TransferWorkItemHandler registration (DefaultLoanApprovalServiceTest.java file).

A new instance of this handler is created and the banking service is set. Then it is registered with WorkItemManager in a session.

Next, we need to ‘connect’ this work item into our ruleflow. This means set its parameters once it is executed. We need to set the source/destination account and the amount to be transferred. We’ll use the in-parameter mappings of Transfer Funds to set these parameters.

Drools JBoss Rules 5.0 Flow (Part 2)

As we can see the Source Account is mapped to the loanSourceAccount ruleflow variable. The Destination Account ruleflow variable is set to the destination account of the loan and the Amount ruleflow variable is set to loan amount.

Testing the transfer work item

This test will verify that the Transfer Funds work item is correctly executed with all of the parameters set and that it calls the bankingService.transfer method with correct parameters. For this test, the bankingService service will be mocked with jMock library (jMock is a lightweight Mock object library for Java. More information can be found at http://www.jmock.org/). First, we need to set up the banking service mock object in the following manner:

mockery = new JUnit4Mockery();
bankingService = mockery.mock(BankingService.class);

Code listing 18: jMock setup of bankingService mock object (DefaultLoanApprovalServiceTest.java file).

Next, we can write our test. We are expecting one invocation of the transfer method with loanSourceAccount and loan’s destination and amount properties. Then the test will set up the transfer work item as in code listing 17, start the process, and approve the loan (more about this is discussed in the next section). The test also verifies that the Transfer Funds node has been executed. Test method’s implementation is as follows:

@Test
public void transferFunds() {
mockery.checking(new Expectations() {
{
one(bankingService).transfer(loanSourceAccount,
loan.getDestinationAccount(), loan.getAmount());
}
});

setUpTransferWorkItem();
setUpLowAmount();
startProcess();
approveLoan();

assertTrue(trackingProcessEventListener.isNodeTriggered(
PROCESS_LOAN_APPROVAL, NODE_WORK_ITEM_TRANSFER));
}

Code listing 19: Test for the Transfer Funds work item (DefaultLoanApprovalServiceTest.java file).

The test should execute successfully.

LEAVE A REPLY

Please enter your comment!
Please enter your name here