ILOG JRules User Guide > Optimizing Execution > Tasks > Executing a Rule Task Directly

A typical ruleflow includes a combination of different tasks: rule tasks, function tasks, and flow tasks. If a ruleflow is used to control the execution of rules in an engine whose cycle is tight, or fine-grained, it is possible that performance becomes unacceptable. To correct this performance issue, ILOG JRules provides an API that enables a rule task to bypass the ruleflow. This API uses classes to add rule task control--these objects are known as rule task runners.

The rule task runner API is most relevant when the definition of a rule task uses a dynamic body in a domain. In this scenario, the active rules are selected by a piece of JRules script (the dynamicselect part). This script is typically slow to evaluate and, in addition, it is called each time the rule task is entered.

When a rule task is applied to one tuple at a time, the script that computes the active rules is evaluated once per tuple. If the tuple is used to select the active rules, the overhead is unavoidable and can quickly become objectionable.

Note
The RetePlus, sequential, and Fastpath execution modes are currently available to implement the body of a rule task. To select an execution mode, see Choosing an Execution Mode.

To offer good interoperability with the engine API, the rule task runner API supports all the rule task body types. However, the most relevant case is a rule task body defined by a dynamic body in a domain.

The rule task runner classes are as follows:

The entry point to a rule task runner is the IlrContext class:

public synchronized IlrRuleTaskRunner getRuleTaskRunner(IlrTask task)

Here is an illustration of the rule activation and body execution of a rule task runner:

images/runner.png

This section has the following subsections:

Using the IlrTaskRunner Interface

The IlrTaskRunner interface is the base interface of a rule task runner. It specifies the fundamental services of a rule task runner:

package ilog.rules.engine;
public interface IlrTaskRunner {
   public IlrTask getTask();
   // Gets and returns the rule task instance.
   public void reset();
   // Synchronizes the runner with the ruleflow.
   public void runInitialActions();
   // Executes the initial actions of the rule task.
   public void runFinalActions();
   // Executes the final actions of the rule task.
}

Using the IlrRuleTaskRunner Interface

The IlrRuleTaskRunner interface extends the IlrTaskRunner interface and adds additional services to a rule task runner.

Using the rule task runner API, the selection of rules is no longer a JRules piece of script, but rather the direct passing of a Java IlrRule[] array to the runner, see IlrRuleTaskRunner.setActiveRules(IlrRule[] rules).

In other words, the user should provide ordinary Java code to fill the array of activated rules.

The active rules can then be applied to the tuples provided by a user-iterator or the working-memory iterator, see IlrRuleTaskRunner.runBody().

Using the rule task runner API the activation of the rules and the application of the rules become two distinct operations instead of the single execute of the ruleflow.

Note
No API has been added to pass a single tuple to the rules since it would have compromised the interoperability with rule tasks that use the RetePlus algorithm. The overhead of an iteration on the tuple iterator remains acceptable even if there is only one tuple to gather.

package ilog.rules.engine;
public interface IlrRuleTaskRunner extends IlrTaskRunner {
   public void setActiveRules(IlrRule[] rules);
   // Sets the active rules.
   public int runBody();
   // Executes the body of the rule task and returns the number of rules fired.
}

The setActiveRules method is not incremental, that is, the active rules are set in a single operation and there is no consideration to any rules that were previously active. This operation is only supported by rule tasks that allow the set of active rules to be changed dynamically, that is, rule tasks that select the rules using a dynamic body or a dynamic body in a domain. If this is not the case, a java.lang.UnsupportedOperationException is thrown.

Note
There is no check to test that the set of active rules is really a subset of the domain used in the rule task.

Using the IlrRuleTaskRunner Implementations

The body property of a rule task is mandatory. It selects the rules of the ruleset that will be taken into account by the rule task. The four ways to select the rules are all supported by the rule task runners.

You can use rule task runners with the RetePlus algorithm or the sequential algorithm.

Using Rule Task Runners for the RetePlus Algorithm

The initial state of a RetePlus rule task runner, a default rule task, is as follows:

Using Rule Task Runners for the Sequential Algorithm

There are two operations to modify the way the body of a sequential rule task runner is executed:

The initial state of a sequential rule task runner is as follows:

Getting a Rule Task Runner from the IlrContext

The IlrContext class contains a method to retrieve a rule task runner from a rule task descriptor:

public synchronized IlrRuleTaskRunner getRuleTaskRunner(IlrTask task);
// task is the rule task descriptor

As the name of the method suggests, a runner can only be applied to rule tasks.

Note
The result of a rule task runner depends on the IlrContext object it has been created from. A new rule task runner should only be called if the IlrContext object can safely evaluate the JRules expressions the rule task depends on.

Basic Rule Task Runner Example

The following basic rule task runner example uses a ruleset named MyRuleset.irl, which contains a rule task named MyRuleTask:

ruleset MyRuleset {
   in MyIterator myIterator;
}
rule R1 {
   when {
      C1(...);
      ...CN(...);
   }
   then {
      ...
   }
}
rule R2 {
   when {
      C1(...);
      ...CN(...);
   }
   then {
      ...
   } 
}
...
rule RM { 
   when {
      C1(...);
      ...CN(...);
   }
   then {
   ...
   }
}
ruletask MyRuleTask {
   algorithm = sequential;
   ordering = literal;
   matchedclasses = {C1,...,CN}
   iterator = myIterator;
   initialaction = {
      ...
   }
   body = select(?rule) { 
      return true; 
   }
   finalaction = {
      ...
   }
}

The following code creates an IlrContext object from the MyRuleset.irl, and gets a rule task runner from the rule task descriptor of MyRuleTask, then executes the Initial Actions, the body, and the Final Actions:

IlrRuleset ruleset = new IlrRuleset();
   ruleset.parseFileName("MyRuleset.irl");
IlrContext ctxt = new IlrContext(ruleset);   
   MyIterator it = new MyIterator();
IlrParameterMap params = new IlrParameterMap();
   params.setParameter("myIterator",it);
   ctxt.setParameters(params);
IlrTask task = ruleset.getTask("MyRuleTask");
IlrRuleTaskRunner runner = ctxt.getRuleTaskRunner(task);
   int fired = 0;
   runner.runInitialActions();
   fired += runner.runBody();
   runner.runFinalActions();

Dynamic Rule Task Runner Example

The following dynamic rule task runner example uses a ruleset named MyDynamicRuleset.irl, which contains a rule task named MyDynamicRuleTask:

ruleset MyDynamicRuleset {
   in MyIterator myIterator;
}
rule R1 {
   when {
      C1(...);
      ...CN(...);
   }
   then {
      ...
   }
}
rule R2 {
   when {
      C1(...);
      ...CN(...);
   }
   then {
      ...
   }
} 
...
rule RM { 
   when {
      C1(...);
      ...CN(...);
   }
   then {
      ...
   }
}
ruletask MyDynamicRuleTask {
   algorithm = sequential;
   ordering = literal;
   matchedclasses = {C1,...,CN}
   iterator = myIterator;
   initialaction = {
      ...
   }
   body = dynamicselect(?rule) {
      return true; // will not be used
   }
   in
      getRuleset().getAllRules(); // will be used
   finalaction = {
      ...
   }
}

The following code creates an IlrContext object from the MyDynamicRuleset.irl, and gets a rule task runner from the rule task descriptor of MyDynamicRuleTask, then executes the Initial Actions, sets the active rules in the body, executes the body, and executes the Final Actions:

IlrRuleset ruleset = new IlrRuleset();
   ruleset.parseFileName("MyDynamicRuleset.irl");
IlrContext ctxt = new IlrContext(ruleset);	    
   MyIterator it = new MyIterator();
IlrParameterMap params = new IlrParameterMap();
   params.setParameter("myIterator",it);
   ctxt.setParameters(params);
IlrTask task = ruleset.getTask("MyDynamicRuleTask");
IlrRule[] allRules = ruleset.getAllRules();
IlrRuleTaskRunner runner = ctxt.getRuleTaskRunner(task);
   int fired = 0;
IlrRule[] activeRules = null;
   while (! myCompletionTest(ctxt)) {
      runner.runInitialActions();
      activeRules = myActiveRulesSelector(allRules);
      runner.setActiveRules(activeRules);
      fired += runner.runBody();
      it.reset();
      runner.runFinalActions();
   }

Related Concepts

Java and ILOG JRules
Performance Optimizers

Related Tasks

Creating a Java Project for Rules
Creating a POJO Client Project for RuleApps

Related Reference

New Client Project for RuleApps Wizard
New Java Project for Rules Wizard