BPM核心业务是能够永久化流程执行. 对任务管理和个人的任务清单来说这个特性是非常有用的. jBPM 允许指明一段软件描述所有人的任务中处于等待状态的流程.
任务是流程定义的部分并且定义在流程执行时间任务实例怎样生成和分配.
任务可以在process-definition中 用 task-node s定义. 最常用的方式是用一个task-node定义一个或多个任务. 这种情况下 task-node 代表一个由用户完成的任务并且流程执行将一直等待知道参与者完成这个任务. 当参与者完成任务,流程执行将继续.当多个任务在 task-node被指定 , 默认的行为是等待所有的任务被完成.
任务也可以在 process-definition被指定 . 任务在流程定义中指定可以通过名字查找(lookup)并且可以在 task-nodes里被引用(referenced)后者在动作里被使用(used) . 事实上, 所有任务 (在task-nodes里也是) 可以通过给一个名字在流程定义中查找到.
任务名字必须是唯一的在整个流程定义中. 任务可以给优先级别. 这个优先级别将用做初始化每个任务实例建立时的优先级. TaskInstances可以在以后修改这个优先级别.
任务实例可以被分配给一个actorId (java.lang.String). 所有任务实例被保存在数据库中的一张表中(JBPM_TASKINSTANCE).可以通过给定的actorId查询这个表得到所有的任务实例, 将会得到特定用户的任务清单.
jBPM 任务清单机制可以组合jBPM tasks 和其他任务, 甚至当这些任务同流程执行不相关. 这样jBPM 开发者可以容易组合jBPM-process-tasks 和其他在一个集中task-list-repository的应用程序的任务.
任务实例生命周期是直接了当的: 在建立后, 任务实例可以随意的被开始. 然后,任务实例可以被结束,意思是任务实例被标记为完成了.
为了灵活性,分派不是生命周期的一部分.因此任务实例可能被分配也可能不被分配.任务实例分配不影响任务实例的生命周期.
任务实例的建立典型的是由流程执行进入一个 task-node (通过方法 TaskMgmtInstance.createTaskInstance(...)). 然后, 一个用户接口组件将用TaskMgmtSession.findTaskInstancesByActorId(...) 查询数据库得到任务清单tasklists.然后, 在收集了用户的输入后,UI组件调用 TaskInstance.assign(String), TaskInstance.start() 或 TaskInstance.end(...).
任务实例通过date-properties维护它的状态包括: create, start和 end. 这些属性可以通过TaskInstance它们对应的getters方法.
当前,完成的任务实例被标记为end date 所以它们不能被随后的任务列表查询获得.但它们依然保存在 JBPM_TASKINSTANCE表里.
任务实例是在参与者tasklist的项目. 任务实例可作为信令(发射信号).一个信令任务实例就是一个任务实例在完成的时候可以发送一个信号给它的令牌去继续流程执行. 任务实例可以被阻塞,意思是说相关的令牌(= 执行路线)不允许离开 task-node,在任务实例被完成之前.默认的任务实例是信令(signalling )和非阻塞的(non-blocking).
在多个任务同一个task-node关联情况下,流程开发者可以指定怎样完成任务实例来影响流程的继续 . 下面是一个给task-node的singal-property的值的清单.
任务实例建立必须基于以上运行时间计算. 这种情况下,增加一个 ActionHandler 在task-node的 node-enter事件里 并且设置属性 create-tasks="false" . 下面是一个动作handler的实现:
public class CreateTasks implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
Token token = executionContext.getToken();
TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
TaskNode taskNode = (TaskNode) executionContext.getNode();
Task changeNappy = taskNode.getTask("change nappy");
// now, 2 task instances are created for the same task.
tmi.createTaskInstance(changeNappy, token);
tmi.createTaskInstance(changeNappy, token);
}
}如例子所展示,在task-node指定的任务被建立. 它们也可以在 process-definition指定并且通过 TaskMgmtDefinition来获取. TaskMgmtDefinition用任务管理信息 扩展(extends) ProcessDefinition .
标记任务实例的API完成用 TaskInstance.end() .随意地,你可以指定一个转换在end 方法里. 这种情况下这个任务实例完成将出发执行的继续,task-node 通过指定的转换离开..
流程定义包含任务节点. task-node包含0或多个任务 .任务是流程定义的静态描述部分. 在运行时间, 任务导致任务实例的建立.一个任务实例对应着个人任务列表里的一个入口.
在jBPM, 推 和 拉l 模式 (看下面) 的任务分派可以组合使用. 流程能计算任务的责任并把它"推"到他/她的任务清单里(tasklist).或者另外的方法, 把任务分配给参与者池, 这种情况下池中的每一个参与者可以"拉"任务并且把它们放到参与者个人的任务清单中(tasklist).
通过接口AssignmentHandler来分配任务实例:
public interface AssignmentHandler extends Serializable {
void assign( Assignable assignable, ExecutionContext executionContext );
}一个分配handler实现在任务实例建立的时候被调用. 在这个时候,任务实例可以分配给一个或多个参与者. AssignmentHandler 实现可以调用 Assignable 方法(setActorId或 setPooledActors )来分配任务. 可分配的要么是一个 TaskInstance 或者 SwimlaneInstance (= 流程角色).
public interface Assignable {
public void setActorId(String actorId);
public void setPooledActors(String[] pooledActors);
}TaskInstances 和 SwimlaneInstance s 两者都可以分配给指定的用户或参与者池. 分配一个TaskInstance给用户, 调用 Assignable.setActorId(String actorId) . 分配一个TaskInstance给候选参与者池, 调用 Assignable.setPooledActors(String[] actorIds).
流程中的每个任务都可以用在运行时间执行分配handler的实现关联起来.
当流程中多个任务被分配给同一个人或一组参与者,考虑 swimlane用途
允许建立重用的 AssignmentHandler s,每个 AssignmentHandler的用途可以在 processdefinition.xml配置. 参看 章节 13.2, “代理” 更多有关怎么添加分配handler的信息.
管理分配任务实例和分配swimlance实例到参与者的数据模型如下. 每个 TaskInstance 有actorId和一组被缓存的参与者.
actorId对任务负责, 当一组缓存的参与者代表候选者集合,如果他们得到任务就可以负责.actorId 和pooledActors 可以任意的组合.
任务实例的actorId 表明对指定任务责任的. TaskInstance 缓存的参与者是任务参与者的候选人. 典型的,TaskInstance 的actorId将引用一个一个用户. 缓存的参与者可以引用用户和组.
个人任务清单是所有所有指定actorId作为用户的 TaskInstance s.这个清单的获得是用过 TaskMgmtSession.findTaskInstances(String actorId).
另外一面, 共有的任务用来给从 共有的参与者中引用的用户. 获取集中的任务典型两个步骤操作: 1)从身份组件得到给定用户所有的组2)根据用户的actorId和从用户组得到的actorId来得到所有共有的任务.得到给定用户的所有共有的任务的清单通过方法 TaskMgmtSession.findPooledTaskInstances(String actorId) 或 TaskMgmtSession.findPooledTaskInstances(List actorIds) . 这些方法只返回任务实例的actorId是null或给定actorId之一匹配共有的的参与者其中一个.
为了防止多个用户在同一个共有的任务上工作, 更新TaskInstance 为用户的actorId就可以了. 做了这个, 任务实例将不会显示在共有的任务清单里,但只在用户的个人任务清单里. 设置TaskInstance 的actorId 为null,将会把任务实例放回共有的任务里.
泳道(swimlane)是流程角色.这是用来指明在一个流程中要完成多个任务中的同一个参与者的机制. 在第一个任务实例为给定的swimlane建立后,参与者将被同一个流程里所有随后都处于同一个swimlane中的任务记住.swimlane 因此有一个 分派 并且所有引用一个swimlane的任务将不会指定一个 分配.
当给定swimlane第一个任务建立时,swimlane 的 AssignmentHandler 被调用. Assignable 传递到 AssignmentHandler 将会成为 SwimlaneInstance . 需要知道的重点是所有分派是在给定swimlane任务实例上完成.将传播到swimlane实例.这个行为被实现作为默认的,因为个人将作为必要的流程角色履行任务.因此所有以后的任务实例分派到swimlane完成,自动到哪个用户.
Swimlane 是从UML活动图中借来的术语.
任务有动作关联. 有4个标准任务事件类型定义: task-create, task-assign, task-start 和 task-end.
task-create 在任务实例建立的时候被产生.
task-assign 当任务实例被分配的时候产生.注意当这个事件是动作被执行,你可以访问前一个参与者通过 executionContext.getTaskInstance().getPreviousActorId()
task-start 是当 TaskInstance.start()被调用时产生 . This can be used to indicate that the user is actually starting to work on this task instance. Starting a task is optional.
task-end 当 TaskInstance.end(...)被调用时产生 .这标记着任务的完成. 如果任务是相关于流程执行, 这个调用将出发流程执行继续.
因此任务可以有事件和动作, 异常handlers也可以对任务指定.更多关于异常处理的信息参看 章节7.6, “异常处理”.
在节点上,定时器可以指定在任务上 参看 章节10.1, “定时器”.
关于任务定时器指明的事情是 cancel-event 可以定制的.默认的, 任务上的定时器在任务结束时,将被放弃 (= 完成). 但在定时器的 cancel-event 属性, 流程开发人员能定制比如 task-assign 和 task-start . cancel-event支持多个事件. cancel-event 类型能被用逗号分隔的多个事件列表.
任务是用来收集用户输入的.但目前有许多用户接口可被用来用户的任务. 比如. 一个web应用程序, swing应用程序,及时消息,Email表单,... 因此任务控制器在流程变量(= process context) 和用户接口应用程序之间起到了桥的作用. 任务控制器为用户接口应用程序提供流程变量的视图.
任务控制器有2个责任:第一个, 从流程变量里提取信息. 从流程变量里提取的信息表示作为一个指定的参数的集合.参数用做来自用户接口表单的输入. 第二个责任是保存用户提交的参数到流程变量里.
在这个场景里, 这是流程变量和表单参数之间一对一的影射.这种情况,默认的jBPM任务控制器可以使用. 任务控制器在task元素中指定.
<task name="clean ceiling">
<controller>
<variable name="a" access="read" mapped-name="x" />
<variable name="b" access="read,write,required" mapped-name="y" />
<variable name="c" access="read,write" />
</controller>
</task>name 属性引用流程变量的. access 属性指明变量是否readable, writable and 或 required. 这个信息可以用于用户接口来产生正确的表单控制. access 属性是任意的并且默认 access 是 read,write .mapped-name 是任意的并且表示用户接口的标签用于表示一个参数.
task-node 能有多个任务并且 start-state 能有1个任务.
如果一个简单的一对一映射在流程变量和表单参数太约束,你可以写你自己的 TaskControllerHandler 实现. TaskControllerHandler接口:
public interface TaskControllerHandler extends Serializable {
List getTaskFormParameters(TaskInstance taskInstance);
void submitParameters(Map parameters, TaskInstance taskInstance);
}这里是在一个任务里如何配置你自己定制的任务控制器实现:
<task name="clean ceiling">
<controller class="com.yourcom.CleanCeilingTaskControllerHandler">
-- here goes your task controller handler configuration --
</controller>
</task>Task instances can be customized. The easiest way to do this is to create a subclass of TaskInstance. Then update the property jbpm.task.instance.class and specify the class name of your custom class that inherits from TaskInstance. Also create a hibernate mapping file for the subclass (using the hibernate extends="org.jbpm.taskmgmt.exe.TaskInstance"). Then add that mapping file to the list of mapping files in the hibernate.cfg.xml
用户管理,组和权限管理一般都称做身份管理. jBPM 包括可选的身份组件,可以弄你公司自己的身份存储数据来代替.
jBPM 身份管理组件包括组织知识模型. 任务分配典型的根据组织知识来完成. 因此这个隐含的组织知识模型,描述了用户,组,系统和它们之间的关系. 任意的,权限和角色也可以包含在组织模型中.数个学术研究尝试失败,证明没有通用的组织模型可以用来适合所有的组织.
jBPM 处理的方法是定义参与者作为实际的流程的实际参与者. 一个参与者用它的ID叫做actorId来标识. jBPM 只有关于actorId的知识并且为了灵活性他们表示为 java.lang.String s . 因此任何关于组织模型和数据结构的知识都不在jBPM 核心引擎之内.
作为扩充jBPM我们会提供(in the future)组件来管理简单的用户-角色模型. 这个用户和角色之间多对多的关系同J2EE和servlet规范中定义的一致因此他能作为一个新的开发开始点 .可以检查jbpm jira问题来追踪更多的细节.
注意用户-角色模型用在servlet,ejb和portlet规范里,是不足以处理任务分派. 在用户和角色之间模型是多对多关系.这不包含任何关于team以及组织结构用户如何涉及流程的的信息.
黄色类图是有关类表达下一节讨论的分配handlerre.
用户表示用户或服务. Group是任何一种类型的一群用户 .组能嵌套进team之间,业务单位之间和整个公司的关系模型 . 组有类型来区分组层次,比如头发颜色组. Membership s 表示多对多的在用户和组之间.membership可用来表示在公司中的职位. 成员的名字可以用来指明用户在组里履行的角色.
身份组件是在分派任务期间评估计算参与者的表达式的时候需要. 这里有个例子在流程定义中用分派表达:
<process-definition>
...
<task-node name='a'>
<task name='laundry'>
<assignment expression='previous --> group(hierarchy) --> member(boss)' />
</task>
<transition to='b' />
</task-node>
...分配语法表达如下:
first-term --> next-term --> next-term --> ... --> next-term
where
first-term ::= previous |
swimlane(swimlane-name) |
variable(variable-name) |
user(user-name) |
group(group-name)
and
next-term ::= group(group-type) |
member(role-name)
表达式分析是从左向右. first-term 指明了在身份模型中的用户或组. 以后的术语从中间的用户或组来计算下一个术语.
previous 意思是任务被分派给当前已经验证过的参与者.这意味着参与者在流程中执行前一个步骤.
swimlane(swimlane-name) 意思是用户或组从指明的swimlane instance取走.
variable(variable-name) 意思是用户或组从指明的variable instance取出. variable instance包含 java.lang.String,用户或组从身份组件中获得. 或者variable instance 包含用户或组对象.
user(user-name) 意思是用户是从身份组件里取出.
group(group-name)意思是组是从身份组件里取出的 .
当你想用你们公司组织自己的数据源比如公司用户数据库或LDAP系统,你可以拔除jBPM身份组件. 你需要做的唯一事情就是删除行 ...
<mapping resource="org/jbpm/identity/User.hbm.xml"/> <mapping resource="org/jbpm/identity/Group.hbm.xml"/> <mapping resource="org/jbpm/identity/Membership.hbm.xml"/>
从文件 hibernate.cfg.xml
ExpressionAssignmentHandler 依赖身份组件因此你不将不能用它. 这中情况下你想重用 ExpressionAssignmentHandler并且同你的用户数据绑定 ,你可以扩展 ExpressionAssignmentHandler然后重写 getExpressionSession方法.
protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);