下面开始接触 JBPM 的 JPDL 部分:
在 jpdl 参考手册中主要介绍了 processdefinition.xml 文件的格式( schema )
一、 Process archive:
如前面所描述的, process archive 是商务流程的规则描述。它被打成 jar 包,通常以扩展名 .par 结束, jbpm 识别一个流程需要三种类型的文件数据:
1、 业务流程的正式声明:在 jpdl 中,是以 processdefinition.xml 文件来表达。这一章节我们就来解析这个文件的格式。
2、 设计逻辑:在流程上加上规划逻辑这些也是在 processdefinition.xml 中给予描述的,在 process archive 中可以嵌套 java-classes 。所有在 process archive 中的 classes 应该放在子目录 /classes 中。
3、 其他资源文件。作为工作流引擎的客户端,在运行时你可能想要在流程中包含一些资源文件的变量。例如。窗体( form )的描述与某人执行任务相关联。 Jbpm 不能在你想要包含在一个流程定义中的资源文件的类型上做任何手脚。
二、 version 机制(此处 versioning 可以大致理解为:一个 process archive 部署后转变成过程定义的过程。)(此部分需要参考原文理解。感觉不对劲)
作为最最基本的, jbpm 的翻译机制遵从下面原理:
l 每次 process archive 部署的时候,一个新的流程定义就在 jbpm 数据库中被创建。
l 在部署的时候, jbpm 安排一个 version 号码(数字)给过程定义。只有当过程名与被定义的号相同时候才会执行 Process archives 。为了实现安排 version 号码,如果它是第一个 version jbpm 采取 1+ (the highest version number of the current process definitions with the same name) 或者 1 。从 jbpm-api 中可以通过一个给定的 name 查找最近的过程定义。
l 在一个给定的定义执行某一个过程实例,过程实例将会(等到同个定义结束前)一直在这个定义中保持执行状态。
l 通过这种方式能通过最近定义启动过程并且在相同定义中的完整的生命周期中保持运行状态。
l 注意: jbpm 可以把一个设计的程序逻辑与一个过程相关联起来。通过在 process archive 中包含类文件, jbpm 将会对于每个过程定义把 classes 分离出来。
三、 Processdefinition.xml 格式
文档类型定义:
<!DOCTYPE process-definition PUBLIC
"-//jBpm/jBpm Mapping DTD 2.0 beta3//EN"
"http://jbpm.org/dtd/processdefinition-2.0-beta3.dtd">
the document type definition of processdefinition.xml
过程定义:
<!ELEMENT process-definition ( description?,
swimlane*,
type*,
start-state,
( state |
milestone |
process-state |
decision |
fork |
join
)*,
end-state,
action* ) >
<!ATTLIST process-definition name CDATA #REQUIRED >
dtd fragment for process-definition
状态:
<!ELEMENT state ( description?, assignment?, action*, transition+ ) >
<!ATTLIST state name CDATA #REQUIRED >
dtd fragment for a state
在 JBPM 中,状态( state )这个术语与 FSM (有限状态机)和 UML 状态图中有着同样的意思。
对商务流程进行建模的目的就是创建一个软件系统。我们考虑到过程定义是软件系统建立的一部分。所以 jbpm 中的状态术语是以一个软件系统的角度来解释的。
状态是 jbpm 的核心概念。在 jbpm 中当开始模仿一个流程,首要需要考虑的事情就是过程的状态。状态将成为你所设计的过程的基本框架。
以状态概念为核心的另外一个特殊原因是:状态在程序语言中没有容易重复混淆的概念。一个软件程序或者运行或者不运行。一个有代表性的商业过程都会与被分别执行的所设计的逻辑部分关联。 Jbpm 允许在相关联的程序逻辑中对状态建模。
Jpdl 也在状态之间定义了变迁,决定,分支,合并, milestone ( flow )。需要注意的,控制流定义在状态与状态之间。无论什么情况下,一个正常的设计逻辑是更合适的,过程开发人员就能够在一个过程事件上指定一个 action 。 Jbpl 与其他过程定义语言如 bpel 等之间的区别之一是, jpdl 具备以状态管理方式对 java 进行扩展和与程序语言最小交叠性的能力。
委派( assignment ):
<ELEMENT assignment EMPTY >
<!ATTLIST assignment swimlane CDATA #IMPLIED
assignment (optional |required) #IMPLIED
authentication (optional |required|verify) #IMPLIED >
dtd fragment for an assignment
当一个流程执行到某种状态时候,工作流引擎将会等到一个外部的trigger (通过调用jbpm 的api ,ExecutionService.endOfState(…) )。在这个方法里面,jbpm 将会算出流程实例的下一个状态。
所以一个状态能够依赖于一个外部参与者。外部参与者可以是一个人也可以是某个系统。有许多种方式来实现业务流程中的状态与任务相关联。这些任务基本上可以分为两组:
1、 基于客户端的委派
在这种策略中,jbpm 的用户管理他们自己用户的任务列表。在这种情况,jbpm 只是用一个执行引擎来控管有限状态机。Jbpm 的职责是计算并且追踪过程执行中的状态,然而客户端的职责就是确保每个人知道做了什么。然后客户端一般就是在一个给定的状态中找到过程实例(或者说tokens )
2、 基于流程的委派
(在jbpm2.0 beta3 之前支持这种委派策略)
在这种委派策略中,jbpm 将会担负给参与者安排状态并且追踪任务列表的职责。在jbpm 中,流程在执行过程中是以一个token 来进行跟踪的。一个token 有一个指针来指向状态和一个参与者。在jbpm 中一个参与者总是引用java.lant.string 。为了详细说明jbpm 如何必须安排tokens 给参与者们,一些事件与此相关。接下来一个一个介绍:
只要一个客户端启动一个过程实例或者发出结束状态的信号,jbpm 都会计算过程实例的下一个状态。
首要的事情是关于这些api 方法中的第一个参数:参与者。这个参与用来识别谁来执行。
在启动一个新的过程实例的情况下,一个root-token 在开始状态中被创建好。在结束一个状态的情况下,一个tokenid 需要以一个某种状态中的参数形式指定。然后,jbpm 将会开始为token 计算下一个新状态。为了简单起见,我们忽略并发这种情况。Token 将会经历变迁和节点,然后抵达下一个状态节点。在那时,一个token 需要安排一个参与者。选择一个参与者之后,jbpm 将通过token 和调用的方法(开始过程实例或者结束状态)的返回值进行关联选择的参与者
所以当一个状态中的token 具备有一个参与者时,那就意味着过程实例的执行是在等待这个参与者提供一个外部trigger 给jbpm 引擎。在这种情形下,过程中的状态对应一个用户的任务,jbpm 通过检索所有已经安排给指定的参与者的tokens 计算任务列表。现在,参与者能够从列表中选择一个token 和发出结束状态信号量。
Assignmentn (委派)有许多属性:assignment 和authentication 。Assingement 属性可以有两个值:optional 和required 。Required 意思指当执行到某一个状态时,jbpm 将会检测是否把一个token 安排了一个参与者。如果非法则会抛出AssignmentException 的异常。如果assingment 是optional (=default ),当到达一个状态时jbpm 就允许离开一个未安排的token 。属性authentication 指定约束条件来限定哪个参与者可以发出结束状态的信号。参与者通过endOfState 方法中的参数actorid 来指定。如果optional=default 意思指对于指定谁来担当结束状态并不是必须的。Required 意味着一个参与者需要被指定。Vertify 意味着结束状态只可以安排给已经安排token 的参与者。
更多信息请查看群组委派。查看faqs 。
Swimlane
<!ELEMENT swimlane ( description?, delegation ? ) >
<!ATTLIST swimlane name CDATA #REQUIRED >
dtd fragment for swimlane
非常典型的情况,一个人具备一个流程中多个状态的职责。在 jbpm 中通过创建一个 swimlane 并且通过 swimlane 安排状态给参与者( actor )。一个业务流程中的 swimlane 可以被看做为一个针对参与者的角色名字。 Jbpm 对 swimlane 的解释与 UML1.5 中的术语 swimlane 解释一致。首次执行到达一个状态,给定一个 swimlane ,就会算出参与者。
public interface AssignmentHandler {
String selectActor( AssignmentContext assignerContext );
}
the AssignmentHandler interface
在 swimalne 中的 delegation 标签指向一个 AssignmentHandler 的具体实现。
然后那个参与者( actor )以 swimlane 中同名的方式被存储为过程变量。下次当一个 process archives 在一个状态,利用 swimlane , jbpm 引擎将会检测变量并且安排 token 给参与者(存储在变量中的)
计算的结果是存储一个 swimlane 同名的过程变量。所以当下一个状态到达相同的 swimlane 时,状态就会安排给相同的 actor ,而不再用 AssignmentHandler 。因为在 swimlane 和参与者之间的关联已经存储在变量中,而且可以通过更新变量来改变 swimlane 参与者( actor )之间的关联关系。
变量和类型:
<!ELEMENT type ( description?, (delegation |transient), variable* ) >
<!ATTLIST type java-type CDATA #IMPLIED >
<!ELEMENT transient EMPTY >
<!ELEMENT variable EMPTY >
<!ATTLIST variable name CDATA #REQUIRED >
dtd fragment for type and variable
一个是变量是一种key-value 对。它与过程实例(一次过程执行)相关联。Key 是java.lang.string ,value 是任何java 类型的任何pojo 。所以任何是java 类型,即使不给jbpm 知道也能被应用到变量中。
变量存储过程实例的上下文信息(context )。变量可以通过下面几种方式进行设置读取:
- ExecutionService.startProcessInstance( String actorId, Long definitionId, Map variables , String transitionName )
- ExecutionService.endOfState( String actorId, Long tokenId, Map variables , String transitionName )
- ExecutionService.setVariables( String actorId, Long tokenId, Map variables )
- ExecutionContext.setVariable( String name, Object value )
- ExecutionService.getVariables( String actorId, Long tokenId )
- ExecutionContext.getVariable( String name )
当设计变量时候,jbpm 尽量模仿java.util.map 的语义。这一点可以通过jbpm-api 来了解。也就是说一个变量只能当它被插入时被赋值,任何java 类型都可以作为变量中的value 。
一个type 描述jbpm 如何存储变量的值到数据库中。Jbpm 有一个文本域用来存储值,中间以serializer 来实现文本和对象之间的转换。
public interface Serializer {
String serialize( Object object );
Object deserialize( String text );
}
the Serializer interface
一个type 可以被看做一个serializer 的引用。Jbpm 包含了一些默认serializer 的实现,其中包括下面java 类型:
java.lang.String
java.lang.Long
java.lang.Double
by default supported java-types
变量类型匹配:
Java 类型声明的变量已经被默认支持,而不用在processdefinition.xml 声明。Jbpm 有个自动转换机制:当一个变量初始化后,jbpm 按照下面步骤,首先检测java 类型。如果它默认被java 支持,那么就可以正常使用,否则,jbpm 检查声明的类型是否与processdefinition.xml 中java-type 属性定义的变量是否一致。在匹配过程中jbpm 也考虑变量值的父类。如果没有找不到这种类型,jbpm 就把这样的变量作为transient (瞬间)变量对待。为了避免jbpm 不得不执行匹配过程,你可以把变量指定到它各自类型中去。
Transient (瞬间)变量:
一些变量并不需要做持久层处理存储在数据库中,jbpm 支持瞬时变量。这样的变量不存储到数据库中并且只可以在jbpm-api 的方法中使用。换句话说,瞬时变量的使用范围是:jbpm-api 的方法。