第 6 章  进程模型


6.1.  一些帮助定义

请阅读本节,了解您将在本书的剩余部分中使用的术语。
进程定义 代表了业务流程的正式规格,它基于 指示的图形。该图由节点和转换组成。图中的每个节点都是特定类型的。节点类型定义运行时的行为。进程定义只有一个启动状态。
令牌是 执行的一个路径。令牌是维护图中节点的指针的运行时概念。
进程实例 是进程定义的一个执行。创建进程实例时,会为执行的主路径生成令牌。此令牌称为进程实例的 root 令牌,它处于进程定义的启动状态。
信号指示令牌继续执行图形。当它收到未命名的信号时,令牌会将其当前节点保留为默认的 离开 节点。当在信号中指定 conversion -name 时,令牌会将其节点保留为指定的转换。提供给进程实例的信号被委派给 root 令牌。
令牌进入节点后,将执行该节点。节点本身负责进行图形执行。通过使令牌离开节点来实现图形执行持续。每种节点类型都可以为图形执行的持续实施不同的行为。不通过执行的节点将充当 状态
操作 是 Java 代码的一部分,在进程执行期间在事件上执行。该图 是软件要求通信的一个重要检测,但它只是正在生成的软件的一个视图(预测)。它隐藏了许多技术详情。操作是用来添加超过图形表示的技术详情的机制。发生图形后,就可以对操作进行解码。主要 事件类型 输入节点保留节点 并采用转换
了解这些定义后,请阅读了解流程模型如何工作。

6.2.  Process Graph

进程定义是由节点和转换组成的图形。此信息在 XML 中表示,并在名为 processdefinition.xml 的文件中找到。每个节点必须具有 类型 (示例状态为、决策分叉和 加入 )。 每个节点都有一组 离开的转换。可将名称提供给离开节点的转换,以便它们相互不同。例如,下图显示了辅助进程的进程图。

图 6.1. 辅助过程图

以下是 XML 中代表的同一辅助进程的进程图:
<process-definition>

  <start-state>
    <transition to="auction" />
  </start-state>
  
  <state name="auction">
    <transition name="auction ends" to="salefork" />
    <transition name="cancel" to="end" />
  </state>
  
  <fork name="salefork">
    <transition name="shipping" to="send item" />
    <transition name="billing" to="receive money" />
  </fork>
  
  <state name="send item">
    <transition to="receive item" />
  </state>

  <state name="receive item">
    <transition to="salejoin" />
  </state>
  
  <state name="receive money">
    <transition to="send money" />
  </state>

  <state name="send money">
    <transition to="salejoin" />
  </state>
  
  <join name="salejoin">
    <transition to="end" />
  </join>
  
  <end-state name="end" />
  
</process-definition>
Copy to Clipboard Toggle word wrap

6.3.  节点

进程图由节点和转换组成。每个节点都是特定类型的。节点类型决定了执行在运行时在节点中到达节点时会发生什么。Business Process Manager 提供了一组要使用的节点类型。或者,您可以编写自定义代码来实现特定的节点行为。

6.3.1.  节点责任

每个节点都有两个主要职责:首先,它可以执行普通 Java 代码,该代码通常与节点的功能相关。它的第二个责任是传递进程执行。
当节点试图传递进程执行时,节点可能会遇到以下选项。它将遵循最适用的课程:
  1. 它无法传播执行。(节点的行为为 等待状态 .
  2. 它可以在其中一个节点 离开转换时传播执行。(这意味着最初到达节点上的令牌通过 API 调用 executionContext.leaveNode(String) 传递。) 节点现在会在意义中自动执行一些自定义编程逻辑,然后在不等待的情况下自动继续执行进程。
  3. 节点可以"decide"来创建新令牌,每个令牌代表新的执行路径。这些新令牌的每个新令牌都可以在节点的 离开转换时启动。这种行为的一个很好的例子是 fork 节点
  4. 它可以结束执行的路径。这意味着令牌已完成。
  5. 它可以修改进程实例的整个 运行时结构。运行时结构是一个包含令牌树的进程实例,各自代表执行路径。节点可以创建和结束令牌,将每个令牌放在图形的节点中,并通过转换启动令牌。
商业流程管理器包含一组预先实现的节点类型,每种类型都有特定的配置和行为。但是,您还可以编写自己的节点行为,并在此过程中使用它。

6.3.2.  节点类型:任务节点

任务节点 代表要手动执行的一个或多个任务。因此,当执行过程到达节点时,任务实例将在属于工作流的列表中创建。之后,节点 将进入等待状态。当用户完成其任务时,将触发执行,使其恢复。

6.3.3.  节点类型:State

状态 是一个"裸机 bones" 等待状态。它不同于一个任务节点,不会为任何任务列表创建任何任务实例。如果进程正在等待外部系统,这非常有用。之后,该过程将进入等待状态。当外部系统发送响应信息时,通常会调用 token.signal(),触发进程执行的恢复。

6.3.4.  节点类型: 决策

有两种方法可以对决定建模,其选择为用户使用给用户自由裁量。这些选项是:
  1. 决策由流程决定,因此在进程定义中指定,
  2. 外部实体决定。
当决定由流程决定时,请使用 决策节点。以两种方式之一指定决策标准,最简单的方法是在转换中添加 condition 元素。(条件是 EL 表达式或 beanshell 脚本,它返回一个布尔值。)
在运行时,决策节点将会循环那些指定了条件的转换情况。它将首先按照 XML 中指定的顺序评估这些转换。使用条件解析为 true 的第一个转换。如果所有转换条件都解析为 false,则进行默认转换(在 XML 中第一个)。如果没有找到默认转换,则会抛出 JbpmException。
第二种方法是使用返回要采用的转换名称的表达式。使用 expression 属性指定决策上的表达式。这将需要解析到决策节点的 离开转换 之一。
另外,也可以使用决策上的 handler 元素,因为此元素用于指定可在决策节点上指定的 DecisionHandler 接口的实现。在这种情况下,决定由 Java 类计算,所选 离开转换decide 方法返回,该方法属于 DecisionHandler 实现。
当外部方采用决策时,始终使用多个转换,这样可离开 状态等待状态 节点。然后,外部触发器中可以提供离开转换,用于在 等待状态 完成后恢复执行(例如,可以是 Token.signal(String transitionName)TaskInstance.end(String transitionName)。)

6.3.5.  节点类型: Fork

fork 将执行的单一路径分成多个并发路径。默认情况下,分叉会为离开它的每个转换创建一个子令牌(通过创建到达 fork 中的令牌之间的父子关系)。)

6.3.6.  节点类型:加入

默认情况下,加入假定到达自身的所有令牌都是同一父项的子项。(这种情况在使用前所述的 fork 以及 fork 创建的所有令牌到达同一加入时发生。)
加入将进入它的每个令牌结束。然后,它将检查这些令牌的父项关系。当所有同级令牌都到达加入时,父令牌将传递到 离开转换。当仍处于同级令牌处于活跃状态时,加入将充当 等待状态

6.3.7.  节点类型:节点

使用此节点避免编写自定义代码。它只需要一个子元素操作,它会在执行节点中到达时运行。使用 actionhandler 编写的自定义代码可以执行任何操作,但请注意它也负责传递执行。(如需更多信息,请参阅 第 6.3.1 节 “ 节点责任 ”。)
当使用 Java API 为企业业务实施一些功能逻辑时,也可以使用此节点。最好以这种方式这样做,因为节点在进程的图形表示中保持可见。(使用操作添加在进程图形表示中不可见的代码。)

6.4.  Trans

转换同时具有源和目标节点。源节点由属性 from 表示,目的地由 to 表示。
(可选)可以为一个名称赋予一个转换。(商业流程管理器的最特性取决于获得唯一的名称。) 如果多个转换的名称相同,则首先进行它们。(如果节点上发生重复的转换名称,Map getLeavingTransitionsMap() 方法会返回比 List getLeavingTransitions() 少的元素。)

6.5. Actions

操作是 java 代码的一部分,在进程执行的事件上执行。该图是与软件要求通信中的重要检测。但是,图只是正在生成的软件的一个视图(项目)。它隐藏了许多技术详情。操作是在图形表示之外添加技术详情的机制。发生图形后,就可以对操作进行解码。这意味着 java 代码可以与图形关联,而无需更改图形的结构。主要事件类型正在进入节点,离开节点并进行转换。
重要
在事件中放置的操作与节点中放置的操作之间存在区别。事件触发时将放入事件的操作。它们无法影响对进程控制的流。(类似于 观察模式 . ) 相反,在节点上放置的操作负责传递执行。
阅读本节,以讨论对事件的操作示例。它演示了如何在给定转换时获取数据库更新。(数据库更新在技术上至关重要,但对业务而言并不重要。)

图 6.2. 数据库更新操作

public class RemoveEmployeeUpdate implements ActionHandler {
  public void execute(ExecutionContext ctx) throws Exception {
    // get the fired employee from the process variables.
    String firedEmployee =
      (String) ctx.getContextInstance().getVariable("fired employee");
    
    // by taking the same database connection as used for the jbpm
    // updates, we reuse the jbpm transaction for our database update.
    Connection connection =
    ctx.getProcessInstance().getJbpmSession().getSession().getConnection();
    Statement statement = connection.createStatement();
    statement.execute("DELETE FROM EMPLOYEE WHERE ...");
    statement.execute(); 
    statement.close();
  }
}
Copy to Clipboard Toggle word wrap
<process-definition name="yearly evaluation">
  <state name="fire employee">
    <transition to="collect badge">
      <action class="com.nomercy.hr.RemoveEmployeeUpdate" />
    </transition>
  </state>
  
  <state name="collect badge">
  
</process-definition>
Copy to Clipboard Toggle word wrap

6.5.1.  操作参考

操作可以被指定名称。这允许从指定操作的其他位置引用它们。命名的操作也可以作为 子元素 添加到进程定义中。
使用此功能来限制操作配置的重复。(这在操作有复杂配置或必须调度或执行运行时操作时特别有用。)

6.5.2.  事件

事件是 在执行过程中的特定时间。商业流程管理器的引擎将在 图形执行期间 "填充"事件,当软件计算下一状态时(换句话说,当它处理信号时)。 事件始终相对于进程定义中的元素。
大多数进程元素都可以触发不同类型的事件。例如,节点可以触发 node-enternode-leave 事件。(events 是操作的 "hooks"。每个事件都有一个操作列表。当 jBPM 引擎触发某个事件时,会执行操作列表。

6.5.3.  传递事件

超级状态 在进程定义的元素中创建一个父子关系。(处于超级状态的节点和转换将具有该超级状态作为父状态。顶级元素具有进程定义作为其父项,它们本身没有进一步的父项。) 触发事件时,事件将传递父层次结构。这允许它捕获进程中的所有转换事件,并通过集中位置将操作与这些事件相关联。

6.5.4.  脚本

脚本是 执行 Beanshell 脚本的操作。(有关 Beanshell 的更多信息,请参阅 http://www.beanshell.org/.) 默认情况下,所有进程变量都作为脚本变量提供,但不会将脚本变量写入进程变量。可用的 script-variables 如下:
  • executionContext
  • token
  • node
  • task
  • taskInstance
<process-definition>
  <event type="node-enter">
    <script>
      System.out.println("this script is entering node "+node);
    </script>
  </event>
  ...
</process-definition>
Copy to Clipboard Toggle word wrap
要自定义将变量加载到脚本的默认行为,请使用 variable 元素作为脚本的子元素。如果这样做,还要将脚本表达式作为子元素放入脚本中: expression
<process-definition>
  <event type="process-end">
    <script>
      <expression>
        a = b + c;
      </expression>
      <variable name='XXX' access='write' mapped-name='a' />
      <variable name='YYY' access='read' mapped-name='b' />
      <variable name='ZZZ' access='read' mapped-name='c' />
    </script>
  </event>
  ...
</process-definition>
Copy to Clipboard Toggle word wrap
在脚本启动前,脚本将分别提供 YYYZZZ 进程变量,作为脚本变量 bc。脚本完成后,script-variable a 的值会存储在进程变量 XXX
如果变量的 access 属性包含 read,则在评估脚本前,进程变量将作为脚本变量载入。如果 access 属性包含 write,则脚本变量将在评估后作为进程变量保存。mapped-name 属性可以在脚本的另一个名称下提供进程变量。当进程变量名称包含空格或其他无效字符时,请使用此选项。

6.5.5.  自定义事件

在执行过程中,通过调用 GraphElement.fireEvent(String eventType, ExecutionContext executionContext); 方法运行自定义事件。自由选择事件类型的名称。

6.6.  超级状态

超级状态是一组节点。它们可以递归嵌套,用于向进程定义添加层次结构。例如,此功能对属于阶段进程的节点很有用。
操作可以与多状态事件关联。令牌在嵌套节点中触发的事件,使多状态层次结构到进程定义。因此,令牌同时位于层次结构中的每个节点中。这在检查进程执行是否处于启动阶段时非常方便。

6.6.1.  super-State Transitions

任何剩余状态的转换都可以由该 super-state 中的任何节点上的令牌参与。此功能的一个用例是对 取消 转换建模,可以随时进行。
转换也可以处于多状态,在这种情况下,令牌将按文档顺序重定向到第一个节点。此外,处于 super-state 之外的节点也可以直接转换为其内部的节点,反之亦然。最后,由于任何其他节点,多状态也可以自助转换。

6.6.2.  超级状态事件

两个事件对 super-states 是唯一的,它们是 superstate-entersuperstate-leave。无论节点已进入或离开,都不会触发它们。只要令牌在多状态内进行转换,这些事件就不会被触发。
注意
状态和超级状态有单独的事件类型。该软件以这种方式设计,以便更轻松地区分从 super-state 中传递的实际多状态事件和节点事件。

6.6.3.  层次结构名称

节点名称必须在其 范围内 唯一。节点的范围是 node-collection。进程定义和多状态都是节点集合。要引用 super-states 中的节点,请指定相对斜杠(/)分隔名称。斜杠分隔节点名称。使用 .. 来指代顶层。下一示例演示了如何在 super-state 中引用节点:
<process-definition>
  <state name="preparation">
    <transition to="phase one/invite murphy"/>
  </state>
  <super-state name="phase one">
    <state name="invite murphy"/>
  </super-state>
</process-definition>
Copy to Clipboard Toggle word wrap
下一示例演示了如何移动多状态层次结构:
<process-definition>
  <super-state name="phase one">
    <state name="preparation">
      <transition to="../phase two/invite murphy"/>
    </state>
  </super-state>
  <super-state name="phase two">
    <state name="invite murphy"/>
  </super-state>
</process-definition>
Copy to Clipboard Toggle word wrap

6.7.  异常处理

商业流程管理器的异常处理机制仅适用于 Java 例外。图形执行无法自行造成问题。只有在执行异常的 委托类 时才会发生。
可以在 进程确定的 s、nodes 和 conversion s 上指定 exception-handlers 列表。每个异常处理程序都有一组操作。当在委派类中发生异常时,将搜索进程元素的父层次结构以获取适当的 例外处理程序,执行该操作。
重要
商业流程管理器的异常处理与 Java 异常处理的一些方面有所不同。在 Java 中,发现的例外可能会影响 控制流。如果是 192.168.1.0/24,则异常处理机制无法更改控制流。例外可能是 caught,也可以是 not。尚未发现的例外会抛出到称为 token.signal() 方法的客户端。对于这些已发现的例外情况,图形执行会象什么情况一样继续进行。
注意
使用 Token.setNode(Node node) 将令牌放在异常处理 操作 图中的任意节点上。

6.8.  流程组成

商业流程管理器支持通过 进程状态进行 进程 组成。这是与另一个进程定义关联的状态。当图形执行到达进程状态时,会创建一个子进程的新实例。然后,这个子进程与到达进程状态的执行路径关联。执行超级用户的路径将等到子进程结束,然后离开进程状态,并在超级用户中继续图形执行。
<process-definition name="hire">
  <start-state>
    <transition to="initial interview" />
  </start-state>
  <process-state name="initial interview">
    <sub-process name="interview" />
    <variable name="a" access="read,write" mapped-name="aa" />
    <variable name="b" access="read" mapped-name="bb" />
    <transition to="..." />
  </process-state>
  ...
</process-definition>
Copy to Clipboard Toggle word wrap
在上例中,Auter 进程包含一个进程状态, 生成了指导过程。 当执行到达 第一个参与 时,将创建一个新的执行(即进程实例)。如果没有明确指定版本,则使用最新版本的子进程。要使 Business Process Manager 实例化特定版本,请指定可选的 version 属性。要提交指定或最新版本,直到实际创建子进程前,将可选的 binding 属性设置为 late
接下来,strat er 进程变量 a 被复制到过期进程变量 aa同样,strater 变量 b 会被复制到过期变量 bb 中。当过期过程完成后,只有 aa 变量被复制到 a 变量中。
通常,当启动子进程时,所有具有读取访问权限的变量都会从超级用户中读取,并放入新创建的子进程。这会在指定信号前发生,以保持启动状态。当子进程实例完成后,具有写入访问权限的所有变量都将从子进程复制到多进程。使用变量的 mapped-name 属性指定在子进程中使用的变量名称。

6.9.  自定义节点行为

通过使用可执行任何业务逻辑的 ActionHandler 的特殊实施创建自定义节点,但也负责传递图形执行。下面是一个从 ERP 系统中读取值的示例,添加数量(从进程变量中),并将结果存储回 ERP 系统中。根据大小,使用 小量或 大大 过渡 退出。

图 6.3. 更新 ERP 示例的进程片段

public class AmountUpdate implements ActionHandler {
  public void execute(ExecutionContext ctx) throws Exception {
    // business logic
    Float erpAmount = ...get amount from erp-system...;
    Float processAmount = (Float) ctx.getContextInstance().getVariable("amount");
    float result = erpAmount.floatValue() + processAmount.floatValue();
    ...update erp-system with the result...;
    
    // graph execution propagation
    if (result > 5000) {
      ctx.leaveNode(ctx, "big amounts");
    } else {
      ctx.leaveNode(ctx, "small amounts");
    }
  }
}
Copy to Clipboard Toggle word wrap
注意
也可以在自定义节点实现中创建并加入令牌。要了解如何执行此操作,请讨论 JADI 源代码中的 Fork 和 join 节点实现。

6.10.  图形执行

商业流程管理器的图形执行模型基于流程定义和"命令链"模式的解释。
进程定义数据存储在数据库中,并在进程执行过程中使用。
注意
请注意,使用 Hibernate 的第二个级别缓存以避免在运行时加载定义信息。由于进程定义没有改变,所以 Hibernate 可以在内存中缓存它们。
"命令模式链"使图形中的每个节点负责传递进程执行。如果节点没有传递它,它将的行为如同 等待状态一样
让执行在进程实例上开始,它将继续,直到它进入 等待状态
令牌表示执行的路径。它有一个指向进程图中节点的指针。在 等待状态 期间,可以让令牌保留在数据库中。
此算法用于计算令牌的执行。当信号发送到令牌时,执行就会通过命令模式链通过转换和节点传递。这些是相关的方法:

图 6.4. 与图形执行相关的方法

当令牌位于节点时,可以将信号发送到节点。信号被视为启动执行的指令,因此指定从令牌当前节点的 离开转换。第一个转换是默认值。在对令牌的信号中,它会获取其当前节点并调用 Node.leave(ExecutionContext,Transition) 方法。(最好将 ExecutionContext 视为令牌,因为它中的主对象是一个令牌。) Node.leave(ExecutionContext,Transition) 方法将触发 node-leave 事件并调用 Transition.take(ExecutionContext)。然后,该方法将运行转换事件,并在转换的目标节点中调用 Node.enter(ExecutionContext)。然后该方法将触发 node-enter 事件并调用 Node.execute(ExecutionContext)
每种节点类型都有自己的行为,它们通过 execute 方法实现。每个节点都负责通过再次调用 Node.leave(ExecutionContext,Transition) 在图形执行上传递。概述:
  • Token.signal(Transition)
  • Node.leave(ExecutionContext,Transition)
  • Transition.take(ExecutionContext)
  • Node.enter(ExecutionContext)
  • Node.execute(ExecutionContext)
注意
下一个状态(包括操作的调用)通过客户端的线程计算。常见的误报是,所有计算都必须以这种方式忽略。相反,就像任何 异步调用 一样,可以使用 异步 消息(通过 Java 消息服务)。当在与进程实例更新相同的事务中发送消息时,所有同步问题都会正确处理。有些工作流系统在图形中的所有节点之间使用异步消息传递,但在高吞吐量环境中,这种算法可以为希望最大化业务进程性能的用户提供更多控制和灵活性。

6.11.  事务分离

第 6.10 节 “ 图形执行 ” 所述,Business Process Manager 在客户端的线程中运行进程,并具有同步性。实际上,这意味着 token.signal()taskInstance.end() 仅在进程进入新的 等待状态时返回
注意
要了解更多有关本节中描述的 jPDL 功能的信息,请参阅 第 10 章 异步约定
在大多数情况下,这是最简单的方法,因为一个可轻松将进程执行绑定到服务器端事务:进程从一个状态移到一个事务空间的下一个状态。
有时,在进程内计算需要大量时间,因此此行为可能并不可能并不可能。为了解决此问题,业务流程管理器包含一个异步消息传递系统,它允许它以异步的方式继续进程,即异步名称。(课程,在 Java 企业环境中,可将 lsblk 配置为使用 Java 消息服务代理,而不是内置消息传递系统。)
jpdl 支持每个节点中的 async="true" 属性。异步节点不会在客户端的线程中执行。相反,在异步消息传递系统中发送一条信息,线程返回给客户端(换句话说,会返回 token.signal()taskInstance.end()。)
批准过程管理器的客户端代码现在可以提交事务。在与包含进程更新相同的事务中发送消息。(此类事务的整体结果是,令牌被移到下一个节点(还没有执行)和 org.jbpm.command.ExecuteNodeCommand 消息将从异步消息传递系统发送到 Executor。这会从队列中读取命令并执行它们。如果是 org.jbpm.command.ExecuteNodeCommand,该过程将在执行节点时继续。(每个命令都在单独的事务中执行。)
重要
确保 Executor 正在运行,以便异步进程能够继续。通过配置 Web 应用程序的 CommandExecutionServlet 来完成此操作。
注意
流程模型器不需要过度关注异步消息传递。请记住,事务的主要点是事务:默认情况下,业务流程管理器将在客户端事务中操作,从而降低整个计算,直到进程进入 等待状态。(使用 async="true" 来解码进程中的事务。)
下面是一个示例:
<start-state>
  <transition to="one" />
</start-state>
<node async="true" name="one">
  <action class="com...MyAutomaticAction" />
  <transition to="two" />
</node>
<node async="true" name="two">
  <action class="com...MyAutomaticAction" />
  <transition to="three" />
</node>
<node async="true" name="three">
  <action class="com...MyAutomaticAction" />
  <transition to="end" />
</node>
<end-state name="end" />
...
Copy to Clipboard Toggle word wrap
启动和停止进程执行所需的客户端代码与普通同步进程所需的客户端代码完全相同。
//start a transaction
JbpmContext jbpmContext = jbpmConfiguration.createContext();
try {
  ProcessInstance processInstance =
    jbpmContext.newProcessInstance("my async process");
  processInstance.signal();
  jbpmContext.save(processInstance);
} finally {
  jbpmContext.close();
}
Copy to Clipboard Toggle word wrap
在进行第一次事务后,进程执行的 根令牌 将指向 节点,并且 ExecuteNodeCommand 消息发送到命令 executor。
在后续的事务中,命令 executor 将从队列中读取消息,并执行 节点。此操作可决定传递执行或进入 等待状态。如果选择传递它,当执行 在节点 2 到达时,事务将被终止。
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat