1.
JPDL
的流程定义元素
1)
第一层:
GraphElement
这个容易理解
,
因为在画流程定义时
,
每个拖拉的对象都是一个
graph
的元素。
GraphElement
有四个属性:
(1)processDefine
表示当前元素属于哪个流程定义
(2)events
表示可以接收哪些
event
(3)name
名字
(4)exceptionHandlers
异常处理类集合
(List)
2)
第二层:
node
、
processDefinition
、
Transition
、
Task
它们都继承自
GraphElement
(1)processDefinition
表示流程定义
(implements NodeCollection),
它有下面的属性
:name
、
version
、
nodes
、
startState
。
nodes
表示流程中所有的
node
,
startState
用于启动流程时找到首节点。
(2)Transition
表示转移,它有三个属性:
from(Node)
,
to(Node)
,
supportedEventTypes
表示支持的
event
类型
(3)node
表示节点,它有四个属性:
leaving transitions
、
arriving transitions
、
action
、
superState
。
(4)Task
定义任务
3)
第三层:各种不同的
node
它们都继承自
node
。
Decision
、
EndState
、
Fork
、
Join
、
Merge
、
Milestone
、
InterleaveEnd
、
InterleaveStart
、
ProcessState
、
State
。
2.
jBPM
的
token
jbpm
中最重要的概念,应该是令牌
(Token)
和信令
(Signal)
。在整个流程实例运行过程中,我们可以迅速的利用
token
得到其当前的
current state
。在解决“并行”等(比如
Fork
)问题时,
jBpm
让
Token
对象维护了父子关系,这种关系在涉及到
Fork
的时候会产生。
jBpm
让
Token
这个对象身兼了多种使命:
(
1
)快速定位
current state
(
2
)用于
fork
,
join
算法
(
3
)用于告知任务执行者的任务索引。
如下代码
:
//pd
是
process definition,pi
是
process instance ProcessInstance
pi = new ProcessInstance( pd );
//
得到根令牌
Token token = pi.getRootToken();
//
发信令
token.signal();
Token
的
signal
方法也可以传入
transition
参数
,
这个方法把信令发送给
Token,
这样
,
令牌将被激活
,
并沿指定的
transition
离开当前的状态
(
如果没有指定
transition,
将沿缺省的
transition
离开当前状态
)
。
jbpm
是怎么实现的呢
?
其实很简单
:
1)Token
记录了当前的状态(
current state
)
,
只有当前的状态
(
或称节点
)
拥有该令牌
2)
向
TOKEN
发
signal
后
,
当前状态收到该
signal
3)
当前状态把令牌传给
signal
中指定的
transition
4)transition
收到令牌后
,
不强占
,
马上把令牌传给下个状态
.
5)
根据令牌的位置
,
流程的状态已经发生改变
.
3.
process definition
一个
process definition
代表了一个正式的业务流程,它以一个流程图为基础。这个流程图由
许多
node
和
transition
组成。每个
node
在这个流程图里都有着各自特殊的类型,这些不同的类型决定了
node
在运行时的不同行为。一个
process definition
只有一个
start state
。
4.
token
一个
token
代表了一条执行路径,它包含了这条执行路径的当前的执行状态(
current state
)。
5.
process instance
一个
process instance
(流程实例)即一个
process definition
(流程定义)的流程执行实例。一个
process definition
可以对应多个
process instance
。当一个
process instance
被创建的时候,一个主执行路径
token
同时被创建,这个
token
叫做
root token
,它指向流程定义的
start state
(
processDefinition.getStartState()==token.getNode()
)。
6.
signal
一个
signal
发送给
token
通知
token
继续流程的执行。如果
signal
没有指定
transition
,
token
将沿缺省的
transition
离开当前状态
,
如果
signal
指定
transition
,
token
将沿指定的
transition
离开当前的状态。看源代码可以看到发给
process instance
的
signal
其实都是发送给了
root token
。
7.
Actions
jbpm
提供了灵活的
action
,当流程执行,
token
进入
node
和
transition
时,会触发相应的一些
event
(事件)。在这些
event
上附上我们自己写的
action
,就会带动
action
的执行。
action
里是我们自己的相关
java
操作代码,非常方便。注意的是
event
(事件)是内置的,无法扩展。另外,
action
也可以直接挂在
node
上,而不依赖于
event
(事件)的触发,这个很重要。
8.
node
一个流程图由许多
node
和
transition
组成。每个
node
都有一种类型,这个类型决定了当流程执行到这个
node
时的不同行为。
jbpm
有一组
node type
可以供你选择,当然你可以定制自己
node
。
node
的作用
node
有两个主要的作用:
1
)执行
java
代码,比如说创建
task instance
(任务实例)、发出通知、更新数据库等等。很典型的就是在
node
上挂上我们的
action
2)
控制流程的执行:
A
、等待状态:流程进入到这个
node
时将处于等待状态,直到一个
signal
的发出
B
、流程将沿着一个
leaving transition
越过这个
node
,这种情况特殊一点,需要有个
action
挂在这个
node
上(注意这个
action
不是
event
触发的!),
action
中将会调用到
API
里
executionContext.leaveNode(String transitionName)
,
transitionName
即这里的
leaving transition
名字。
C
、创建新的执行路径:
很典型的就是
fork node
。流程在这里会分叉,产生新的执行路径。这样就创建了新的
token
,每个新的
token
代表一个新的执行路径。注意的是,这些新的
token
和产生前的
token
是父子关系!
D
、结束执行路径:一个
node
可以结束一条执行路径,这同样意味着相应的
token
的结束和流程的结束。
9.
流程图中的
node type
1)
task-node
一个
task-node
可以包含一个或多个
task
,这些
task
分配给特定的
user
。当流程执行到
task-node
时,
task instance
将会被创建,一个
task
对应一个
task instance
。
task instances
创建后,
task-node
就处于等待状态。当所有的
task instances
被特定的
user
执行完毕后,将会发出一个新的
signal
到
token
,即流程继续执行。
2)
state
state
是一个纯粹的
wait state(
等待状态)。它和
task-node
的区别就是它不会创建
task instances
。很典型的用法是,当进入这个节点时(通过绑定一个
action
到
node-enter event
),发送一条消息到外部的系统,然后流程就处于等待状态。外部系统完成一些操作后返回一条消息,这个消息触发一个
signal
到
token
,然后流程继续执行。(不常用)
3)
decision
当需要在流程中根据不同条件来判断执行不同路径时,就可以用
decision
节点。两种方法:最简单的是在
transitions
里增加
condition elements
(条件),
condition
是
beanshell script
写的,它返回一个
boolean
。当运行的时候,
decision
节点将会在它的
leaving transitions
里循环,同时比较
leaving transitions
里的
condition
,最先返回
'true'
的
condition
,那个
leaving transitions
将会被执行;作为选择,你可以实现
DecisionHandler
接口,它有一个
decide()
方法,该方法返回一个
String(leaving transition
的名字)。
4)
fork
fork
节点把一条执行路径分离成多条同时进行(并发)的执行路径,每条离开
fork
节点的路径产生一个子
token
。
5)
join
默认情况下,
join
节点会认为所有到达该节点的
token
都有着相同的父
token
。
join
节点会结束每一个到达该节点的
token,
当所有的子
token
都到达该节点后,父
token
会激活。当仍然有子
token
处于活动状态时,
join
节点是
wait state(
等待状态)。
6)
node
node
节点就是让你挂自己的
action
用的(注意:不是
event
触发!),当流程到达该节点时,
action
会被执行。你的
action
要实现
ActionHandler
接口。同样,在你的
action
里要控制流程。
10.
Actions
的说明
存在两种
action
,一种是
event
触发的
action
,一种是挂在
node
节点的
action
。要注意它们的区别,
event
触发的
action
无法控制流程,也就是说它无法决定流程经过这个节点后下一步将到哪一个
leaving transition
;而挂在
node
节点的
action
就不同,它可以控制流程。不管是哪一种
action
都要实现
ActionHandler
接口。
11.
Task
(任务)
jbpm
一个相当重要的功能就是对任务进行管理。
Task
(任务)是流程定义里的一部分,它决定了
task instance
的创建和分配。
Task
(任务)可以在
task-node
节点下定义,也可以挂在
process-definition
节点下。最普遍的方式是在
task-node
节点下定义一个或多个任务。默认情况下,流程在
task-node
节点会处于等待状态,直到所有的任务被执行完毕。任务的名称在整个流程中必须是唯一的。一个
TaskNode
对应多个
Task
。
对于这样的流程定义:
<task-node name="a"></task-node>
xml 代码
- < task-node name = 'a' >
- < task name = 'laundry' />
- < task name = 'dishes' />
- < task name = 'change nappy' />
- < transition to = 'b' />
- </ task-node >
只有当节点中的三个任务都完成后,流程才进入后面的节点
对于这样的流程定义:
xml 代码
<transition to="b"> </transition>
- < task-node name = 'a' signal = 'first' > >
- < task name = 'laundry' />
- < task name = 'dishes' />
- < task name = 'change nappy' />
- < transition to = 'b' />
- </ task-node >
当第一个任务完成后,
token
就指向后面的节点
对于这样的流程定义:
<task-node name="a" signal="never"></task-node>
xml 代码
- < task-node name = 'a' signal = 'never' > >
- < task name = 'laundry' />
- < task name = 'dishes' />
- < task name = 'change nappy' />
- < transition to = 'b' />
- </ task-node >
三个任务都完成后,
token
仍然不会指向后面的节点;需要自己手动调用
processInstance.signal()
才会驱动流程到下面的节点
。
对于这样的流程定义:
<task-node name="a" signal="unsynchronized"></task-node>
xml 代码
- < task-node name = 'a' signal = 'unsynchronized' > >
- < task name = 'laundry' />
- < task name = 'dishes' />
- < task name = 'change nappy' />
- < transition to = 'b' />
- </ task-node >
token
不会在本节点停留,而是直接到后面的节点
12.
jbpm
的任务管理实现
一个
Task instance
(任务实例)可以被分配给一个
actorId (java.lang.String)
。所有的
Task instance
都被保存在数据库中的表
jbpm_taskinstance
里。当你想得到特定用户的任务清单时,你就可以通过一个与用户关联的
actorId
来查询这张表。
一个流程定义有一个
TaskMgmtDefinition
;一个
TaskMgmtDefinition
对应多个
swimlane,
同时对应多个
task;
一个
swimlane
有多个
task,
可以从
TaskMgmtDefinition
中通过
task
的名称直接获取相应的
task;
swimlane
对象有四个属性,分别是
name
(名字)、
assignmentDelegation
(分配代理类)、
taskMgmtDefinition
、
tasks
(
Set
对应多个
task),
可以增加
task
task
对象主要的属性:
taskMgmtDefinition
、
swimlane
、
assignmentDelegation
、
taskNode
,需要注意的是
swimlane
和
assignmentDelegation
中间只是可以一个属性有值,因为它们都和任务的分配有关系。
一个流程实例有一个
TaskMgmtInstance
;一个
TaskMgmtInstance
对应多个
swimlaneInstance,
同时对应多个
taskInstance;
一个
swimlaneInstance
有多个
taskInstance,
可以从
TaskMgmtInstance
中直接获取相应的
taskInstance;
swimlaneInstance
对象主要有五个属性,分别是
name
、
actorId
、
pooledActors
(
Set
)、
swimlane
、
taskMgmtInstance
。
taskInstance
对象的主要属性:
name
、
actorId
、
task
、
swimlaneInstance
、
taskMgmtInstance
、
pooledActors
。
当对任务进行分配时,一般需要实现
AssignmentHandler
这个接口,这个接口的方法只有一个:
void assign( Assignable assignable, ExecutionContext executionContext ) throws Exception;
一个典型的实现(把名字是
'change nappy'
的任务交给
NappyAssignmentHandler
这个类来分配)
xml 代码
- < task name = 'change nappy' >
- < assignment class = 'org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler' />
- task > NappyAssignmentHandler 类:
java 代码
- public void assign(Assignable assignable, ExecutionContext executionContext) {
- assignable.setActorId( "papa" );
- }
同样,
Assignable
只是一个接口,它有两个方法:
setActorId()
和
setPooledActors()
,
Assignable
的具体实现类也是两个:
swimlaneInstancehe
和
taskInstance
。这样就不不难理解整个任务分配流程了:
1
、流程进入
TaskNode
节点,执行
TaskNode
类的
execute()
方法,该方法首先获得
TaskMgmtInstance
实例,然后通过它来创建
TaskInstance
。
taskMgmtInstance.createTaskInstance(task, executionContext);
2
、在上面的
createTaskInstance(task, executionContext)
里,该方法调用了
taskInstance.assign(executionContext)
对
taskInstance
进行分配。
3
、在
assign(executionContext)
方法里,首先会判断
task
属性里是否存在
swimlane
,如果有的话,这个
taskInstance
就会分配给
swimlane
指定的
ActorId
或
PooledActors
;如果不存在,再去找
task
属性里
assignmentDelegation
(分配代理类)通过代理类(即我们自己写的实现
AssignmentHandler
这个接口的类)指定
ActorId
或
PooledActors
。
13.
jbpm
的用户角色管理
jbpm
在用户角色管理上共设计了四个类:
Entity
、
Membership
、
Group
、
User
。
Entity
类是其他三个类的父类,它包含了两个属性:
name(String)
、
permissions(Set)
;
User
类继承
Entity
类,包含三个属性:
password(String)
、
email(String)
、
memberships(Set)
;
Group
类继承
Entity
类,包含四个属性
: type(String)
、
parent(Group)
、
children(Set)
、
memberships(Set)
;
Membership
类继承
Entity
类,包含三个属性
:role(String)
、
user(User)
、
group(Group)
很明显,一个
user
对应一个用户,一个
group
对应一个用户组,它们之间通过
membership
关联,并且一个
user
可以属于多个不同类型(
type)
的
group
,
user
和
group
之间是多对多的关系。
Membership
类的
role
属性个人感觉用途不大,反倒是
name
属性代表了
user
在
group
里的
role
(角色)。