您的位置:首页 > 产品设计 > 产品经理

说说 jBPM 流程定义语言(11)—— 事件

2018-03-17 20:18 681 查看
事件(event)用来流程执行的过程中特定的“点”,例如“流程实例开始”、“状态活动结束”等,可以在这些“点”中注册相应的监听器(listener)。当流程实例执行到这些被监听的点时,就会执行监听器中代码逻辑。

事件与监听器没有显示在流程定义的图中,因为它们更注重内部逻辑。

通过实现 EventListener 接口,来编写一个事件监听器:

public interface EventListener extends Serializable {
void notify(EventListenerExecution var1) throws Exception;
}


之前说到的 java、script、hql 以及 sql 等自助活动,都可以作为事件监听器来监听事件。

可以使用 on 元素为事件监听器分组并制定事件,on 元素可以嵌入到 process 元素或 process 元素下的任何流程活动中。

on 元素的属性:

属性类型默认值是否必需描述
eventstart(开始事件)/end(结束事件)必需事件名称
对于转移(transition)的执行事件,我们只需在 transition 元素中嵌入相应的事件监听器即可 。

on 元素支持的子元素:

元素数目描述
event-listener0..*指定自定义的事件监听器,它需要实现 EventListener 接口。
任意自动活动0..*使用自动活动(java,script,hql…)作为事件监听器。
自定义的事件监听器指向我们编写的 Java 类,它具有以下独特的属性:

属性类型默认值是否必需描述
propagationenable、disabled、true、false、on、offdisabled可选是否支持被传播的事件
continuesync、async、exclusivesync可选是否可异步执行

1 监听事件



jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="EventListener" xmlns="http://jbpm.org/4.4/jpdl">
<!-- 监听流程的启动事件 -->
<on event="start">
<event-listener class="net.deniro.jbpm.test.eventListener.LogListener">
<!-- 为事件监听器注入 msg 值 -->
<field name="msg">
<string value="start on process definition"/>
</field>
</event-listener
10758
>
</on>
<start g="345,119,48,48" name="start1">
<transition to="等待"/>
</start>

<state g="443,118,92,52" name="等待">
<!-- 捕获【等待】活动开始事件 -->
<on event="start">
<event-listener class="net.deniro.jbpm.test.eventListener.LogListener">
<field name="msg">
<string value="start on activity wait"/>
</field>
</event-listener>
</on>
<!-- 捕获【等待】活动结束事件 -->
<on event="end">
<event-listener class="net.deniro.jbpm.test.eventListener.LogListener">
<field name="msg">
<string value="end on activity wait"/>
</field>
</event-listener>
</on>
<transition to="停车">
<!-- 捕获【等待】活动转移事件 -->
<event-listener class="net.deniro.jbpm.test.eventListener.LogListener">
<field name="msg">
<string value="take transition"/>
</field>
</event-listener>
</transition>
</state>
<state g="589,120,92,52" name="停车"/>
</process>


现在,我们在 LogListener 中实现记录流程运行的日志,这些日志都记录在流程变量中:

public class LogListener implements EventListener {

String msg;

@Override
public void notify(EventListenerExecution execution) throws Exception {
List<String> logs = (List<String>) execution.getVariable("logs");
if (logs == null) {
logs = new ArrayList<>();
execution.setVariable("logs", logs);
}

logs.add(msg);
execution.setVariable("logs", logs);
}
}


单元测试:

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
("EventListener");

//触发流程走到下一步
Execution execution = processInstance.findActiveExecutionIn("等待");
assertNotNull(execution);//在【等待】节点

//发出执行信号
executionService.signalExecutionById(execution.getId());

List<String> logs = (List<String>) executionService.getVariable(execution.getId(),
"logs");
System.out.println("logs:" + logs);
assertFalse(logs.isEmpty());


2 传播事件

触发事件支持从父元素传播到子元素。

默认情况下,事件监听器只对当前定义的元素有效,即 propagation=”disable”。我们可以通过指定事件监听器的传播属性 propagation=”enabled”,让这个事件监听器可以对其监听元素的所有子元素有效。

修改刚才的流程定义文件:

<?xml version="1.0" encoding="UTF-8"?>

<process name="EventListenerPropagation" xmlns="http://jbpm.org/4.4/jpdl">
<!-- 监听流程的启动事件 -->
<on event="start">
<!-- 支持传播,因为是定义在全局,所以,所有活动的 start 事件都会被捕获 -->
<event-listener class="net.deniro.jbpm.test.eventListener.LogListener2" propagation="enabled">
<!-- 为事件监听器注入 msg 值 -->
<field name="msg">
<string value="start on "/>
</field>
</event-listener>
</on>
<start g="351,122,48,48" name="start1">
<transition to="等待"/>
</start>

<!-- wait 活动的 start 事件将被捕获 -->
<state g="439,117,92,52" name="等待">
<transition to="停车"/>
</state>
<!-- park 活动的 start 事件将被捕获 -->
<state g="558,118,92,52" name="停车"/>
</process>


事件监听器:

public class LogListener2 implements EventListener {
String msg;

@Override
public void notify(EventListenerExecution execution) throws Exception {
List<String> logs = (List<String>) execution.getVariable("logs");
if (logs == null) {
logs = new ArrayList<>();
execution.setVariable("logs", logs);
}

String actName=((ExecutionImpl)execution).getActivityName();
////因为流程实例的 start 事件所对应的流程名称为 null(如果未设置的话), 所以这里需要做处理
logs.add(msg+((actName==null)?"process":actName));
execution.setVariable("logs", logs);
}
}


单元测试:

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
("EventListenerPropagation");

//触发流程走到下一步
Execution execution = processInstance.findActiveExecutionIn("等待");
assertNotNull(execution);//在【等待】节点

//发出执行信号
executionService.signalExecutionById(execution.getId());

//获取流程变量
List<String> logs = (List<String>) executionService.getVariable(processInstance.getId(),
"logs");
System.out.println("logs:" + logs);
assertEquals(3, logs.size());

//设定预期日志内容
List<String> expectedLogs = new ArrayList<String>();
expectedLogs.add("start on start1");
expectedLogs.add("start on 等待");
expectedLogs.add("start on 停车");
assertEquals(expectedLogs, logs);


3 处理异常事件

建议在事件监听器中处理异常事件,即监听那些预期可能会发生的异常事件,比如可以设置变量标识异常、发出邮件报告异常以及利用 JMS 消息发布异常等处理,然后让流程实例执行所期望的行为,或者抛出异常让事务失败,同时做出一些 “补偿” 操作,让流程实例回到异常发生前的状态。

一个良好的设计习惯是:在事件监听器中捕获业务异常,并设置一些流程变量来标识这个异常,然后我们可以在流程定义中设置若干个 decision 活动,来选择一个能够处理异常的转移路径上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: