概述

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没有处理的数据,如创建执行路径,为后续的任务回退进行做准备,进行任务的人员分配处理等。

了解咨询QQ:1361783075

Activiti的全局事件机制及其监听处理的更多相关文章

  1. Pox启动及事件产生、监听分析

        ./pox/pox.py , Pox       实例化core=pox.core.initialize(),即为实例化POXCore类(该类是所有组件的交接点,提供组件注册功能),监听cor ...

  2. 关于JAVA中事件分发和监听机制实现的代码实例-绝对原创实用

    http://blog.csdn.net/5iasp/article/details/37054171 文章标题:关于JAVA中事件分发和监听机制实现的代码实例 文章地址: http://blog.c ...

  3. Spring事件发布与监听机制

    我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 目录 ...

  4. JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换

    本篇博客我们就来聊一下Spring框架中的观察者模式的应用,即事件的发送与监听机制.之前我们已经剖析过观察者模式的具体实现,以及使用Swift3.0自定义过通知机制.所以本篇博客对于事件发送与监听的底 ...

  5. spring 自定义事件发布及监听(简单实例)

    前言: Spring的AppilcaitionContext能够发布事件和注册相对应的事件监听器,因此,它有一套完整的事件发布和监听机制. 流程分析: 在一个完整的事件体系中,除了事件和监听器以外,还 ...

  6. spring中的事件发布与监听

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. spring事件发布与监听的应用场景 当处理完一段代码逻辑,接下来需要同 ...

  7. Spring知识点回顾(07)事件发布和监听

    Spring知识点回顾(07)事件发布和监听 1.DemoEvent extends ApplicationEvent { public DemoEvent(Object source, String ...

  8. apiCloud事件发送与监听

    apiCloud事件发送与监听 1.sendEvent 将任意一个自定义事件广播出去,该事件可在任意页面通过 addEventListener 监听收到. sendEvent({params}) 2. ...

  9. SpringBoot系列——事件发布与监听

    前言 日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱.短信提示用户,通常我们都是这样写: /** * 用户注册 */ @GetMapping("/userRegi ...

随机推荐

  1. InnoDB和Foreign KEY Constraints

    InnoDB表中中Foreign Key定义 1. InnoDB允许a foreign key引用一个索引列或者索引组列. 2. InnoDB现在并不支持用户定义的分区表有foreign keys,这 ...

  2. devexpress表格控件gridcontrol特殊应用(一)——实现禁用特定行(附源代码)

    一些特殊的项目中会存在一些特殊需求,如需要禁用特定行.这时候gridcontrol的一般属性是实现不了的,就需要做一些更改.这时候你就需要去devexpress官网中找寻些资料(官网https://w ...

  3. LNMP系统服务搭建过程详解

    和LAMP不同的是LNMP中的N指的是Nginx(类似于Apache的一种web服务软件)其他都一样.目前这种环境应用的也是非常之多.Nginx设计的初衷是提供一种快速高效多并发的web服务软件.在静 ...

  4. c++ TCP keepalive 使用

    来源:http://blog.csdn.net/weiwangchao_/article/details/7225338 http://www.cnitblog.com/zouzheng/archiv ...

  5. 【排序算法】快速排序算法 Java实现

    快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序.它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod). 基本思想 先从数组中找出一个数作为基 ...

  6. Kafka概述与设计原理

    kafka是一种高吞吐量的分布式发布订阅消息系统,有如下特性: 1. 通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能. 2 .高吞吐量:即使是 ...

  7. MySQL优化-一 、缓存优化

    body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-top: 10 ...

  8. 开始了大概三四天的Rails学习之路

    最近因为一位极光推送朋友,我开始了大概三四天的Rails学习之路,最终达到的水平是可以比较轻松地做出大部分功能,然后自我感觉可以自如地按照Rails的设计思想去思考.由于编程的日益流行,我结识了越来越 ...

  9. Collector for ArcGIS的使用体验

    基于Esri的Portal for ArcGIS(下面简称Portal),用户可以搭建一个本地的地理信息云平台.围绕着这个云平台,Esri为不同的终端提供了响应的解决方案,其中Collector fo ...

  10. 【Unity编程】Unity中的欧拉旋转

    欧拉角的定义 在写这篇博客之前,我搜索了网上很多关于欧拉角的定义,发现大部分引用自维基百科的定义,我这里也引述一下: 维基百科定义 莱昂哈德·欧拉用欧拉角来描述刚体在三维欧几里得空间的取向.对于任何参 ...