66.6. 进程引擎中的事件监听程序
每次进程或任务更改其生命周期中的不同点时,进程引擎都会生成事件。您可以开发接收和处理此类事件的类。此类称为 事件监听程序。
进程引擎将事件对象传递到此类。对象提供对相关信息的访问。例如,如果事件与进程节点相关,对象则提供对进程实例和节点实例的访问。
66.6.1. 事件监听程序的接口 复制链接链接已复制到粘贴板!
您可以使用以下接口为进程引擎开发事件监听程序。
66.6.1.1. 进程事件监听程序的接口 复制链接链接已复制到粘贴板!
您可以开发实现 ProcessEventListener 接口的类。此类可以侦听与进程相关的事件,如启动或完成进程或输入和离开节点。
以下源代码显示了 ProcessEventListener 接口的不同方法:
ProcessEventListener 接口
public interface ProcessEventListener
extends
EventListener {
/**
* This listener method is invoked right before a process instance is being started.
* @param event
*/
void beforeProcessStarted(ProcessStartedEvent event);
/**
* This listener method is invoked right after a process instance has been started.
* @param event
*/
void afterProcessStarted(ProcessStartedEvent event);
/**
* This listener method is invoked right before a process instance is being completed (or aborted).
* @param event
*/
void beforeProcessCompleted(ProcessCompletedEvent event);
/**
* This listener method is invoked right after a process instance has been completed (or aborted).
* @param event
*/
void afterProcessCompleted(ProcessCompletedEvent event);
/**
* This listener method is invoked right before a node in a process instance is being triggered
* (which is when the node is being entered, for example when an incoming connection triggers it).
* @param event
*/
void beforeNodeTriggered(ProcessNodeTriggeredEvent event);
/**
* This listener method is invoked right after a node in a process instance has been triggered
* (which is when the node was entered, for example when an incoming connection triggered it).
* @param event
*/
void afterNodeTriggered(ProcessNodeTriggeredEvent event);
/**
* This listener method is invoked right before a node in a process instance is being left
* (which is when the node is completed, for example when it has performed the task it was
* designed for).
* @param event
*/
void beforeNodeLeft(ProcessNodeLeftEvent event);
/**
* This listener method is invoked right after a node in a process instance has been left
* (which is when the node was completed, for example when it performed the task it was
* designed for).
* @param event
*/
void afterNodeLeft(ProcessNodeLeftEvent event);
/**
* This listener method is invoked right before the value of a process variable is being changed.
* @param event
*/
void beforeVariableChanged(ProcessVariableChangedEvent event);
/**
* This listener method is invoked right after the value of a process variable has been changed.
* @param event
*/
void afterVariableChanged(ProcessVariableChangedEvent event);
/**
* This listener method is invoked right before a process/node instance's SLA has been violated.
* @param event
*/
default void beforeSLAViolated(SLAViolatedEvent event) {}
/**
* This listener method is invoked right after a process/node instance's SLA has been violated.
* @param event
*/
default void afterSLAViolated(SLAViolatedEvent event) {}
/**
* This listener method is invoked when a signal is sent
* @param event
*/
default void onSignal(SignalEvent event) {}
/**
* This listener method is invoked when a message is sent
* @param event
*/
default void onMessage(MessageEvent event) {}
}
您可以实施任何这些方法来处理相应的事件。
有关进程引擎传递给方法的事件类的定义,请参阅 Java 文档中的 org.kie.api.event.process 软件包。
您可以使用事件类的方法检索包含事件中涉及实体的所有信息的其他类。
以下示例是节点相关事件的一部分,如 afterNodeLeft(),并检索进程实例和节点类型。
在节点相关事件中检索进程实例和节点类型
WorkflowProcessInstance processInstance = event.getNodeInstance().getProcessInstance()
NodeType nodeType = event.getNodeInstance().getNode().getNodeType()
66.6.1.2. 任务生命周期事件监听程序的接口 复制链接链接已复制到粘贴板!
您可以开发实现 TaskLifecycleEventListener 接口的类。此类可以侦听与任务生命周期相关的事件,如分配所有者或完成任务。
以下源代码显示了 TaskLifecycleEventListener 接口的不同方法:
TaskLifecycleEventListener 接口
public interface TaskLifeCycleEventListener extends EventListener {
public enum AssignmentType {
POT_OWNER,
EXCL_OWNER,
ADMIN;
}
public void beforeTaskActivatedEvent(TaskEvent event);
public void beforeTaskClaimedEvent(TaskEvent event);
public void beforeTaskSkippedEvent(TaskEvent event);
public void beforeTaskStartedEvent(TaskEvent event);
public void beforeTaskStoppedEvent(TaskEvent event);
public void beforeTaskCompletedEvent(TaskEvent event);
public void beforeTaskFailedEvent(TaskEvent event);
public void beforeTaskAddedEvent(TaskEvent event);
public void beforeTaskExitedEvent(TaskEvent event);
public void beforeTaskReleasedEvent(TaskEvent event);
public void beforeTaskResumedEvent(TaskEvent event);
public void beforeTaskSuspendedEvent(TaskEvent event);
public void beforeTaskForwardedEvent(TaskEvent event);
public void beforeTaskDelegatedEvent(TaskEvent event);
public void beforeTaskNominatedEvent(TaskEvent event);
public default void beforeTaskUpdatedEvent(TaskEvent event){};
public default void beforeTaskReassignedEvent(TaskEvent event){};
public default void beforeTaskNotificationEvent(TaskEvent event){};
public default void beforeTaskInputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
public default void beforeTaskOutputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
public default void beforeTaskAssignmentsAddedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};
public default void beforeTaskAssignmentsRemovedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};
public void afterTaskActivatedEvent(TaskEvent event);
public void afterTaskClaimedEvent(TaskEvent event);
public void afterTaskSkippedEvent(TaskEvent event);
public void afterTaskStartedEvent(TaskEvent event);
public void afterTaskStoppedEvent(TaskEvent event);
public void afterTaskCompletedEvent(TaskEvent event);
public void afterTaskFailedEvent(TaskEvent event);
public void afterTaskAddedEvent(TaskEvent event);
public void afterTaskExitedEvent(TaskEvent event);
public void afterTaskReleasedEvent(TaskEvent event);
public void afterTaskResumedEvent(TaskEvent event);
public void afterTaskSuspendedEvent(TaskEvent event);
public void afterTaskForwardedEvent(TaskEvent event);
public void afterTaskDelegatedEvent(TaskEvent event);
public void afterTaskNominatedEvent(TaskEvent event);
public default void afterTaskReassignedEvent(TaskEvent event){};
public default void afterTaskUpdatedEvent(TaskEvent event){};
public default void afterTaskNotificationEvent(TaskEvent event){};
public default void afterTaskInputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
public default void afterTaskOutputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
public default void afterTaskAssignmentsAddedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};
public default void afterTaskAssignmentsRemovedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};
}
您可以实施任何这些方法来处理相应的事件。
有关进程引擎传递给方法的事件类的定义,请参阅 Java 文档中的 org.kie.api.task 软件包。
您可以使用事件类的方法检索代表任务、任务上下文和任务元数据的类。
66.6.2. 对事件监听器调用的时间 复制链接链接已复制到粘贴板!
一个事件监听器调用的数量是 before 和 after events,如 beforeNodeLeft() 和 afterNodeLeft(),beforeTaskActivatedEvent() 和 afterTaskActivatedEvent()。
事件调用的 before 和 after 通常会像堆栈一样工作。如果事件 A 直接导致事件 B,则会出现以下调用序列:
- 前 A
- B 之前
- B 后
- A 后
例如,如果保留节点 X 触发节点 Y,则所有与触发节点 Y 的事件调用在节点 X 的 beforeNodeLeft 和 afterNodeLeft 调用之间发生。
同样,如果启动进程直接导致某些节点启动,则所有 nodeTriggered 和 nodeLeft 事件调用在 beforeProcessStarted 和 afterProcessStarted 调用之间发生。
此方法反映了事件之间原因和效果。但是,事件调用 的时间和 顺序并不总是直观。例如,在对过程中某些节点的 afterNodeLeft 调用后,可能会出现 afterProcessStarted 调用。
通常,在发生特定事件时获得通知,请使用 before 调用事件。只有在要确保与这个事件相关的所有处理均已结束时,才使用 after 调用。例如,当您要获得与启动特定进程实例关联的所有步骤时,请通知。
根据节点的类型,一些节点可能只生成 nodeLeft 调用,而其他节点只能生成 nodeTriggered 调用。例如,捕获中间事件节点不会生成 nodeTriggered 调用,因为它们不会被另一个进程节点触发。同样,引发中间事件节点不会生成 nodeLeft 调用,因为这些节点没有到另一节点的传出连接。
66.6.3. 事件监听程序开发实践 复制链接链接已复制到粘贴板!
进程引擎在处理事件或任务期间调用事件监听程序。这个调用发生在进程引擎事务和块执行中。因此,事件监听器可能会影响进程引擎的逻辑和性能。
为确保最小中断,请遵循以下准则:
- 任何操作都必须尽可能短。
- 侦听器类不能具有 状态。进程引擎可以随时销毁和重新创建侦听器类。
如果监听器修改任何存在于监听器方法范围之外的资源,请确保资源在当前事务中被列出。事务可能会被回滚。在这种情况下,如果修改的资源不是事务的一部分,则资源的状态会不一致。
红帽 JBoss EAP 提供的数据库相关资源始终被纳入当前事务中。在其他情况下,检查您使用的运行时环境的信息。
- 不要使用依赖于不同事件监听器执行顺序的逻辑。
- 不要在监听器内包含与进程引擎之外的不同实体的交互。例如,不要包含事件通知的 REST 调用。反之,使用进程节点来完成此类调用。一个例外是日志信息的输出,但日志记录监听器必须尽可能简单。
- 您可以使用侦听器修改事件中涉及的进程或任务的状态,例如,更改其变量。
- 您可以使用监听程序与进程引擎交互,例如发送信号或与事件中不涉及的进程实例交互。
66.6.4. 注册事件监听程序 复制链接链接已复制到粘贴板!
KieSession 类实施 RuleRuntimeEventManager 接口,它提供注册、删除和列出事件监听程序的方法,如下表所示。
RuleRuntimeEventManager 接口的方法
void addEventListener(AgendaEventListener listener);
void addEventListener(RuleRuntimeEventListener listener);
void removeEventListener(AgendaEventListener listener);
void removeEventListener(RuleRuntimeEventListener listener);
Collection<AgendaEventListener> getAgendaEventListeners();
Collection<RuleRuntimeEventListener> getRuleRintimeEventListeners();
但是,在典型的情况下,不要使用这些方法。
如果使用 RuntimeManager 接口,您可以使用 RuntimeEnvironment 类来注册事件监听程序。
如果使用 Services API,您可以在项目中的 META-INF/services/org.jbpm.services.task.deadlines.NotificationListener 文件中添加事件监听程序的完全限定类名称。服务 API 还注册一些默认监听程序,包括 org.jbpm.services.task.deadlines.notifications.impl.email.EmailNotificationListener,它可以为事件发送电子邮件通知。
要排除默认监听器,您可以将监听器的完全限定名称添加到 org.kie.jbpm.notification_listeners.exclude JVM 系统属性。
66.6.5. KieRuntimeLogger 事件监听程序 复制链接链接已复制到粘贴板!
KieServices 软件包包含 KieRuntimeLogger 事件监听程序,您可以添加到 KIE 会话。您可以使用此监听程序创建审计日志。此日志包含运行时发生的所有不同事件。
这些日志记录器主要用于调试目的。在业务级别流程分析方面,它们可能过于详细。
侦听器实施以下日志记录器类型:
-
控制台日志记录器:该日志记录器将所有事件写入控制台。此日志记录器的完全限定类名称是
org.drools.core.audit.WorkingMemoryConsoleLogger。 文件日志记录器:该日志记录器使用 XML 表示将所有事件写入文件。您可以使用 IDE 中的日志文件来生成在执行过程中发生的事件的树形视觉化。此日志记录器的完全限定类名称是
org.drools.core.audit.WorkingMemoryFileLogger。文件日志记录器仅在关闭日志记录器时或日志记录器中的事件数量达到预定义的级别时将事件写入磁盘。因此,它不适用于在运行时调试进程。
-
线程文件日志记录器:此日志记录器在指定时间间隔后将事件写入文件中。您可以使用此日志记录器在调试进程时实时视觉化进度。此日志记录器的完全限定类名称是
org.drools.core.audit.ThreadedWorkingMemoryFileLogger。
在创建日志记录器时,您必须将 KIE 会话作为参数传递。文件日志记录器还需要创建日志文件的名称。线程文件日志记录器需要保存事件的间隔(以毫秒为单位)。
始终关闭应用程序末尾的日志记录器。
下例演示了使用文件日志记录器。
使用文件日志记录器
import org.kie.api.KieServices;
import org.kie.api.logger.KieRuntimeLogger;
...
KieRuntimeLogger logger = KieServices.Factory.get().getLoggers().newFileLogger(ksession, "test");
// add invocations to the process engine here,
// e.g. ksession.startProcess(processId);
...
logger.close();
由基于文件的日志记录器创建的日志文件包含有关进程运行时发生的所有事件的基于 XML 的概览。