通过jbpm源码分析jbpm引擎内核工作原理 & 流程图中的node type

finalcola 2010-01-27

Jbpm,他是jboss下的一个开源项目,是个基于petrinet理论为基础的工作流引擎。本文主要通过jbpm源代码分析下jbpm引擎内核工作原理。

Jbpm是基于微内核引擎的基础上扩展开发出来的工作流平台,其运行的核心包是在org.jbpm.graph下,在该包下又分有action、def、exe、log、node几个包,jbpm内核引擎实现逻辑主要存放在def、exe这两个包下,其他的包是基于此内核扩展出来的动作、模型和日志。

下面我们通过一个简单的例子来逐步的分析jbpm是如何工作的。看下面jbpm自带演示的一个hello流程(视乎大家都喜欢从hello实现开始^_^),代码如下:

publicvoidtestHelloWorldProcess(){

ProcessDefinitionprocessDefinition=ProcessDefinition.parseXmlString(

"<process-definition>"+

"<start-state>"+

"<transitionto='s'/>"+

"</start-state>"+

"<statename='s'>"+

"<transitionto='end'/>"+

"</state>"+

"<end-statename='end'/>"+

"</process-definition>"

);

ProcessInstanceprocessInstance=newProcessInstance(processDefinition);

Tokentoken=processInstance.getRootToken();

assertSame(processDefinition.getStartState(),token.getNode());

token.signal();

assertSame(processDefinition.getNode("s"),token.getNode());

token.signal();

assertSame(processDefinition.getNode("end"),token.getNode());

}

首先,我们定义个流程模板(ProcessDefinition),就是上面代码的ProcessDefinitionprocessDefinition=ProcessDefinition.parseXmlString(….);这段,在括号中是jbpm定义的流程,其中包括三个环节,分别是starts-state、state和end-state。parseXmlString()方法的主要功能是解析这段xml语言返回个流程模板对象(processDefinition)。

接着,通过流程实例类(ProcessInstance)来实例化个流程实例,通过传进来的流程模板对象创建ProcessInstanceprocessInstance=newProcessInstance(processDefinition)。我们来看看newProcessInstance(processDefinition)到底做了什么,通过查看ProcessInstance的源代码,可以看到其中主要的一段是

publicProcessInstance(ProcessDefinitionprocessDefinition){

//略去其他代码

this.processDefinition=processDefinition;//将流程模板对象付给流程实例

this.rootToken=newToken(this);//创建跟令牌

//略去其他代码

}

我们继续跟进Token这个类

publicToken(ProcessInstanceprocessInstance){

//主要一句如下

this.node=processInstance.getProcessDefinition().getStartState();

}

这样就实现了令牌绑定到开始节点。至此,一个流程实例就创建起来了,并且该流程实例走到了开始节点,即令牌所处的位置。

我们接着往下走token.signal()

publicvoidsignal(){

signal(node.getDefaultLeavingTransition(),newExecutionContext(this));

//这里的getDefaultLeavingTransition()如果有多条路径,则去第一条路径

}

voidsignal(Transitiontransition,ExecutionContextexecutionContext){

//省略其他代码

node.leave(executionContext,transition);

//省略其他代码

}

这里的node就是刚才令牌所在的开始节点,我们来看看jbpm是如何将令牌从开始节点移到下个节点的。

publicvoidleave(ExecutionContextexecutionContext,Transitiontransition){

Tokentoken=executionContext.getToken();

token.setNode(this);//此时令牌还在开始节点

executionContext.setTransition(transition);

//略去部分代码

executionContext.setTransitionSource(this);

transition.take(executionContext);//实现令牌的转移

}

我们来看看transition.take(..)方法做了什么

publicvoidtake(ExecutionContextexecutionContext){

//略去部分代码

to.enter(executionContext);//离开开始节点,进入到下个节点

}

大家可能会有点疑问,这个to节点是什么是否初始化的?其实在signal时有句node.getDefaultLeavingTransition(),这句返回Transition对象,该对象就已经初始化了to节点的对象。我们在跟进to.enter(..)

publicvoidenter(ExecutionContextexecutionContext){

Tokentoken=executionContext.getToken();

token.setNode(this);//此时令牌就到了名字为“s”的state节点

token.setNodeEnter(newDate());

executionContext.setTransition(null);

executionContext.setTransitionSource(null);

execute(executionContext);

}

在这段代码中的注释这句,真正实现了令牌从开始节点到下个节点了。

至此,jbpm工作流引擎的内部工作原理就介绍完了,其实这就是工作流引擎最核心的部分了,就是如何从一个环节转移到另一个环节。或许你会说“这么简单,我马上就可以写一个”,其实不然,上面我们所用的例子是十分简单的例子,其实在工作流联盟规范中还有其他复杂的节点模型,如split,join,subflow等。不过幸运的是这些复杂的节点模型jbpm都为我们提供了他自己的默认的实现,这些节点模型都在org.jbpm.graph.node包下。jbpm引擎中很好的抽象了节点模型Node类,大部分的复杂节点模型都继承自Node,我们也可以定制自己的节点,只要实现Node类的execute()方法即可方便的实现。其实从上面分析的代码可以看出,Node类主要的逻辑处理是在leave()、enter()和execute()三个方法,大家可以看下ProcessState,join,fork这些节点模型是如何实现的。

1、task-node

一个task-node可以包含一个或多个task,这些task分配给特定的user。当流程执行到task-node时,taskinstance将会被创建,一个task对应一个taskinstance。taskinstances创建后,task-node就处于等待状态。当所有的taskinstances被特定的user执行完毕后,将会发出一个新的signal到token,即流程继续执行。

2、state

state是一个纯粹的waitstate(等待状态)。它和task-node的区别就是它不会创建taskinstances。很典型的用法是,当进入这个节点时(通过绑定一个action到node-enterevent),发送一条消息到外部的系统,然后流程就处于等待状态。外部系统完成一些操作后返回一条消息,这个消息触发一个signal到token,然后流程继续执行。(不常用)

3、decision

当需要在流程中根据不同条件来判断执行不同路径时,就可以用decision节点。两种方法:最简单的是在transitions里增加conditionelements(条件),condition是beanshellscript写的,它返回一个boolean。当运行的时候,decision节点将会在它的leavingtransitions里循环,同时比较leavingtransitions里的condition,最先返回'true'的condition,那个leavingtransitions将会被执行;作为选择,你可以实现DecisionHandler接口,它有一个decide()方法,该方法返回一个String(leavingtransition的名字)。

4、fork

fork节点把一条执行路径分离成多条同时进行(并发)的执行路径,每条离开fork节点的路径产生一个子token。

5、join

默认情况下,join节点会认为所有到达该节点的token都有着相同的父token。join节点会结束每一个到达该节点的token,当所有的子token都到达该节点后,父token会激活。当仍然有子token处于活动状态时,join节点是waitstate(等待状态)。

6、node

node节点就是让你挂自己的action用的(注意:不是event触发!!),当流程到达该节点时,action会被执行。你的action要实现ActionHandler接口。同样,在你的action里要控制流程!

相关推荐