Drools and jBPM: integration patterns

It is fundamental for us to understand why jBPM and Drools coexist under the KIE umbrella. The relationship between the two projects and the similarities they have make them share most tools and methodologies. These two projects complement each other, allowing end users to describe business knowledge using different paradigms and languages to model business scenarios. We will be able to choose the right tool for the job and then combine the power of the Rule world with the Process world.

This section will cover three of the most common patterns of rule and process integration. The main idea here is to open our mind to look for new alternatives to implement our business solutions.

We already learned in this chapter how to start new process instances by using the KIE APIs, but now we will see how we can interact with the process engine from within our rules.

In the next section we will learn how to interact with the Process engine from our rules. This will open the door for more advanced modeling techniques, giving us even more flexibility to implement our business scenarios.

Accessing the process engine from our rules

Using rules to start a process is one of the most common patterns that we can benefit from. The main idea is to use processes to deal with different scenarios that we might find difficult to perform by chaining rules.

One common scenario is Human Validation; as we saw before, the simple.bpmn2 process's only mission was to request the input from an external actor to validate an amount. Most of the time, we will want to perform this kind of validation using rules, but we will find it difficult to cover all the possibilities, and sometimes there are sensible decisions and validations that must be performed by people. We can detect these situations by using rules and then delegate to a business process for further processing.

The following rule will create a new process instance via OrderLine, which we insert in our Kie Session:

rule "Validate OrderLine Item's cost"
    when
        $ol: OrderLine()
    then
        Map<String, Object> params = new HashMap<String, Oject>();
        params.put("requested_amount", $ol.getItem().getCost());
        kcontext.getKieRuntime().startProcess("simple", params);
end

Once again, we are starting our simple.bpmn2 process, but here we are binding the OrderLine item cost to the requested_amount process variable required by the process.

If we have too much procedural logic on the right-hand side of our rules, decoupling it as a business process can help us to have more maintainable rules and processes.

It is recommended that, if we want to interact with external systems or if we require human intervention, we just delegate that to a process, where we can easily make changes and plug different connectors. When human interventions are required, using a process engine allows us to automatically track their progress without needing to build all those mechanisms in our application.

By using the kcontext.getKieRuntime() we will be able to create, abort, and signal business processes. We can also access the WorkItemManager to complete WorkItems based on rules.

Process instances as facts

Another way to integrate processes and rules is to insert our Process Instances as facts in the Rule Engine; by doing this, we can start writing rules about our processes. We could even write rules about groups of processes that will enable us to enforce some business requirements.

We could manually insert our ProcessInstances objects into our Kie Session, but there is already a ProcessEventListener that does all the work for us. This listener is provided by jBPM and is called RuleAwareProcessEventLister. This listener will automatically insert our ProcessInstances and update them whenever a variable is changed.

The only catch with using this listener is that our processes need to include Async activities to be able to be evaluated by the Rule Engine. This is mostly because, if we run a simple process such as our simple.bpmn2 example, the process will start and end inside the startProcess call, not allowing the Rule Engine to evaluate the ProcessInstance fact. This is usually not a big deal, due the fact that most of the processes end up containing multiple safe points to demarcate transactions, as we will see in the second half of this chapter. If we take a look at the RuleAwareProcessEventLister implementation, we will notice that the listener is in charge of inserting the WorkflowProcessInstance object as a fact and keeping it updated every time that a process variable is changed.

This allows us to start writing rules about those instances. A test class in the chapter source code called ProcessInstancesAsFactsTest shows a couple of example rules that evaluate our process instances as we create them.

Let's take a look at these rules. The first one will evaluate to true for every process instance that we start inside this Kie Session:

rule "There is a WorkflowProcessInstance fact"
    when
        $wi: WorkflowProcessInstance()
    then
    // There is a WorkflowProcessInstance fact: "+$wi
end

The next one will only evaluate to true for our "process-order" business process instances. For any other fact, we can filter our process instances by their field/property values.

rule "There is a Process Order Instance"
    when
        $wi: WorkflowProcessInstance(processId == "process-order")
    then
       // There is a Process Order Instance: "+$wi
end

In the same way, we can get the value of the process variables. Notice that we need to check for null, due the fact that in the example process the Order process variable is set inside a User Task and not when we start the process. It is really useful to control what our process is doing. Remember that we can leverage all this power without doing much, besides adding the RuleAwareProcessEventLister.

rule "Process Order Instance with a big order"
    when
        $wi: WorkflowProcessInstance(
                           processId == "process-order",
                           $o: getVariable("order") != null &&  
                        ((Order)getVariable("order")).getTotal() > 1000)
    then
        // We can abort or create another process instance //   to review this big orders here
end

Finally, it is important to understand that we can write rules to evaluate a set of Process Instances and mix that information with some other facts. The following rule, for example, checks how many Process Order instances are currently started. We can use this information to make decisions about whether the company can cope with a high number of concurrent orders. Notice that it is possible get a list of process instances and then execute operations on them, such as aborting them if they are not high-priority:

rule "Too many orders for just one Manager"
    when
      List($managersCount:size > 0) from collect(Manager())
      List(size > ($managersCount * 3)) from collect(WorkflowProcessInstance(processId == "process-order"))
    then
        //There are more than 3 Process Order Flows per manager.
        //  Please hire more people :)
end

This rule will evaluate to false if the number of Managers inside the Kie Session is not enough to handle the orders.

BPMN2 Business Rule Tasks

Finally the BPMN2 specification proposes a specific type of task called a Business Rule Task. This task type proposes the most traditional integration between processes and Rules. Traditionally, a Rule Engine was seen as a stateless service that can be called with some data to get some results. As we saw in the rest of this book, Drools is much more than a simple stateless service, and for that reason using the Business Rule Task approach is, most of the time, a very limited approach. In this short section, we will see how the Business Rule Task can be used in a very simple example. In jBPM, the Business Rule Task is used in conjunction with a Rule Property called ruleflow-group. This rule property allows us to specify which rules can be fired when the Business Rule Task is executed as part of a process instance. We can have as many groups as we need, but we need to remember to set up the correct Rule Flow Group inside the Business Rule Task.

Tip

Note that rules inside a ruleflow-group are evaluated and activated as soon as data is available, but those rules will not be fired until the ruleflow-group where they belong is activated by a process instance triggering a Business Rule Task that corresponds with that ruleflow-group.

In the following example a process uses two Business Rule Tasks to validate and apply discounts to an Order:

BPMN2 Business Rule Tasks

The Order is modeled as a process variable so our rules will pick up the ProcessInstance as a fact (as explained in the previous section) and it will analyze the Process Variable that contains the order. Two different ruleflow-groups are defined:

  • Validation (ruleflow-group Validation):

    This ruleflow-group is in charge of validating different aspects of the order. Three simple rules validate that the order has a customer associated with it, that the order is not empty (at least one or more OrderLines), and that the state of the Order is pending.

  • Discounts (ruleflow-group Discounts):

    This ruleflow-group can contain any number of rules to apply discounts based on the Order data. In this example two different rules were defined to evaluate the Order total and the Customer Category to apply different discounts.

These rules can be found here: chapter-10/chapter-10-kjar/src/main/resources/chapter10/order-validation-rules.drl.

Looking at the BPMN2 process file (here: chapter-10/chapter-10-kjar/src/main/resources/chapter10/order-validation.bpmn2), we can notice that each Business Rule Task has its correspondent ruleflow-group set:

<bpmn2:businessRuleTask id="..." drools:selectable="true" drools:ruleFlowGroup="Validation" name="Validate Order">

Let's do this for Discounts as well:

<bpmn2:businessRuleTask id="..." drools:selectable="true" drools:ruleFlowGroup="Discounts" name="Apply Discounts">

By adding this relationship with each ruleflow-group the engine will know which rules can be triggered at each point in our process instance.

Tip

Business Rule Tasks are just another way of defining when rules can be fired. We usually recommend creating a separate WorkItemHandler with a separate Kie Session to handle more complex rule sets. If you really want to interact with Drools in a stateless fashion, having a Rules Service and delegating to a WorkItemHandler implementation the interaction with this services is most of the time the best solution. We can take a look at what happens on the execution of this process looking at a test called: BusinessRuleTasksTest (chapter-10/chapter-10-tests/src/test/java/org/drools/devguide/chapter10/BusinessRuleTasksTest.java). This test class contains four unit tests that validate the correct behavior of our process and rules by inserting different orders as process variables.

These tests all use RuleAwareProcessEventLister, which will automatically add each ProcessInstance as a fact, and the TriggerRulesEventListener, which is in charge of firing our rules as soon as our ruleflow-groups get activated in our process instances.

Tip

Note that there is no strict rule for what the rules inside a ruleflow-group need to evaluate. For this example the rules are evaluating process instances as facts, but there is no restriction to adding any other Fact type on the conditional side of the rules. Because of this flexibility, you need to be careful how you write those rules; remember that they will evaluate all the data in the Kie Session and not only the data in your process instance.

We hope that these examples demonstrate how powerful these tools are when used together. The previous three patterns give us a wide range of possibilities to model complex scenarios. By mastering these three patterns, we should be able to simplify solutions that have been previously written based only on rules or processes into more natural and decoupled models. Instead of forcing rules to behave sequentially, we should be able to formalize a business process for the tasks that need to be performed in sequence. If we need to make decisions exclusively based on data, rules are the right tool for that job. Knowing when to choose rules over processes is a skill that we will learn by practicing and testing different approaches for implementing our business scenarios.

In the second half of this chapter, we will look at more advanced topics related to how we persist our processes and how to demarcate transaction boundaries. Both persistence and transaction mechanisms are shared by Drools and jBPM so we need to understand how these mechanisms impact the execution of our rules and processes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.134.151