Eric D. Schabell: Red Hat JBoss BPM Suite - accessing process context variables from a rule

Thursday, February 13, 2014

Red Hat JBoss BPM Suite - accessing process context variables from a rule


In another installment of the JBoss BPM Suite Tips & Tricks series we will be helping understand how you can access or change a process context variable from inside a rule in your BPM project.

We will be using a simple demo project that can be found in our demo collections on github, which will allow you to view and experiment with the final working example in a JBoss BPM Suite installation.

The JBoss BPM Suite installation you will have to set up yourself, but you can leverage our JBoss BPM Suite installation project we have to save you some trouble.

The goal

The basic goal of this article and demo project is to show you exactly how to design a process that uses a rule that if it fires will then access and modify one of the process variables. This communication between a process and a rule is an often asked question.

The setup

Start by unzipping the demo project repository and copy it into the JBoss BPM Suite installation projects directory.

# Start by cloning project at 
#   https://github.com/eschabell/demo-collections.
#
$ git clone git@github.com:eschabell/demo-collections.git

$ cd demo-collections

$ unzip niogit-rule-procvar.zip

# Follow instructions and install JBoss BPM Suite 
# via https://github.com/eschabell/bpms-install-demo.
#
# Then copy the demo repository into the JBoss BPM Suite server.
#
$ cp -rv .niogit bpms-install-demo/target/jboss-eap-6.1/bin

# Start the JBoss BPM Suite server.
#
$ ./bpms-install-demo/target/jboss-eap-6.1/bin/standalone.sh

# Login with user 'erics' and password 'bpmsuite' to get started.

Now we can take a look at the process, how it is configured to be deployed by default, what the rule is, what we have to do to modify a process variable, and look at the output.

The initial output

Our process consists of a simple run through that prints a log statement at the first node, then runs a rule (which we want to modify a process variable), then a gateway where we decide if the rule fired or not, and then the path where the rule fired (we log that) or the path where the rule did not fire (we log that too).
Test process for rule modifying a process variable.
What we want to test is that a rule with a Left Hand Side or Condition that evaluates to TRUE, will trigger its corresponding Right Hand Side or Action to print a log statement that the rule fired.

On our initial attempt we had a process that only had the first two nodes, then ended. We expected to see something like this in the logs if we ran three instances of the process:
17:29:46,315 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
17:29:46,330 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Tested Rule
17:29:46,315 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
17:29:46,330 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Tested Rule
17:29:46,315 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
17:29:46,330 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Tested Rule

What we ended up with was this:
17:29:46,315 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
17:29:46,330 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Tested Rule
17:29:46,315 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
17:29:46,315 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step

At this point we modified the process you see above, adding two debug nodes that report if the rule fired or not, making the outcome clearer. We expected to see something like this in the logs if we ran three instances of the process:
20:51:22,491 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Execute Java Step
20:51:22,494 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Tested Rule
20:51:22,497 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Rule fired!
20:51:35,615 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Execute Java Step
20:51:35,618 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Tested Rule
20:51:35,621 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Rule fired!
20:51:43,312 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Execute Java Step
20:51:43,316 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Tested Rule
20:51:43,320 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Rule fired!

What we ended up with was this:
20:51:22,491 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Execute Java Step
20:51:22,494 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Tested Rule
20:51:22,497 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Rule fired!
20:51:35,615 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Execute Java Step
20:51:35,618 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Rule did not fire...
20:51:43,312 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Execute Java Step
20:51:43,316 INFO  [stdout] (http-localhost/127.0.0.1:8080-2) Rule did not fire...

What happened to the rule firing the second and third time? Not only that, the rule never even triggers in the second and third time through, why?

To find out we need to look at the session strategy that is applied by default.

The deployment

Taking a closer look at how this process is deployed, we see that it makes use of the default session strategy SINGLETON which means it will run all process instances in a single ksession.

Deployment by default is using SINGLETON runtime strategy
This means that the process would put any facts into working memory and then run through our process and at the rule task it would evaluate the rule. This was the first time through an instance.

The second (and all consecutive) time through the working memory was not being modified so the rule is never evaluated, it just passes over it.

To fix this we need to make the process clearer, so we added a process variable to be updated when the rule fired, a gateway to check the process variable, and if set to log that the rules had fired. Remember, the rule firing sets the process variable.

Let's see how that looks.

The rule

We took our initial stab at the rule by setting it up to always match the condition so that the action would fire:
rule "update-procvar"
ruleflow-group "fire-always"

when 
  eval(true)
then
  System.out.println("Tested Rule");
end
Remember with the single ksession the first time through it fired, but after that it never gets evaluated again. To fix this we will need to make sure that some form of object or fact is added into the working memory each time this process instance is run.

Luckily this is provided for in a rule task, where you can create Data Assignments and map your DataInputSets. This is done by selecting the rule task and expanding the Properties sidebar to show you that we have created a fired variable and assigned it the value null, then we are inserting it into the working memory as our DataInputSet.

Mapping the fired variable into the working memory every time.

import org.kie.api.runtime.process.WorkflowProcessInstance;

rule "update-procvar"
ruleflow-group "fire-always"

when 
  $pi: WorkflowProcessInstance()
  String()
then
  $pi.setVariable("fired", "true");
end
What we have done here in the rule is add an import to give us access to the process instance we are running in, then assign it to the local variable $pi so we can work with it to access our process variable, then we have a check that will always succeed if there is a new String object in working memory. Remember we insert a fired object every time we enter this rule task node?

This should cause the rule to evaluate and then using the process instance we can set the process variable fired to true, which is exactly the check that is done at the following gateway node.

Let's run it a few times and see what happens?

The Output

Now that we have sorted out the issues and understand what it means to have a single ksession in which our rules will only fire on a new instance if there has been some fact change in the working memory, we are ready to test our solution.

This is what you will see when you run three instances of the demo process:
21:06:32,767 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
21:06:32,774 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Tested Rule
21:06:32,777 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Rule fired!
21:06:41,646 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
21:06:41,654 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Tested Rule
21:06:41,656 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Rule fired!
21:06:47,559 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Execute Java Step
21:06:47,565 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Tested Rule
21:06:47,567 INFO  [stdout] (http-localhost/127.0.0.1:8080-5) Rule fired!

We hope you now understand how a process is deployed using a rule, how to access and update a process variable from a rule in your project, and can put this knowledge to use in your own projects.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.