| ILOG JRules User Guide > Optimizing Execution > Tasks > Executing a Rule Task Directly |
Executing a Rule Task Directly |
PREVIOUS NEXT |
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:
This section has the following subsections:
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.
}
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.
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. |
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.
The initial state of a RetePlus rule task runner, a default rule task, is as follows:
body = {...}
IlrContext object only takes into account the set of rules for the rule task. The runner uses this set of rules at each runBody().
setActivateRules(IlrRule[]) method throws a java.lang.UnsupportedOperationException.
body = select ...
runBody(). The runner should only be instantiated when the JRules expression that selects the rules can be safely evaluated.
setActivateRules(IlrRule[]) method throws a java.lang.UnsupportedOperationException.
body = dynamicselect ...
runBody(). The runner should only be instantiated when the JRules expression that selects the rules can be safely evaluated.
setActivateRules(IlrRule[]) method notifies the IlrContext object to take only into account the array of rules for the task.
body = dynamicselect ... in ...
There are two operations to modify the way the body of a sequential rule task runner is executed:
reset()
setActivateRules(IlrRule[])
The initial state of a sequential rule task runner is as follows:
body = {...}
runBody().
setActivateRules(IlrRule[]) method throws a java.lang.UnsupportedOperationException.
body = select ...
runBody(). The runner should only be instantiated when the JRules expression that selects the rules can be safely evaluated.
setActivateRules(IlrRule[]) method throws a java.lang.UnsupportedOperationException.
body = dynamicselect ...
runBody(). The runner should only be instantiated when the JRules expression that selects the rules can be safely evaluated.
setActivateRules(IlrRule[]) method generates new bytecode to take into account the array of rules. The bytecode generation is a costly operation in terms of memory consumption and an even more costly operation in terms of processing time especially when a great number of rules are involved. This rule selection mechanism should only be chosen when the set of active rules does not change very often.
body = dynamicselect ... in ...
in) is evaluated. A special tuple matcher bytecode that supports dynamic rule activation is generated with the rule domain. The runner uses this set of rules at each runBody(). The runner should only be instantiated when the JRules expression that retrieves the rule domain can be safely evaluated.
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.
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();
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();
}
| Copyright © 1987-2008 ILOG S.A. All rights reserved. Legal terms. Documentation homepage. | PREVIOUS NEXT |