Global event mechanism of Activiti and its listening and processing

Summary

After 5.15, Activiti has added a unified event entry. It is not necessary to add the following configuration for each node and process in the BPMN file defined by the process when listening for process events, so as to realize the practice of listening for events. This practice leads us to need to set the BPMN file when publishing the process, which is very inconvenient. If we adjust its XML Or Class class Class name or package name, BPMN files need to be modified and published again. The difficulty can be imagined.

In order to avoid this problem, we reintroduce the unified monitoring mechanism. The idea comes from the development guidance document of Activiti, as follows:

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

Build event distributor for Activiti

Unified event processing is conducive to providing a unified entry for the combination of process and business, and the same entry provides convenience for subsequent process expansion, including the assignment of task personnel, countersignature calculation, process fallback processing, process log and other data entry points, so it is very important to build our event listener.

Entry to the defined global event listener

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 The global event listener of, that is, all events need to be distributed and processed uniformly here
 * @author csx
 * @copyright http://www.redxun.cn
 *
 */
public class GlobalEventListener implements ActivitiEventListener{
 /**
  * Log processor
  */
 public final static Log logger=LogFactory.getLog(GlobalEventListener.class);
 
 //Event and event handler
 //private Map<String,EventHandler> handlers=new HashMap<String, EventHandler>();
 //Change to the following mode to prevent ProcessEngine from not being created when the Spring container starts, and this reference is used in the business class
 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);
  //Find the corresponding event handler according to the type ID of the event
  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;
 }

 
}

How to add global monitoring to Activiti configuration

<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="Blackbody"/>
    <property name="labelFontName" value="Blackbody"/>
    <!-- Used to change the execution behavior of a process node -->
    <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>

Define your own event handler interface

/**
 *  Activiti Event handler for
 * @author csx
 *
 */
public interface EventHandler {
 /**
  * Event handler
  * @param event
  */
 public void handle(ActivitiEvent event);
}

Realize own task monitoring and processing

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;
/**
 * Task creation listener
 * It is mainly used for personnel allocation, event execution, etc
 * @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");
  //Handling events
  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;
    }
   }
   //Execution script
   if(bpmEventConfig!=null && StringUtils.isNotEmpty(bpmEventConfig.getScript())){
    logger.debug("===================execute the script in task create listener:"+bpmEventConfig.getScript());
    Map<String,Object> vars=taskEntity.getVariables();
    //Put task entity variables in
    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());
  
  //Title Handling of tasks performed
  String processSubject=(String)taskEntity.getVariable("processSubject");
  String solId=(String)taskEntity.getVariable("solId");
  taskEntity.setDescription(processSubject);
  taskEntity.setSolId(solId);
  taskEntity.setTenantId(ContextUtil.getCurrentTenantId());
  
  //Record jump information
  createNodeJump(taskEntity);
  
  //Handling of execution events
  executeScript(taskEntity);
  //Whether the task has been assigned
  boolean isAssigned=false;
  //Check whether it is a countersign task. If so, obtain the executor from the variable first
  String multiInstance=(String)taskEntity.getExecution().getActivity().getProperty("multiInstance");
  //If it is a fallback processing and the fallback node is not a countersign node, then
  BpmRuPath backRuPath=ProcessHandleHelper.getBackPath();
  if(backRuPath!=null && StringUtils.isEmpty(multiInstance)){
   if("userTask".equals(backRuPath.getNodeType())){
    taskEntity.setAssignee(backRuPath.getAssignee());
    isAssigned=true;
   }else{//Find the executor on its child node 
    BpmRuPath nodePath= bpmRuPathManager.getByParentIdNodeId(backRuPath.getPathId(),taskEntity.getTaskDefinitionKey());
    if(nodePath!=null && StringUtils.isNotEmpty(nodePath.getAssignee())){
     taskEntity.setAssignee(nodePath.getAssignee());
     isAssigned=true;
    }
   }
  }
  //If it has been assigned, the personnel data will not be obtained from the configuration data
  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());
   
   //Take priority from variables
   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 it has been assigned, the personnel data will not be obtained from the configuration data
  if(isAssigned){
   publishAssignEvent(taskEntity);
   return;
  }
  

  //Get the people list map from the thread (that is, the people configuration passed from the page)
  //Give priority to the staffing in the page
  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 it has been assigned, the personnel data will not be obtained from the configuration data
  if(isAssigned){
   publishAssignEvent(taskEntity);
   return;
  }
  
  //Get a list of staffing information
  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);
  }
  
  
    
 }
 
 /**
  * Publish task assignment event
  * @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());
  //Get task creation time
  nodeJump.setNodeName(taskEntity.getName());
  nodeJump.setNodeId(taskEntity.getTaskDefinitionKey());
  nodeJump.setHandlerId(ContextUtil.getCurrentUserId());
  IExecutionCmd cmd=ProcessHandleHelper.getProcessCmd();
  nodeJump.setCheckStatus(BpmNodeJump.JUMP_TYPE_UNHANDLE);
  nodeJump.setRemark("nothing");
  if(cmd instanceof ProcessStartCmd){
   bpmNodeJumpManager.create(nodeJump);
  }else{
   bpmNodeJumpManager.create(nodeJump);
  }
 }
 
}

[explanation]

By listening to this event, we have completed a lot of data that activiti did not process, such as creating an execution path, preparing for the follow-up task fallback, and allocating personnel for the task.

191 original articles published, 96 praised, 340000 visitors+
His message board follow

Tags: Java Apache Spring xml

Posted on Wed, 12 Feb 2020 09:29:33 -0500 by Hebbs