jBPM4实现基本活动(下)

开发 后端
这一章解释了流程定义的基础,流程虚拟机给予的功能 以及活动实现是如何构建的。 同时,客户端API被用来执行包含了那些活动实现的流程。

5.5. 基本流程执行

在下一个例子里,我们会结合自动活动和等待状态。 这里例子构建了贷款审批流程,使用WaitState 和Display活动,我们刚刚创建的。 贷款流程的图形看起来像这样:
贷款流程

贷款流程 

图 5.3. 贷款流程

使用Java构建流程图形是很乏味的事情, 因为你必须在局部变量中跟踪所有的引用。 为了解决这个问题,流程虚拟机提供了一个ProcessFactory。 ProcessFactory是一种领域特定语言(DSL),可以嵌入到Java中, 简化流程图形的结构。这个模型也叫做 流畅接口。

  1. ClientProcessDefinition processDefinition = ProcessFactory.build("loan")   
  2.   .activity("submit loan request").initial().behaviour(new Display("loan request submitted"))   
  3.     .transition().to("evaluate")   
  4.   .activity("evaluate").behaviour(new WaitState())   
  5.     .transition("approve").to("wire money")   
  6.     .transition("reject").to("end")   
  7.   .activity("wire money").behaviour(new Display("wire the money"))   
  8.     .transition().to("archive")   
  9.   .activity("archive").behaviour(new WaitState())   
  10.     .transition().to("end")   
  11.   .activity("end").behaviour(new WaitState())   
  12. .done();  

为了了解ProcessFactory的更多细节,可以参考 api文档。 ProcessFactory的另一种选择是创建一个XML语言和一个XML解析器,来表示流程。 XML解析器可以直接实例化 org.jbpm.pvm.internal.model包中的类。 这种方式一般都被流程语言选择使用。

初始化活动submit loan request和 wire the money活动是自动活动。 在这个例子中,wire the money活动的 Display实现 使用Java API来把信息输出到控制台上。但是读取器可以想象一个可选的 Activity实现,使用支付流程库的Java API 来实现一个真实的自动支付。

上述流程的一个新执行可以像下面这样启动

ClientExecution execution = processDefinition.startProcessInstance();

当startExecution方法返回时, submit loan request活动会被执行, 执行会位于evaluate活动。
位于'evaluate'活动的执行

位于evaluate活动的执行 

图 5.4. 位于'evaluate'活动的执行

现在,执行处在一个很有趣的点。这里有两个转移从evaluate指向外边。 一个转移叫approve 一个转移叫reject。像我们上面解释的, WaitState实现会根据执行的signal选择转移。 让我们像这样执行'approve' signal:

execution.signal("approve");

这个approve signal会导致执行选择approve转移 它会到达wire money活动。

在wire money活动中,信息会打印到控制台里。 因为Display没有调用execution.waitForSignal(), 也没有调用其他执行传播方法, 默认流程行为只会让执行继续, 使用向外的转移到达archive活动, 这也是一个WaitState。
位于'archive'活动的执行

位于archive活动的执行 

图 5.5. 位于'archive'活动的执行

所以只有当archive到达时, signal("approve")会返回。

另一个signal就像这样:

  1. execution.signal("approve");  

将让执行最终到达结束状态。
位于'end'活动的执行

位于end活动的执行 

图 5.6. 位于'end'活动的执行

5.6. 事件

事件位于流程定义中, 一系列的EventListener可以进行注册。

  1. public interface EventListener extends Serializable {   
  2.  
  3.   void notify(EventListenerExecution execution) throws Exception;   
  4.  
  5. }   

事件的目的是让开发者可以为流程添加程序逻辑, 不必改变流程图。 这是非常有价值的机制,可以促进业务分析人员和开发者之间的协作。 业务分析人员负责描述需求。 当他们使用流程图归档那些需求, 开发者可以获得这些图形,让它可执行化。 事件会非常方便,向一个流程中添加技术细节(比如一些数据库插入操作) 这些都是业务分析人员不感兴趣的东西。

最常用的事件是由执行自动触发的:

TODO: 在用户手册中解释事件

事件是由流程元素和事件名称结合而成。 用户和流程语言也可以出发事件, 使用编程的方式在流程中使用fire方法。

  1. public interface Execution extends Serializable {   
  2.   ...   
  3.   void fire(String eventName, ProcessElement eventSource);   
  4.   ...   
  5. }  

可以把一系列的EventListeners分配给一个事件。 但是事件监听器不能控制执行的流向, 因为它们仅仅是监听已经执行了的执行。 这与活动处理活动的行为是不同的。 活动行为可以响应执行的传播。

我们会创建一个PrintLn事件监听器, 这与上面的Display活动是非常相似的。

  1. public class PrintLn implements EventListener {   
  2.  
  3.   String message;   
  4.  
  5.   public PrintLn(String message) {   
  6.     this.message = message;   
  7.   }   
  8.  
  9.   public void notify(EventListenerExecution execution) throws Exception {   
  10.     System.out.println("message");   
  11.   }   
  12. }   

多个PrintLn监听器 会在流程中注册。
PrintLn监听器流程

PrintLn监听器流程  

图 5.7. PrintLn监听器流程

  1. ClientProcessDefinition processDefinition = ProcessFactory.build()   
  2.   .activity("a").initial().behaviour(new AutomaticActivity())   
  3.     .event("end")   
  4.       .listener(new PrintLn("leaving a"))   
  5.       .listener(new PrintLn("second message while leaving a"))   
  6.     .transition().to("b")   
  7.       .listener(new PrintLn("taking transition"))   
  8.   .activity("b").behaviour(new WaitState())   
  9.     .event("start")   
  10.       .listener(new PrintLn("entering b"))   
  11. .done();  

***个事件演示如何为相同的事件注册多个监听器。 它们会根据它们指定的顺序依次执行。

然后,在转椅上,这里的事件只有一种类型。 所以在那种情况下,事件类型不需要指定, 监听器可以直接添加到转移上。

一个监听器每次都会执行,当一个执行触发事件时,如果这个监听器被注册了。 执行会作为一个参数提供给活动接口, 除了控制流程传播的方法以外, 都可以被监听器使用。

5.7. 事件传播

事件会默认传播给最近的流程元素。 目的是允许监听器在流程定义或组合活动中 可以执行所有发生在流程元素中的事件。 比如这个功能允许为end事件在流程定义或一个组合活动中注册一个事件监听器。 这种动作会被执行,如果一个活动离开。 如果事件监听器被注册到一个组合活动中, 它也会被所有活动执行,当组合活动中出现了离开事件。

为了清楚地显示这个,我们会创建一个DisplaySource事件监听器, 这会把leaving信息和事件源 打印到控制台。

  1. public class DisplaySource implements EventListener {   
  2.  
  3.   public void execute(EventListenerExecution execution) {   
  4.     System.out.println("leaving "+execution.getEventSource());   
  5.   }   
  6. }  

注意事件监听器的目的不是可视化,这是为什么事件监听器本身 不应该显示在图形中。一个DisplaySource事件监听器 会作为end事件的监听器添加到组合活动中。

下一个流程展示了DisplaySource事件监听器如何 作为'end'事件的监听器注册到composite活动:
一个在组合活动中为end事件注册了不可见的事件监听器的流程。

一个在组合活动中为end事件注册了不可见的事件监听器的流程 

图 5.8. 一个在组合活动中为end事件注册了不可见的事件监听器的流程。

TODO 更新代码片段

下一步,我们会启动一个执行。

ClientExecution execution = processDefinition.startProcessInstance();

在启动一个新执行后,执行将在a活动中 作为初始活动。没有活动离开,所以没有信息被记录下来。 下一个signal会给与执行, 导致它选择从a到b。

execution.signal();

当signal方法返回,执行会选择转移 然后end事件会被a活动触发。 那个组合活动会被传播到组合活动和流程定义中。 因为我们的DisplaySource 监听器放到 composite活动中, 它会接收事件,把下面的信息打印到控制台中:

leaving activity(a)

另一个

execution.signal();

会选择b到c的转移。那会触发两个活动离开事件。 一个在b活动,一个在组合活动。 所以下面的几行会添加到控制台输出中:

leaving activity(b)
leaving activity(composite)

事件传播建立在流程定义的继承组合结构中。 ***元素总是流程定义。 流程定义包含一系列活动。每个活动可以是叶子活动或者可以是一个组合节点, 这意味着它包含了一系列内嵌活动。 内嵌活动可以被使用,比如超级状态或组合活动,在内嵌流程语言中,像BPEL。

所以事件模型在组合活动和上面的流程定义中的功能是相似的。 想象'Phase one'模型一个超级状态作为一个状态机。 然后事件传播允许在超级状态中注册所有事件。 这个主意是继承组合响应图形展示。 如果一个'e'元素画在另一个'p'元素中, 'p'是'e'的父节点。一个流程定义拥有一系列定义活动。 每个活动可以拥有一系列内嵌活动。 一个转移的父节点就是它的源头和目的的***个父节点。

如果一个事件监听器对传播的事件没有兴趣, 可以在构建流程使用ProcessFactory的propagationDisabled()。 下一个流程是与上面相同的流程, 除了传播的事件会被事件监听器禁用。 图形还是一样。
注册到'end'事件的事件监听器被禁用的流程。

注册到end事件的事件监听器被禁用的流程 

图 5.9. 注册到'end'事件的事件监听器被禁用的流程。

使用流程工厂构建流程:

TODO 更新代码

所以当***个signal在流程中调用时,end事件 会再次触发在a活动上,但是现在在组合活动的事件监听器 不会被执行,因为传播的事件被禁用了。 禁用传播是单独的事件监听器的一个属性, 不会影响其他监听器。事件会一直被触发, 传播到整个父继承结构。

ClientExecution execution = processDefinition.startProcessInstance();

***个signal会选择从a到b的流程。 没有信息会被打印到控制台。

execution.signal();

下一步,第二个signal会选择从b到c的转移。

execution.signal()

还是两个end事件被触发, 就像上面分别在b和composite活动中。 ***个事件是b活动上的 end事件。 那将被传播给composite活动。 所以事件监听器不会为这个事件执行,因为它已经禁用了传播。 但是事件监听器会在composite活动上 为end事件执行。 那是不传播的,但是直接在composite活动上触发。 所以事件监听器现在会被执行 一次,为组合活动,就像下面控制台里显示的那样:

leaving activity(composite)

【编辑推荐】

  1. jBPM4实现基本活动(上)
  2. jBPM与Spring整合浅析
  3. jBPM 4.0配置浅析
  4. jBPM4的架构
  5. 浅谈jBPM下MySQL的配置
责任编辑:yangsai 来源: BlogJava
相关推荐

2009-06-26 09:15:31

jBPM4基本活动

2009-06-24 14:57:03

jBPM4架构

2009-06-26 13:51:49

jBPM4高级图形执行

2009-06-29 14:42:54

2009-06-23 15:30:20

jBPMMySQL

2010-01-20 09:23:38

jBPM高级交互模式jBPM四眼原则

2019-09-19 11:52:59

Linux脚本语言Vim

2022-04-25 16:27:33

春节活动红包除夕

2010-05-12 16:13:04

2018-06-22 15:25:31

LinuxDocker容器管理

2009-06-24 16:23:29

jBPM 4.0配置

2009-06-11 13:53:35

jBPM用户指南

2009-06-25 17:13:51

jBPM与Spring

2010-05-14 09:57:25

MySQL数据库

2009-06-23 15:49:00

Liferay Por

2021-11-19 14:33:27

挖矿木马病毒

2014-07-31 13:41:36

程序员

2022-10-18 08:28:38

运营活动实现逻辑整体协作

2019-12-03 18:31:55

联通/云时代/创新

2009-06-11 14:00:34

jBPM用户指南jBPM范例
点赞
收藏

51CTO技术栈公众号