FixFlow6.0版本开始改名为FoxBPM,该版本添加了一个对客户和开发人员来说都很实用的功能:流程运行轨迹。站在开发人员的角度流程运行轨迹可用于调试跟踪相对复杂的流程,站在客户的角度对流程监控分析提供动态支持。运行轨迹基于流程引擎的全局事件监听机制,其原理是针对每个节点的进入事件添加一个负责记录节点信息的监听器,当流程启动运行进入节点的时候,会产生一个进入事件并且触发该事件所有的监听器,这样就可以拿到流程运行的时候每个节点的进入执行信息了。
代码实现:
1、需要一个记录监听器。
2、通过配置文件对监听器进行配置,在流程模型转化的时候将监听器装入流程定义。
3、针对记录信息需要一个纵向的可查询的持久化功能。
4、数据库表、实体类。
5、展示记录信息的平台(目前支持SVG格式的流程定义图形)。
以下为部分核心实现代码、首先是实现KernelListener接口的监听定义类
public abstract class AbstractTrackListener implements KernelListener { private static long tractRecord = 0; /** * serialVersionUID:序列化 */ private static final long serialVersionUID = 6870039432248742401L; private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmssSSS"); @Override public void notify(ListenerExecutionContext executionContext) throws Exception { // 记录流程实例的运行轨迹 this.recordOperate(executionContext); // TODO 用户定制其他的监听操作 // otherOperate(executionContext); } /** * recordOperate(记录流程实例的运行轨迹) * * @param executionContext * void */ private void recordOperate(ListenerExecutionContext executionContext) { // 记录流程实例的运行轨迹 RunningTrackEntity runningTrackEntity = this.recordRunningTrack(executionContext); if (runningTrackEntity == null) { throw new FoxBPMException("分类构造的运行轨迹实体 不能为空"); } if (tractRecord == 0) { tractRecord = Long.parseLong(sdf.format(new Date())); } KernelTokenImpl kernelTokenImpl = (KernelTokenImpl) executionContext; KernelProcessDefinitionImpl processDefinition = kernelTokenImpl.getProcessDefinition(); runningTrackEntity.setId(GuidUtil.CreateGuid()); runningTrackEntity.setProcessDefinitionId(processDefinition.getId()); runningTrackEntity.setProcessDefinitionKey(processDefinition.getKey()); runningTrackEntity.setTrackRecord(String.valueOf(tractRecord)); runningTrackEntity.setExecutionTime(new Date()); runningTrackEntity.setEventName(kernelTokenImpl.getEventName()); runningTrackEntity.setTokenId(kernelTokenImpl.getId()); runningTrackEntity.setParentTokenId(kernelTokenImpl.getParent() == null ? "" : kernelTokenImpl.getParent().getId()); runningTrackEntity.setProcessInstanceId(kernelTokenImpl.getProcessInstanceId()); // 增加运行轨迹、保存数据 tractRecord = tractRecord + 1; this.saveRunningTrackEntity(runningTrackEntity); } /** * * saveRunningTrackEntity 保存数据 * * @param runningTrackEntity * @since 1.0.0 */ private void saveRunningTrackEntity(RunningTrackEntity runningTrackEntity) { Context.getCommandContext().getRunningTrackManager().insert(runningTrackEntity); } /** * * recordRunningTrack(记录操作人、节点信息等特定轨迹信息) * * @param executionContext * @return RunningTrackEntity */ protected abstract RunningTrackEntity recordRunningTrack( ListenerExecutionContext executionContext); }
以下是监听器的配置和注册代码
配置、foxbpm.cfg.xml:
<eventListenerConfig> <!-- <eventListener eventType="process-start" listenerClass="org.foxbpm.engine.impl.listener.runningtrack.StartEventTrackListener" /> --> <eventListener eventType="node-enter" listenerClass="org.foxbpm.engine.impl.listener.runningtrack.FlowNodeTrackListener" /> <!-- <eventListener eventType="node-execute" listenerClass="org.foxbpm.engine.impl.listener.runningtrack.FlowNodeTrackListener" /> <eventListener eventType="node-leave" listenerClass="org.foxbpm.engine.impl.listener.runningtrack.FlowNodeTrackListener" /> --> <eventListener eventType="sequenceflow-take" listenerClass="org.foxbpm.engine.impl.listener.runningtrack.SequenceTrackEventListener" /> <eventListener eventType="process-start" listenerClass="org.foxbpm.engine.impl.listener.task.StartEventTaskListener" /> <eventListener eventType="process-end" listenerClass="org.foxbpm.engine.impl.listener.task.EndEventTaskListener" /> </eventListenerConfig>
注册、BpmnParseHandlerImpl.java:
/** * 加载配置监听器、 独立加载 和嵌入流程定义创建代码中,算法效率是一样的 监听器集合SIZE * 节点集合SIZE * 不建议侵入到流程定义的LOAD代码中 * * @param processEntity */ private void registListener(ProcessDefinitionEntity processEntity) { EventListenerConfig eventListenerConfig = Context.getProcessEngineConfiguration().getFoxBpmConfig().getEventListenerConfig(); if (eventListenerConfig != null) { // 加载监听器 List<EventListener> eventListenerList = eventListenerConfig.getEventListener(); KernelListener foxbpmEventListener = null; try { for (EventListener eventListener : eventListenerList) { foxbpmEventListener = (KernelListener) Class.forName(eventListener.getListenerClass()).newInstance(); if (StringUtil.equals(eventListener.getEventType(), KernelEventType.EVENTTYPE_PROCESS_START) || StringUtil.equals(eventListener.getEventType(), KernelEventType.EVENTTYPE_PROCESS_END)) { // 注册启动监听 processEntity.addKernelListener(eventListener.getEventType(), foxbpmEventListener); } else { if (StringUtil.equals(eventListener.getEventType(), KernelEventType.EVENTTYPE_SEQUENCEFLOW_TAKE)) { // 注册线条监听 Map<String, KernelSequenceFlowImpl> sequenceFlows = processEntity.getSequenceFlows(); Set<Entry<String, KernelSequenceFlowImpl>> sequenceEntrySet = sequenceFlows.entrySet(); Iterator<Entry<String, KernelSequenceFlowImpl>> sequenceEntryIter = sequenceEntrySet.iterator(); Entry<String, KernelSequenceFlowImpl> sequenceFlow = null; KernelSequenceFlowImpl kernelSequenceFlowImpl = null; while (sequenceEntryIter.hasNext()) { sequenceFlow = sequenceEntryIter.next(); kernelSequenceFlowImpl = sequenceFlow.getValue(); kernelSequenceFlowImpl.addKernelListener(foxbpmEventListener); } } else { // 注册节点监听 List<KernelFlowNodeImpl> flowNodes = processEntity.getFlowNodes(); this.registerFlowNodeListener(flowNodes, eventListener, foxbpmEventListener); } } } } catch (Exception e) { throw new FoxBPMException("加载运行轨迹监听器时出现问题", e); } } }