jBPM 是一个非常优秀的开源工作流引擎,虽然他不是一个一站式的工作流平台,不过它已经为我们提供了比较丰富的底层操作,为了满足特定的项目需求,我们一般需要对其进行二次开发,才能适用于具体的业务需求。
其中一个主要的扩展点是针对 TaskNode 进行的用户的分配。TaskNode 是 jBPM 中一个非常重要的概念,一个任务节点可以包含若干个任务,不同的任务可以由不同的人来完成,任务实例被分配给 actorId 来完成,其中指定到人的分配工作就是 Assignment 要处理的,这也是我们需要定制的功能,为了实现用户的分配,我们需要实现 AssignmentHandler 接口,接口原型如下。
public interface AssignmentHandler extends Serializable {
void assign(Assignable assignable, ExecutionContext executionContext) throws Exception;
}
通常用代码实现的话,我们可以让一个类实现这个接口,并在 swimlane 或者 tasknode 中的 assignment 指定该类。但是,这样的灵活性是显然不够的,在系统的使用过程中,分配策略会不断的进行调整,因此我们需要更为灵活的解决方案,jBPM 本身可以使用基于 Bean Shell 的脚本来写分配策略,但是 Bean Shell 不是那么强大,我们需要更为强大的解决方案,因此,我们选用了已经被 JBoss 收为旗下的 JBoss Drools 4.0 规则引擎 (在 3.0 的时候曾经改名为 JBoss Rules,4.0 又改回来了)
jBPM 和 Drools 虽然同在 JBoss 旗下,不过他们目前并没有很好的进行整合,所以我们还是要利用它们系统系统的一些功能来做整合,同样也是实现 AssignmentHandler 接口,不过另外我们利用了 jBPM 里面的一个小小的技巧。看一下这段配置:
<swimlane name="agent">
<assignment class="org.agilejava.workflow.drools.RulesAssignmentHandler">
<ruleName>AgentAssignmentRule</ruleName>
</assignment>
</swimlane>
红色标注的这段配置,我们可以理解为,在 RulesAssignmentHandler 这个类里有一个 ruleName 这样的属性,在初始化这个类的时候,jBPM 会把配置中 ruleName 的值 set 给 RulesAssignmentHandler 中 ruleName 的属性。
public class RulesAssignmentHandler implements AssignmentHandler {
protected String ruleName;
public String getRuleName() {
return ruleName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
protected RuleBase readRule(String ruleName) throws Exception {
// 到 classes 下的 /rules 下加载相应的文件
String rulePath = "/rules/" + ruleName + ".drl";
Resource resource = new ClassPathResource(rulePath);
Reader reader = new InputStreamReader(resource.getInputStream());
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl(reader);
Package pkg = builder.getPackage();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(pkg);
return ruleBase;
}
protected void initRuleContextData(ExecutionContext executionContext, WorkingMemory workingMemory) {
ContextInstance ci = executionContext.getContextInstance();
Map vars = ci.getVariables();
workingMemory.insert(ci);
workingMemory.insert(vars);
}
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
RuleBase ruleBase = readRule(ruleName);
WorkingMemory workingMemory = ruleBase.newStatefulSession();
// 为了简便操作,我只是拿了放入 ExecuteContext 中的 variable 进行逻辑处理
initRuleContextData(executionContext, workingMemory);
workingMemory.insert(assignable);
workingMemory.fireAllRules();
}
}
对应的 AgentAssignmentRule.drl 文件内容如下,假定 ExecutionContext 中有 price 这个 variable,我们判定当这个值 > 5000 的时候,我们将此任务分配给 senior_agent 来处理。
package org.agilejava.workflow
import java.util.Map
import org.jbpm.taskmgmt.exe.Assignable
rule "Assign Agent"
when
a : Assignable()
Map(this['price'] >= 5000)
then
a.setActorId("senior_agent");
end
就这样,我们就完成了最为简单的 jBPM 和 Drools 的整合,当然这种方式只是简单的利用了 jBPM 的一些特性来做的,我们每次都得指定这个 RulesAssignmentHandler,还是很麻烦的,更好的方式就是我们改写 ProcessDefinition.xml 的 parser,让 Drools 的规则定义成为和 swimlane, actor-id, expression 这样的分配方式同样级别的,让 Drools 成为 jBPM 的一等公民,这个以后研究好了再来和大家分享
其中一个主要的扩展点是针对 TaskNode 进行的用户的分配。TaskNode 是 jBPM 中一个非常重要的概念,一个任务节点可以包含若干个任务,不同的任务可以由不同的人来完成,任务实例被分配给 actorId 来完成,其中指定到人的分配工作就是 Assignment 要处理的,这也是我们需要定制的功能,为了实现用户的分配,我们需要实现 AssignmentHandler 接口,接口原型如下。
public interface AssignmentHandler extends Serializable {
void assign(Assignable assignable, ExecutionContext executionContext) throws Exception;
}
通常用代码实现的话,我们可以让一个类实现这个接口,并在 swimlane 或者 tasknode 中的 assignment 指定该类。但是,这样的灵活性是显然不够的,在系统的使用过程中,分配策略会不断的进行调整,因此我们需要更为灵活的解决方案,jBPM 本身可以使用基于 Bean Shell 的脚本来写分配策略,但是 Bean Shell 不是那么强大,我们需要更为强大的解决方案,因此,我们选用了已经被 JBoss 收为旗下的 JBoss Drools 4.0 规则引擎 (在 3.0 的时候曾经改名为 JBoss Rules,4.0 又改回来了)
jBPM 和 Drools 虽然同在 JBoss 旗下,不过他们目前并没有很好的进行整合,所以我们还是要利用它们系统系统的一些功能来做整合,同样也是实现 AssignmentHandler 接口,不过另外我们利用了 jBPM 里面的一个小小的技巧。看一下这段配置:
<swimlane name="agent">
<assignment class="org.agilejava.workflow.drools.RulesAssignmentHandler">
<ruleName>AgentAssignmentRule</ruleName>
</assignment>
</swimlane>
红色标注的这段配置,我们可以理解为,在 RulesAssignmentHandler 这个类里有一个 ruleName 这样的属性,在初始化这个类的时候,jBPM 会把配置中 ruleName 的值 set 给 RulesAssignmentHandler 中 ruleName 的属性。
public class RulesAssignmentHandler implements AssignmentHandler {
protected String ruleName;
public String getRuleName() {
return ruleName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
protected RuleBase readRule(String ruleName) throws Exception {
// 到 classes 下的 /rules 下加载相应的文件
String rulePath = "/rules/" + ruleName + ".drl";
Resource resource = new ClassPathResource(rulePath);
Reader reader = new InputStreamReader(resource.getInputStream());
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl(reader);
Package pkg = builder.getPackage();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(pkg);
return ruleBase;
}
protected void initRuleContextData(ExecutionContext executionContext, WorkingMemory workingMemory) {
ContextInstance ci = executionContext.getContextInstance();
Map vars = ci.getVariables();
workingMemory.insert(ci);
workingMemory.insert(vars);
}
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
RuleBase ruleBase = readRule(ruleName);
WorkingMemory workingMemory = ruleBase.newStatefulSession();
// 为了简便操作,我只是拿了放入 ExecuteContext 中的 variable 进行逻辑处理
initRuleContextData(executionContext, workingMemory);
workingMemory.insert(assignable);
workingMemory.fireAllRules();
}
}
对应的 AgentAssignmentRule.drl 文件内容如下,假定 ExecutionContext 中有 price 这个 variable,我们判定当这个值 > 5000 的时候,我们将此任务分配给 senior_agent 来处理。
package org.agilejava.workflow
import java.util.Map
import org.jbpm.taskmgmt.exe.Assignable
rule "Assign Agent"
when
a : Assignable()
Map(this['price'] >= 5000)
then
a.setActorId("senior_agent");
end
就这样,我们就完成了最为简单的 jBPM 和 Drools 的整合,当然这种方式只是简单的利用了 jBPM 的一些特性来做的,我们每次都得指定这个 RulesAssignmentHandler,还是很麻烦的,更好的方式就是我们改写 ProcessDefinition.xml 的 parser,让 Drools 的规则定义成为和 swimlane, actor-id, expression 这样的分配方式同样级别的,让 Drools 成为 jBPM 的一等公民,这个以后研究好了再来和大家分享