drools引擎控制规则分组,以及规则执行先后次序的方法有多种。
A. 对于逻辑上聚合比较紧密的一组规则,可以利用salience的值控制事实匹配到的一组规则的执行先后,salience值越大,优先级越高,salience可以为负值。
salience在每条rule本身的属性部分设定,语法是:
rule "XX"
salience 50
when
...
then
...
end
B. 可以通过ruleflow来控制一组规则先匹配,另一组规则后匹配。每条规则可被划分到ruleflow中的某一个(在当前的实现中不能是多个)rule task节点内。由于bpm本身的灵活特性,这样的匹配规程设定可以非常复杂——但工作流有图形表示,因此管理相对简单,其实现步骤大概是这样:
1. 编写一个流程,在drools5.0遵循规则流的语法(后缀rf),在drools5.1及以后遵循jbpm5和BPMN2的语法(后缀bpmn),并保证在流程定义中具备一个或多个rule task节点。流程的编写可以使用drools的eclipse插件可视化的完成。
写道
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="Definition"
targetNamespace="http://www.jboss.org/drools"
typeLanguage="http://www.java.com/javaTypes"
expressionLanguage="http://www.mvel.org/2.0"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
xmlns:g="http://www.jboss.org/drools/flow/gpd"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:tns="http://www.jboss.org/drools">
<process processType="Private" isExecutable="true"
id="aaa" name="SampleFlow.b" >
<!-- nodes -->
<startEvent id="_1" name="Start" />
<businessRuleTask id="_2" name="A"
g:ruleFlowGroup="A" />
<businessRuleTask id="_3" name="B"
g:ruleFlowGroup="B" />
<endEvent id="_4" name="End" >
<terminateEventDefinition/>
</endEvent>
<!-- connections -->
<sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
<sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" />
<sequenceFlow id="_3-_4" sourceRef="_3" targetRef="_4" />
</process>
<bpmndi:BPMNDiagram>
<bpmndi:BPMNPlane bpmnElement="aaa" >
<bpmndi:BPMNShape bpmnElement="_1" >
<dc:Bounds x="100" y="100" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_2" >
<dc:Bounds x="239" y="106" width="80" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" >
<dc:Bounds x="398" y="148" width="80" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_4" >
<dc:Bounds x="542" y="191" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_1-_2" >
<di:waypoint x="124" y="124" />
<di:waypoint x="279" y="130" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_2-_3" >
<di:waypoint x="279" y="130" />
<di:waypoint x="438" y="172" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_3-_4" >
<di:waypoint x="438" y="172" />
<di:waypoint x="566" y="215" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
注意上面的流程id“aaa“,以及两个businessRuleTask节点的ruleFlowGroup属性(命名空间g:表明它是一个drools相关的属性,而非BPMN标准)
2. 在流程定义文件中,通过rule本身的ruleflow-group属性值,将一组组的rule映射到多个流程节点中,例如
rule "XX"
ruleflow-group "A"
...
then
...
end
3. Java驱动流程引擎的代码中,这样写
//装载knowledge base,加入rule配置和流程配置
...
//启动ksession,为一个stateful的session
...
//准备事实pojo
...
//asert实时,驱动规则流
ksession.insert(pojo);
ksession.startProcess("aaa");
ksession.fireAllRules();
ksession.dispose();
...
于是乎,指定到ruleflow-group A的规则会首先安排匹配,而后是ruleflow-group B的那些规则安排匹配。
C. 如果不愿意采用规则流,可以使用agenda-group属性,将所有规则分组,而后通过设定agendagroup的焦点(focus)来激活某一分组,让事实匹配这一分组内的规则。
写道
rule "XXX"
agenda-group "GROUP1"
...
then
...
end
如果rule没有指定group,则被归于缺省的MAIN group。在运行时,group之间是栈节点的关系,工作原理如下:
1. 可以在流程引擎驱动的Java代码中使用ksession.getAgenda().getAgendaGroup("GROUPX").setFocus();或rule的RHS中使用drools.setFocus("GROUPX")来设定组焦点。
2. 如果设定成功,当前的activation状态会保持不变,获得焦点的groupx压到栈顶,进行规则匹配
3. 匹配完成后,此groupx自动失去焦点,退栈,继续之前的activation状态执行
相较规则流, agenda group的优点是体现了栈的逻辑,在任何规则的右部,焦点的设定是“动态”的,非常灵活;缺点则是没有图形化的规则组排程匹配。
注意,在agenda group没有获得焦点之前,它是不会匹配执行的。缺省的,MAIN这个特殊的组(在至少有一条规则没有指定agenda-group的情况下)总是存在的,位于栈的底部。
no-loop属性和lock-on-action属性
在两个属性存在的目的都是为了避免当规则右部改变了事实(使用modify或update)时,有可能产生的无限递归匹配的情形。
no-loop针对自身,保证相同的一组事实数据只驱动no-loop的规则一次——换言之,如果在其他规则里数据再次发生改变,无限递归还是可能产生的。
lock-on-action稍有不同,针对某个agenda-group或ruleflow-group,当此group获得焦点,或节点进入,发生activation后,lock-on-action的规则们会被打上一个临时标记,在离开这一组前,这些规则不会再次得到激活。