文档章节

Activiti的全局事件机制及其监听处理

keitch
 keitch
发布于 2016/06/12 17:31
字数 1531
阅读 372
收藏 0

概述

Activiti在5.15以后的版本后,增加了统一的事件入口,不需要再像以前那样,监听流程的事件时,在流程定义的BPMN文件中为每个节点及流程增加以下的配置,以实现监听事件的做法,这种做法导致我们发布流程时,需要对bpmn文件进行设置,非常不方便,若调整其XML或Class类名或包名,都需要对BPMN文件重新修改并且发布,难度可想而知。


为了规避这种问题,我们重新引入统一监控机制,其思路来自Activiti的开发指导文件,如下:

http://www.activiti.org/userguide/index.html#eventDispatcherConfiguration

构建Activiti的事件分发器

统一事件处理,有利于为流程与业务的结合提供统一入口的处理,同进为后续的流程扩展提供了便利,包括任务人员的指派、会签的计算、流程回退的处理、流程日志等提供数据的切入口,所以通过构建我们的事件监听器就显得非常重要了。

定义的全局事件监听器的入口

package com.redxun.bpm.activiti.listener;

import java.util.HashMap;
import java.util.Map;

import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.redxun.saweb.util.WebAppUtil;

/**
 * Activiti的全局事件监听器,即所有事件均需要在这里统一分发处理
 * @author csx
 * @copyright http://www.redxun.cn
 *
 */
public class GlobalEventListener implements ActivitiEventListener{
 /**
  * 日志处理器
  */
 public final static Log logger=LogFactory.getLog(GlobalEventListener.class);
 
 //事件及事件的处理器
 //private Map<String,EventHandler> handlers=new HashMap<String, EventHandler>();
 //更换为以下模式,可以防止Spring容器启动时,ProcessEngine尚未创建,而业务类中又使用了这个引用
 private Map<String,String> handlers=new HashMap<String, String>();
 
 @Override
 public void onEvent(ActivitiEvent event) {
  String eventType=event.getType().name();
  logger.debug("envent type is ========>" + eventType);
  //根据事件的类型ID,找到对应的事件处理器
  String eventHandlerBeanId=handlers.get(eventType);
  if(eventHandlerBeanId!=null){
   EventHandler handler=(EventHandler)WebAppUtil.getBean(eventHandlerBeanId);
   handler.handle(event);
  }
 }

 @Override
 public boolean isFailOnException() {
  return false;
 }

 public Map<String, String> getHandlers() {
  return handlers;
 }

 public void setHandlers(Map<String, String> handlers) {
  this.handlers = handlers;
 }

 
}

如何把全局监听加至Activiti的配置中去

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
   <property name="dataSource" ref="dataSource" />
   <property name="transactionManager" ref="transactionManager" />
   <property name="databaseSchemaUpdate" value="true" />
   <property name="jobExecutorActivate" value="false" />
    <property name="enableDatabaseEventLogging" value="false" />
    <property name="databaseType" value="${db.type}" />
    <property name="idGenerator" ref="actIdGenerator"/>
    <property name="eventListeners">
      <list>
        <ref bean="globalEventListener"/>
      </list>
    </property>
    <property name="activityFontName" value="黑体"/>
    <property name="labelFontName" value="黑体"/>
    <!-- 用于更改流程节点的执行行为 -->
    <property name="activityBehaviorFactory" ref="activityBehaviorFactoryExt"/>
  </bean>

<bean id="globalEventListener" class="com.redxun.bpm.activiti.listener.GlobalEventListener">
   <property name="handlers">
  <map>
   <entry key="TASK_CREATED" value="taskCreateListener"/>
   <entry key="TASK_COMPLETED" value="taskCompleteListener"/>
   <entry key="TASK_ASSIGNED" value="taskAssignedListener"/>
   <entry key="PROCESS_COMPLETED" value="processCompleteListener"/>
   <entry key="ACTIVITY_STARTED" value="activityStartedListener"/>
   <entry key="ACTIVITY_COMPLETED" value="activityCompletedListener"/>
   <entry key="ACTIVITY_SIGNALED" value="activitySignaledListener"/>
  </map>
 </property>
  </bean>

定义自己的事件处理器接口

/**
 *  Activiti的事件处理器
 * @author csx
 *
 */
public interface EventHandler {
 /**
  * 事件处理器
  * @param event
  */
 public void handle(ActivitiEvent event);
}

实现自己的任务监控处理

package com.redxun.bpm.activiti.listener;

import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import javax.annotation.Resource;

import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEntityEventImpl;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.redxun.bpm.activiti.util.ProcessHandleHelper;
import com.redxun.bpm.core.entity.BpmDestNode;
import com.redxun.bpm.core.entity.BpmNodeJump;
import com.redxun.bpm.core.entity.BpmRuPath;
import com.redxun.bpm.core.entity.IExecutionCmd;
import com.redxun.bpm.core.entity.ProcessStartCmd;
import com.redxun.bpm.core.entity.config.BpmEventConfig;
import com.redxun.bpm.core.entity.config.UserTaskConfig;
import com.redxun.bpm.core.identity.service.BpmIdentityCalService;
import com.redxun.bpm.core.identity.service.IdentityTypeService;
import com.redxun.bpm.core.manager.BpmInstManager;
import com.redxun.bpm.core.manager.BpmNodeJumpManager;
import com.redxun.bpm.core.manager.BpmNodeSetManager;
import com.redxun.bpm.core.manager.BpmRuPathManager;
import com.redxun.bpm.core.manager.BpmTaskManager;
import com.redxun.bpm.enums.TaskEventType;
import com.redxun.core.script.GroovyEngine;
import com.redxun.org.api.model.IdentityInfo;
import com.redxun.saweb.context.ContextUtil;
/**
 * 任务创建监听器
 * 主要用来执行人员分配,事件执行等
 * @author csx
 *
 */
public class TaskCreateListener implements EventHandler{
 
 private static Log logger=LogFactory.getLog(TaskCreateListener.class);
 
 @Resource
 private IdentityTypeService identityTypeService;
 
 @Resource BpmIdentityCalService bpmIdentityCalService;
 
 @Resource BpmNodeSetManager bpmNodeSetManager;
 
 @Resource GroovyEngine groovyEngine;
 
 @Resource
 BpmTaskManager bpmTaskManager;
 
 @Resource
 BpmRuPathManager bpmRuPathManager;
 
 @Resource BpmInstManager bpmInstManager;
 
 @Resource
 private BpmNodeJumpManager bpmNodeJumpManager;
 

 public void executeScript(TaskEntity taskEntity){
  String solId=(String) taskEntity.getVariable("solId");
  //处理事件
  UserTaskConfig userTaskConfig=bpmNodeSetManager.getTaskConfig(solId, taskEntity.getTaskDefinitionKey());
  if(userTaskConfig.getEvents().size()>0){
   BpmEventConfig bpmEventConfig=null;
   for(BpmEventConfig eventConfig:userTaskConfig.getEvents()){
    if(TaskEventType.TASK_CREATED.name().equals(eventConfig.getEventKey())){
     bpmEventConfig=eventConfig;
     break;
    }
   }
   //执行脚本
   if(bpmEventConfig!=null && StringUtils.isNotEmpty(bpmEventConfig.getScript())){
    logger.debug("===================execute the script in task create listener:"+bpmEventConfig.getScript());
    Map<String,Object> vars=taskEntity.getVariables();
    //把任务实体变量放置进来
    vars.put("taskEntity", taskEntity);
    vars.put("taskId", taskEntity.getId());
    groovyEngine.executeScripts(bpmEventConfig.getScript(),vars);
   }
  }
 }
 
 @Override
 public void handle(ActivitiEvent event) {
  ActivitiEntityEventImpl eventImpl=(ActivitiEntityEventImpl)event;
  TaskEntity taskEntity=(TaskEntity)eventImpl.getEntity();

  logger.debug("create task is "+taskEntity.getName()+" key is:"+taskEntity.getTaskDefinitionKey());
  logger.debug("enter the task create listener ---->" + event.getType().name());
  
  //执行任务的标题处理
  String processSubject=(String)taskEntity.getVariable("processSubject");
  String solId=(String)taskEntity.getVariable("solId");
  taskEntity.setDescription(processSubject);
  taskEntity.setSolId(solId);
  taskEntity.setTenantId(ContextUtil.getCurrentTenantId());
  
  //记录跳转的信息
  createNodeJump(taskEntity);
  
  //执行事件的处理
  executeScript(taskEntity);
  //是否已经对任务进行了人员分配
  boolean isAssigned=false;
  //检查是否为会签任务,若是,则先从变量中获得执行人员
  String multiInstance=(String)taskEntity.getExecution().getActivity().getProperty("multiInstance");
  //是否为回退的处理,并且回退的节点不是会签节点,则
  BpmRuPath backRuPath=ProcessHandleHelper.getBackPath();
  if(backRuPath!=null && StringUtils.isEmpty(multiInstance)){
   if("userTask".equals(backRuPath.getNodeType())){
    taskEntity.setAssignee(backRuPath.getAssignee());
    isAssigned=true;
   }else{//查找其子结点上的执行人员 
    BpmRuPath nodePath= bpmRuPathManager.getByParentIdNodeId(backRuPath.getPathId(),taskEntity.getTaskDefinitionKey());
    if(nodePath!=null && StringUtils.isNotEmpty(nodePath.getAssignee())){
     taskEntity.setAssignee(nodePath.getAssignee());
     isAssigned=true;
    }
   }
  }
  //已经分配,则不从配置数据中获取人员数据
  if(isAssigned){
   publishAssignEvent(taskEntity);
   return;
  }
  
  
  if(StringUtils.isNotEmpty(multiInstance)){
   Integer loopCounter=(Integer)taskEntity.getExecution().getVariable("loopCounter");
   String signUserIds=(String)taskEntity.getExecution().getVariable("signUserIds_"+taskEntity.getTaskDefinitionKey());
   
   //优先从变量中取
   String assignee=getUserIds(signUserIds,loopCounter);
   if(StringUtils.isNotEmpty(assignee)){
    isAssigned=true;
    taskEntity.setAssignee(assignee);
    taskEntity.setOwner(assignee);
    Date expiretime=(Date)taskEntity.getExecution().getVariable("expiretime_"+taskEntity.getTaskDefinitionKey());
    Integer priority=(Integer)taskEntity.getExecution().getVariable("priority_"+taskEntity.getTaskDefinitionKey());
    taskEntity.setDueDate(expiretime);
    taskEntity.setPriority(priority);
   }
  }
  //已经分配,则不从配置数据中获取人员数据
  if(isAssigned){
   publishAssignEvent(taskEntity);
   return;
  }
  

  //从线程中获得人员列表映射(即从页面中传过来的人员配置)
  //优先使用页面中的人员配置
  IExecutionCmd processNextCmd=ProcessHandleHelper.getProcessCmd();
  if(processNextCmd!=null){
   BpmDestNode bpmDestNode=processNextCmd.getNodeUserMap().get(taskEntity.getTaskDefinitionKey());
   
   if(bpmDestNode!=null && StringUtils.isNotEmpty(bpmDestNode.getUserIds())){
    String[]uIds=bpmDestNode.getUserIds().split(",");
    isAssigned=true;
    if(uIds.length==1){
     taskEntity.setAssignee(uIds[0]);
     taskEntity.setOwner(uIds[0]);
    }else{
     taskEntity.addCandidateUsers(Arrays.asList(uIds));
    }
    taskEntity.setPriority(bpmDestNode.getPriority());
    taskEntity.setDueDate(bpmDestNode.getExpireTime());
   }
  }
  //已经分配,则不从配置数据中获取人员数据
  if(isAssigned){
   publishAssignEvent(taskEntity);
   return;
  }
  
  //取得人员配置的信息列表
  Collection<IdentityInfo> idInfoList=bpmIdentityCalService.calNodeUsersOrGroups(taskEntity.getProcessDefinitionId(), taskEntity.getTaskDefinitionKey(),taskEntity.getVariables());
  
  if(idInfoList.size()==1){
   IdentityInfo identityInfo=idInfoList.iterator().next();
   if(IdentityInfo.IDENTIFY_TYPE_USER.equals(identityInfo.getIdentityType())){
    taskEntity.setAssignee(identityInfo.getIdentityInfoId());
    taskEntity.setOwner(identityInfo.getIdentityInfoId());
   }else{
    taskEntity.addCandidateGroup(identityInfo.getIdentityInfoId());
   }
   isAssigned=true;
  }else{
   if(idInfoList.size()>0){
    isAssigned=true;
   }
   for(IdentityInfo info:idInfoList){
    
    if(IdentityInfo.IDENTIFY_TYPE_USER.equals(info.getIdentityType())){
     taskEntity.addCandidateUser(info.getIdentityInfoId());
    }else{
     taskEntity.addCandidateGroup(info.getIdentityInfoId());
    }
   }
  }
  
  if(isAssigned){
   publishAssignEvent(taskEntity);
  }
  
  
    
 }
 
 /**
  * 发布任务分配事件
  * @param taskEntity
  */
 public void publishAssignEvent(TaskEntity taskEntity){
   if (StringUtils.isNotEmpty(taskEntity.getAssignee())) {
       Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
               ActivitiEventBuilder.createEntityEvent(ActivitiEventType.TASK_ASSIGNED, taskEntity));
    }
 }
 
 private String getUserIds(String userIds,Integer index){
  String[] uIds=userIds.split("[,]");
  if(index<uIds.length){
   return uIds[index];
  }
  return null;
 }
 
 private void createNodeJump(TaskEntity taskEntity){

  BpmNodeJump nodeJump=new BpmNodeJump();
  nodeJump.setActDefId(taskEntity.getProcessDefinitionId());
  nodeJump.setActInstId(taskEntity.getProcessInstanceId());
  nodeJump.setTaskId(taskEntity.getId());
  nodeJump.setCreateTime(taskEntity.getCreateTime());
  //获得任务的创建时间
  nodeJump.setNodeName(taskEntity.getName());
  nodeJump.setNodeId(taskEntity.getTaskDefinitionKey());
  nodeJump.setHandlerId(ContextUtil.getCurrentUserId());
  IExecutionCmd cmd=ProcessHandleHelper.getProcessCmd();
  nodeJump.setCheckStatus(BpmNodeJump.JUMP_TYPE_UNHANDLE);
  nodeJump.setRemark("无");
  if(cmd instanceof ProcessStartCmd){
   bpmNodeJumpManager.create(nodeJump);
  }else{
   bpmNodeJumpManager.create(nodeJump);
  }
 }
 
}

【说明】
我们通过在监听这个事件,完成了很多activiti没有处理的数据,如创建执行路径,为后续的任务回退进行做准备,进行任务的人员分配处理等。

© 著作权归作者所有

keitch
粉丝 2
博文 5
码字总数 7070
作品 0
广州
后端工程师
私信 提问
Activiti 5.16 版本发布 - 基于BPMN2.0规范的流程引擎

Activiti 5.16 版本发布,此版本更新内容如下: 添加了对Spring boot的支持,支持更多的Spring注解(多谢Josh Long) 重构作业执行器(调度器)简化逻辑,防止长时间等待 添加了新的事件(全...

咖啡兔
2014/07/23
8.7K
6
Mac OSX 鼠标键盘事件的监听和模拟

最近完成了 Mac OSX 平台下的远程控制功能,期间找了不少资料,这里做个总结,主要涉及到一下知识点: OSX 的事件机制 OSX/iOS 响应链者链 鼠标事件的监听及模拟(鼠标单击、双击、拖动、滚动...

topws1
2018/10/12
0
0
「前端进阶」如何优雅的处理图片异常

引子 在我们的实际工作中,不可避免的会在页面中加载大量图片,但可能由于网络问题,或者图片文件缺失等问题,导致图片不能正常展示 我们希望有一种降级处理的方式,可以在图片加载失败后显示...

云中桥
07/29
0
0
【原创】MySQL Proxy - 底层实现篇

底层实现篇(chassis) 【Configfile and Commandline Options】 glib2 提供了 config-file 解析和 command-line option 解析功能。 其提供了将 option 以相同方式暴露给调用者的方法,以及从...

摩云飞
2013/03/17
2.7K
5
Webpack原理与实践(一):打包流程

写在前面的话 在阅读 源码的过程中,参考了《深入浅出webpack》一书和众多大神的文章,结合自己的一点体会,总结如下。 总述 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出...

薄荷前端
2018/11/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
今天
1K
12
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
22
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
17
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
29
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部