第 8 章 任务管理
Tailoring 的核心角色是 保留 进程的执行。当一个希望为人员管理任务和任务列表时,此功能非常有用。其中一个软件可以指定一个描述整个过程的软件。此类软件对于人工 任务可以处于等待状态。
8.1. 任务 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
任务是 进程定义的一部分。它们定义如何在进程执行过程中创建和分配任务实例。
在任务
节点和 进程确定的 中定义任务。最常见的方法是在任务 节点 中定义一个或多个任务。在这种情况下,task-node 代表用户要获取的任务,进程执行应等到执行者完成任务。当执行者完成任务时,进程执行将继续。当在任务 节点 中指定更多任务时,默认的行为是等待所有任务都已结束。
另外,还可在
进程确定时指定任务。以这种方式指定的任务可以通过搜索其名称来查找。还可以从 任务节点内引用它们,或者从操作中使用它们。实际上,给定名称的 每个任务(或任务节点)都可以在 进程 确定中找到。
确保每个任务名称都是唯一的。此外,为任务赋予
优先级。这将作为为此任务创建的每个任务实例的初始优先级。(之后任务实例可以更改此初始优先级。)
8.2. 任务实例 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
可将任务实例分配给
actorId (java.lang.String)。每个任务实例都存储在一个表中(J114_TASKINSTANCE.) 查询此表以获取给定 actorId 的每个任务实例,以获取该特定用户的任务列表。
使用 jBPM 任务列表机制将 jBPM 任务与其他任务相结合,即使其他任务与进程执行不相关。这样,一个可以轻松地将 lsblk-process-tasks 与一个中央化存储库中的其他应用程序任务合并。
8.2.1. 任务实例生命周期 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
任务实例生命周期很简单:创建后,可以启动实例。然后,可以结束它们,这意味着它们将标记为完成。
注意
为了获得灵活性,分配 不是生命周期的一部分。
- 任务实例通常在进程执行进入
任务节点时创建(通过TaskMgmtInstance.createTaskInstance(...)方法)。 - 然后,用户界面组件会查询数据库以获取任务列表。它使用
TaskMgmtSession.findTaskInstancesByActorId(...)方法完成此操作。 - 然后,在从用户收集输入后,UI 组件会调用
TaskInstance.assign(String)、TaskInstance.start()或TaskInstance.end(...)。
任务实例通过三个 date-properties 来维护其状态:
createstartend
通过对应的"getters"访问这些属性,可在
TaskInstance 中找到。
完成的任务实例标记为结束日期,以便在后续查询搜索任务列表时不会获取它们。但是,完成的任务保留在 J114
_TASKINSTANCE 表中。
8.2.2. 任务实例和图形执行 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
任务实例 是执行者任务列表中的项目。信号任务实例是一个任务实例,可在完成后向其令牌发送信号,以继续进程执行。阻塞任务实例是不允许相关的令牌(执行路径)在任务实例完成前离开任务
节点。默认情况下,任务实例配置为信号和非阻塞。
如果多个任务实例与任务节点关联,则进程开发人员可以指定完成任务实例的过程会影响进程持续处理的方法。
为 任务节点的signal- property 指定任何这些值:
- last
- 这是默认值。最后一次任务实例完成后,它会继续执行。如果此节点没有创建任何任务,则执行将继续。
- last-wait
- 这会在最后一次任务实例完成后执行。如果此节点没有创建任何任务,则执行会等待任务节点,直到任务创建为止。
- first
- 在第一次任务实例完成后,这将继续执行。如果此节点条目上没有创建任何任务,则执行将继续。
- first-wait
- 在第一次任务实例完成后,这将继续执行。如果此节点没有创建任何任务,则执行会等待任务节点,直到任务创建为止。
- unsynchronized
- 在这种情况下,无论任务是否创建还是未完成,执行始终将继续。
- never
- 在这种情况下,执行永远不会继续,无论任务是否创建还是仍然未完成。
任务实例创建可以基于运行时计算。在这些情况下,在任务
节点的node- enter 事件中添加 ActionHandler 并设置 create-tasks="false"。下面是一个示例:
在这里,要创建的任务
在任务节点 中指定。它们也可以在 process-definition 中指定,并从 TaskMgmtDefinition 获取。(TaskMgmtDefinition 通过添加任务管理信息来扩展进程定义。)
TaskInstance.end() 方法用于将任务实例标记为完成。可以选择在结束方法中指定转换。如果完成此任务实例会触发执行的延续,则 任务节点会停留在 指定的转换中。
8.3. 分配 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
进程定义包含任务节点。
任务节点 包含零个或更多任务。任务是进程定义的一部分的静态描述。在运行时,执行任务会导致创建任务实例。任务实例对应于个人任务列表中的一个条目。
使用 jBPM 时,可以应用任务分配的推送(个人任务列表)和拉取(组任务列表)模型。此过程决定了负责某个任务并将其推送到其任务列表中的任务。也可以将任务分配给执行者池,在这种情况下,每个参与者都会拉取任务并将其放入其个人任务列表中。
8.3.1. 分配接口 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
通过
AssignmentHandler 接口分配任务实例:
public interface AssignmentHandler extends Serializable {
void assign( Assignable assignable, ExecutionContext executionContext );
}
public interface AssignmentHandler extends Serializable {
void assign( Assignable assignable, ExecutionContext executionContext );
}
创建任务实例时,会调用分配处理器实施。此时,任务实例被分配给一个或多个参与者。
AssignmentHandler 实现调用可分配方法(setActorId 或 setPooledActors)来分配任务。可分配的项目可以是 TaskInstance 或 SwimlaneInstance (即进程角色)。
public interface Assignable {
public void setActorId(String actorId);
public void setPooledActors(String[] pooledActors);
}
public interface Assignable {
public void setActorId(String actorId);
public void setPooledActors(String[] pooledActors);
}
TaskInstances 和 SwimlaneInstances 都可以分配给特定用户或 actors 池。要为用户分配一个 TaskInstance,请调用 Assignable.setActorId(String actorId)。要为候选者池分配一个 TaskInstance,请调用 Assignable.setPooledActors(String[] actorIds)。
一个可以将进程定义中的每个任务与处理程序实施关联,以便在运行时执行分配。
当一个进程中的多个任务应该分配给同一人或一组参与者时,请考虑使用 swimlane,请参阅 第 8.6 节 “ Swimlanes ”。
8.3.2. 分配数据模型 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
用于管理任务实例分配的数据模型,swimlane 实例分配给 actors,如下所示。每个
TaskInstance 都有一个 actorId 和一组池的 actors。
图 8.1. 分配模型类图
actorId 负责该任务,而池的 actors 代表一组候选者,负责他们是否负责执行该任务。actorId 和 pooledActors 都是可选的,也可以合并。
8.3.3. 个人任务列表 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
个人任务列表表示分配给特定个人的所有任务实例。这通过
TaskInstance 中存在 actorId 属性来表示。使用以下方法之一将 TaskInstance 放置到某人的任务列表中:
- 在 task 元素的
actor-id属性中指定表达式 - 使用代码中任何位置的
TaskInstance.setActorId(String)方法 - 在
AssignmentHandler中使用assignable.setActorId(String)
要获取给定用户的个人任务列表,请使用
TaskMgmtSession.findTaskInstances(String actorId)。
8.3.4. 组任务列表 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
池的执行者是提供该任务的候选者组。一个候选者必须接受它。如果很多人开始处理同一任务,用户无法立即开始任务。为防止这种情况,用户只能从组任务列表中获取任务实例,并将它们移到他们的个人任务列表中。只有在将任务放在用户个人任务列表中时,才会将其放在她可以开始处理它的个人任务列表中。
要将
taskInstance 放在某人组任务列表中,请将用户的 actorId 或用户的 groupIds 添加到 pooledActorIds 中。要指定池的参与者,请使用以下方法之一:
- 在进程中的 task 元素的属性
pooled-actor-ids中指定表达式 - 使用代码中的任何位置的
TaskInstance.setPooledActorIds(String[]) - 在 AssignmentHandler 中使用
assignable.setPooledActorIds(String[])
要获取给定用户的组任务列表,请创建一个集合,其中包含用户的 actorId 以及用户所属的所有组。使用
TaskMgmtSession.findPooledTaskInstances(String actorId) 或 TaskMgmtSession.findPooledTaskInstances(List actorIds) 搜索不在个人任务列表中的任务实例(actorId==null),并在池 actorId 之间有匹配项。
注意
该软件以这种方式将身份组件与 jBPM 任务分配分开。Tailoring 仅将字符串存储为 actorId。它不知道用户和组或任何其他身份信息之间的关系。
actorId 始终覆盖池的 actors。因此,具有 actorId 和
pooledActorIds 列表的任务Instance 只会显示在执行者个人任务列表中。通过将 taskInstance 的actorId 属性设置为 null 来保留 pooledActorIds,以便将任务实例重新放入组中。
8.4. 任务实例变量 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
任务实例可以拥有自己的一组变量,也可以"查看"进程变量。任务实例通常在执行路径(令牌)中创建。这会在令牌和任务实例之间创建一个父子关系,这与令牌本身之间的父子关系类似。请注意,适用正常的范围规则。
使用 控制器 在任务实例范围和进程范围变量之间创建、填充和提交变量。
这意味着任务实例可以"查看"自己的变量以及相关令牌的所有变量。
控制器可用于在任务实例范围和进程范围变量之间填充和提交变量。
8.5. 任务控制器 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
创建任务实例时,可以使用任务控制器填充任务实例变量。当任务实例终止时,可以使用任务控制器将属于它们的数据提交到进程变量。
注意
任务控制器的使用是可选的。任务实例还可以"查看"与其令牌相关的进程变量。使用任务控制器获取这些任务:
- 创建任务实例变量副本,以便在进程完成前对它们进行中间更新不会影响进程变量。此时,副本被提交到进程变量中。
- 任务实例变量与进程变量没有一对一的关系。例如,如果流程在
3 月有名为 Sales 的变量,则任务实例表单可能需要显示这三个月的平均销售销售。2销售方面月销售和
任务从用户收集输入。但是,有许多用户界面可用于向用户展示任务。例如,一个 Web 应用,swing 应用,一个即时的 messenger,电子邮件表单...因此,任务控制器在进程变量(=process 上下文)和用户界面应用之间实现网桥。任务控制器向用户界面应用提供进程变量视图。
创建任务实例时,任务控制器会将进程变量(若有)转换为任务变量。任务变量充当用户界面表单的输入。用户输入本身存储在任务变量中。当用户结束任务时,任务控制器会根据任务实例数据更新进程变量。
图 8.2. 任务控制器
在简单的场景中,进程变量和表单参数之间有一个一对一的映射。在任务元素中指定任务控制器。在这种情况下,可以使用默认的 J114 任务控制器。它取
一个变量 元素列表,它表示进程变量在任务变量中如何复制。
下一示例演示了如何根据进程变量创建任务实例变量的独立副本:
name 属性引用进程变量的名称。mapped-name 是可选的,引用任务实例变量的名称。如果省略 mapped-name 属性,mapped-name 默认为名称。请注意,mapped-name 也用作 web 应用程序任务实例表单中字段的标签。
使用 access 属性指定在任务实例创建时复制的变量是否将被写回任务实例中的进程变量。(此信息可供用户界面用来生成正确的表单控制。) access 属性是可选的,默认访问是
read,write。
Task
-node 可以有多个任务,而一个 start-state 具有一个任务。
如果进程变量和表单参数之间的简单一对一映射太限,请创建一个自定义
TaskControllerHandler 实现。以下是它的接口:
public interface TaskControllerHandler extends Serializable {
void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
}
public interface TaskControllerHandler extends Serializable {
void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
}
这个代码示例演示了如何配置它:
8.6. Swimlanes 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
swimlane 是一个进程角色。使用此机制指定进程中的多个任务由同一执行者获取。创建给定 swimlane 的第一个任务实例后,对于同一 swimlane 中的每个后续任务,Actor 为"remembered"。因此,swimlane 有一个
分配。研究 第 8.3 节 “ 分配 ” 以了解更多信息。
创建给定 swimlane 中的第一项任务时,会调用
AssignmentHandler。传递给 AssignmentHandler 的 Assignable 项是 SwimlaneInstance。在给定 swimlane 中的任务实例上的每个分配都会传播到 swimlane 实例。这是默认行为,因为执行任务的人员将了解该特定进程。因此,该 swimlane 中的后续任务实例会自动分配给该用户。
8.7. Start Task 中的 Swimlane 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
可将 swimlane 与 start 任务关联。一个可以捕获进程发起方。
可以在 start-state 中指定任务,它将将其与 swimlane 关联。创建新任务实例时,当前经过身份验证的执行者将通过
Authentication.getAuthenticatedActorId() 方法捕获。actor 存储在起始任务的 swimlane 中。
使用正常方法将变量添加到启动任务中。这样做来定义与任务关联的表单。请参阅 第 8.5 节 “ 任务控制器 ” 了解更多信息。
8.8. 任务事件 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
一个用于将操作与任务关联。有四个标准事件类型:
task-create,在创建任务实例时触发。任务分配,它会在分配任务实例时触发。请注意,在这个事件中执行的操作中,可以使用executionContext.getTaskInstance().getPreviousActorId();方法访问之前的参与者。任务启动,它会在调用TaskInstance.start()方法时触发。使用此可选功能表示用户实际启动任务实例的工作。任务末尾,它会在调用TaskInstance.end(...)时触发。这会标记任务的完成。如果任务与进程执行相关,这个调用可能会触发进程执行的恢复。
注意
异常处理程序可以与任务关联,以获取有关此信息的更多信息,请参阅 第 6.7 节 “ 异常处理 ”。
8.9. 任务计时器 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
一个用于指定任务上的计时器。请参阅 第 9.1 节 “ timers ”。
可以为任务计时器自定义 cancel-event。默认情况下,任务计时器会在任务结束时取消,但带有计时器中的 cancel-event 属性,一个可以自定义它 task-assign 或 task-start。cancel-event 支持多个事件。要组合 cancel-event 类型,在属性的逗号分隔列表中指定它们。
8.10. 自定义任务实例 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
要自定义任务实例,请按照以下步骤执行:
- 创建
TaskInstance的子类 - 创建
org.jbpm.taskmgmt.TaskInstanceFactory实现 - 通过将 jbpm.task.instance.factory 配置属性设置为
jbpm.cfg.xml文件中的完全限定类名称来配置实施。 - 如果使用
TaskInstance的子类,请为子类创建一个 Hibernate 映射文件(使用extends="org.jbpm.taskmgmt.exe.TaskInstance" - 将该文件添加到
hibernate.cfg.xml中的列表中。
8.11. Identity 组件 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
管理用户、组和权限是 身份管理 的术语。jBPM 包括一个可选的身份组件。一个可轻松将其替换为一个公司自己的数据存储。
jBPM 身份管理组件了解组织模型,并使用它来分配任务。这个模型描述了用户、组、系统以及它们之间的关系。(可选)也可以包含权限和角色。
在进程中将 actor 定义为实际参与处理者来处理此问题。行动者通过其 ID 标识,称为 anctorId。jBPM 只了解 actorIds,它们表示为
java.lang.Strings,以获得最大的灵活性。因此,对组织模型和该数据的结构的了解超出了 Ice 核心引擎的范围。
作为 Ice 的扩展,我们将提供(未来)一个组件来管理该简单的用户角色模型。与角色和角色之间的许多关系与 J2EE 和 servlet 规范中定义的模型相同,它可以作为新开发中的起点。
请注意,在 servlet、ejb 和 portlet 规范中使用的用户角色模型不足,用于处理任务分配。该模型是用户和角色之间的多对多关系。这不包括有关团队的信息,以及涉及某一流程的用户的组织结构。
8.11.1. 身份模型 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
图 8.3. 身份模型类图
黄色的类是与表达式分配处理程序相关的类。
User 代表用户或一个服务。Group 是任何类型的用户组。组可以嵌套,以对团队、业务单元和整个公司之间的关系建模。组有一个类型来区分分级组和,例如 hair color 组。成员资格代表用户和组之间的多对多关系。成员资格可用于表示公司中的位置。成员资格的名称可用于指示用户在组中实现的角色。
8.11.2. 分配表达式 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
身份组件附带了一种实施,用于评估分配任务期间执行者计算的表达式。以下是在进程定义中使用分配表达式的示例:
8.11.2.1. 第一个术语 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
表达式从左到右解析。第一个术语指定身份
模型 中的用户或组。后续术语从中间用户或组计算下一个术语。
在以前的版本中,任务被分配给当前经过身份验证的执行者。这意味着,执行过程中前面的步骤的执行者。
swimlane (swimlane-name) 意味着从指定的 swimlane 实例获取用户或组。
variable (variable-name) 意味着从指定的变量实例获取用户或组。变量实例可以包含 java.lang.String,在这种情况下,从身份组件获取用户或组。或变量实例包含 User 或 Group 对象。
user (user-name) 表示给定用户从身份组件中获取。
group (group-name) 表示给定组是从身份组件中获取的。
8.11.2.2. 下一个术语 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
group (group-type) 获取用户的组。这意味着前面的术语必须产生 用户。它会在用户的所有成员资格中搜索给定 group-type 的组。
member (role-name) 获取为组执行给定角色的用户。以上术语必须产生 组。此术语搜索与给定角色名称匹配的组群成员资格的用户。
8.11.3. 删除身份组件 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
当您想使用您自己的数据源进行组织信息(如公司的用户数据库或 LDAP 系统)时,您可以删除 jBPM 身份组件。您需要做的唯一操作是确保您从
hibernate.cfg.xml 中删除以下行。
<mapping resource="org/jbpm/identity/User.hbm.xml"/> <mapping resource="org/jbpm/identity/Group.hbm.xml"/> <mapping resource="org/jbpm/identity/Membership.hbm.xml"/>
<mapping resource="org/jbpm/identity/User.hbm.xml"/>
<mapping resource="org/jbpm/identity/Group.hbm.xml"/>
<mapping resource="org/jbpm/identity/Membership.hbm.xml"/>
ExpressionAssignmentHandler 依赖于身份组件,因此您将无法将其使用。如果要重复使用 ExpressionAssignmentHandler 并将其绑定到用户数据,您可以从 ExpressionAssignmentHandler 扩展并覆盖方法 getExpressionSession。
protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);
protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);