Activiti7 Events Monitor (Leafage.top)
I haven’t been taking notes for a while, but I’ve been doing some work on workflows recently and documenting my experience with Activiti 7.
Requirements:
- In the process of process initiation and operation, send notification and reminder of process approval to relevant personnel;
- Do not manually add it during the configuration of the flow. It cannot intrude into the process of the flow operation and affect the flow execution.
How do I get this? The official documentation for Activiti, Activiti7, is as bad as shit, feels so hard 😔…
There is an example module called Activiti-Examples. You can’t run the examples directly, you can just look at them. To run them, copy, paste, and add them to your own project. Off topic, talking about the topic…
There are several important classes or interfaces associated with Activiti:
-
Each process information in Activiti is described by ProcessInstance, which has several states: Created, started, completed, cancelled, resumed, updated, suspended, and corresponding related event description classes are: ProcessCreatedEvent, ProcessStartedEvent, ProcessCompletedEvent, ProcessCancelledEvent, ProcessResumedEvent, ProcessUpdatedEv Ent, ProcessSuspendedEvent, etc.
-
Each process node is described by Task in Activiti. It has several states: Created, assigned, Completed, updated, cancelled, suspended, etc. Corresponding event description classes are: TaskCreatedEvent, TaskAssignedEvent, TaskCompletedEvent, TaskUpdatedEvent, TaskCancelledEvent, TaskSuspendedEvent
How do I configure listeners?
1. Global event listener
Involves two class \ interface, global event listener ActivitiEventListener and ProcessEngineConfigurationConfigurer (has a default implementation class: DefaultActivityBehaviorFactoryMappingConfigurer)
The ActitiviEventListener interface has a void onEvent(ActivitiEvent ActivitiEvent) method that takes place when the event state changes. The source code is as follows:
/**
* Describes a class that listens for {@link ActivitiEvent}s dispatched by the engine.
*
*/
public interface ActivitiEventListener {
void onEvent(ActivitiEvent event);
boolean isFailOnException(a);
}
Copy the code
ActivitiEvent contains the process definition ID, sample ID, execution ID, and event type information. The source code is as follows:
public interface ActivitiEvent {
ActivitiEventType getType(a);
String getExecutionId(a);
String getProcessInstanceId(a);
String getProcessDefinitionId(a);
}
Copy the code
Its event types include many, source code as follows:
public enum ActivitiEventType {
// ENTITY: process instance that is created from the process template when initiating the process
ENTITY_CREATED, / / create
ENTITY_INITIALIZED, ENTITY_CREATED (if the creation of the entity includes the creation of child entities, this event will be emitted after the child entities are created/initialized, which is different from ENTITY_CREATED)
ENTITY_UPDATED, / / update
ENTITY_DELETED, / / delete
ENTITY_SUSPENDED, // Suspend (thrown by ProcessDefinitions, ProcessInstances, and Tasks)
ENTITY_ACTIVATED, // Activate (thrown by ProcessDefinitions, ProcessInstances, and Tasks)
/ / timer
TIMER_SCHEDULED, / / create
TIMER_FIRED, / / triggers
/ / homework
JOB_CANCELED, / / cancel
JOB_EXECUTION_SUCCESS, // The command is successfully executed
JOB_EXECUTION_FAILURE, // Execution failed
JOB_RETRIES_DECREMENTED, // Fewer retries (fewer retries because the job failed)
CUSTOM, / / custom
/ / the engine
ENGINE_CREATED, / / create
ENGINE_CLOSED, / / close
// Process node
ACTIVITY_STARTED, / /
ACTIVITY_COMPLETED, / / finish
ACTIVITY_CANCELLED, / / cancel
ACTIVITY_SIGNALED, // A signal is received
ACTIVITY_COMPENSATE, // will be compensated
ACTIVITY_MESSAGE_SENT, // Message sent
ACTIVITY_MESSAGE_WAITING, // Message waiting
ACTIVITY_MESSAGE_RECEIVED, // Message receive
ACTIVITY_ERROR_RECEIVED, // Failed to receive
// Process history
HISTORIC_ACTIVITY_INSTANCE_CREATED, / / create
HISTORIC_ACTIVITY_INSTANCE_ENDED, / / end
// Queue process
SEQUENCEFLOW_TAKEN, / / has taken
UNCAUGHT_BPMN_ERROR, // No BPMN exception is found
/ / variable
VARIABLE_CREATED, / / create
VARIABLE_UPDATED, / / update
VARIABLE_DELETED, / / delete
/ / task
TASK_CREATED, // Create (it comes after the ENTITY_CREATE event. When a task is created by a process, this event is executed before the TaskListener executes.)
TASK_ASSIGNED, / / distribution
TASK_COMPLETED, // Done (this is triggered before the ENTITY_DELETE event. When the task is part of the process, the event will be ACTIVITY_COMPLETE, corresponding to the node that completed the task, before the process continues.)
/ / process
PROCESS_STARTED, / /
PROCESS_COMPLETED, // Complete (triggered after the ACTIVITY_COMPLETED event on the last node. The process ends when it reaches a state where there are no further connections.
PROCESS_COMPLETED_WITH_ERROR_END_EVENT, // Abnormal end
PROCESS_CANCELLED, / / cancel
HISTORIC_PROCESS_INSTANCE_CREATED, // Process instance creation
HISTORIC_PROCESS_INSTANCE_ENDED, // Process instance creation
/ / members
MEMBERSHIP_CREATED, // The user is added to a group
MEMBERSHIP_DELETED, // The user was deleted from a group
MEMBERSHIPS_DELETED; // All members are removed from a group
// other code ...
}
Copy the code
ProcessEngineConfigurationConfigurer void in the configure (SpringProcessEngineConfiguration SpringProcessEngineConfiguration) method can add a custom event listener, the listener process scope for the entire process. The source code is as follows:
public class DefaultActivityBehaviorFactoryMappingConfigurer implements ProcessEngineConfigurationConfigurer {
private VariablesMappingProvider variablesMappingProvider;
private ProcessVariablesInitiator processVariablesInitiator;
private final EventSubscriptionPayloadMappingProvider eventSubscriptionPayloadMappingProvider;
public DefaultActivityBehaviorFactoryMappingConfigurer(VariablesMappingProvider variablesMappingProvider, ProcessVariablesInitiator processVariablesInitiator, EventSubscriptionPayloadMappingProvider eventSubscriptionPayloadMappingProvider){
this.variablesMappingProvider = variablesMappingProvider;
this.processVariablesInitiator = processVariablesInitiator;
this.eventSubscriptionPayloadMappingProvider = eventSubscriptionPayloadMappingProvider;
}
@Override
public void configure(SpringProcessEngineConfiguration processEngineConfiguration){
processEngineConfiguration.setEventSubscriptionPayloadMappingProvider(eventSubscriptionPayloadMappingProvider);
processEngineConfiguration.setActivityBehaviorFactory(newMappingAwareActivityBehaviorFactory(variablesMappingProvider, processVariablesInitiator)); }}Copy the code
How do I listen for events?
- ActivitiEventListener interface, rewrite void onEvent(ActivitiEvent) method;
- Added configuration class, Inheritance DefaultActivityBehaviorFactoryMappingConfigurer class (or implement ProcessEngineConfigurationConfigurer interface) and re-written configura () method, Add custom listener to SpringProcessEngineConfiguration attribute implementation class;
The following is an example:
@Configuration
public class ActivitiConfiguration extends DefaultActivityBehaviorFactoryMappingConfigurer {
@Autowired
ActivitiTaskLogService activitiTaskLogService;
@Autowired
UserService userService;
public ActivitiConfiguration(VariablesMappingProvider variablesMappingProvider, ProcessVariablesInitiator processVariablesInitiator, EventSubscriptionPayloadMappingProvider eventSubscriptionPayloadMappingProvider) {
super(variablesMappingProvider, processVariablesInitiator, eventSubscriptionPayloadMappingProvider);
}
@Override
public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
super.configure(springProcessEngineConfiguration);
springProcessEngineConfiguration.setEventListeners(
Collections.singletonList(newActivitiTaskEventListener(activitiTaskLogService, userService))); }}Copy the code
public class ActivitiTaskEventListener implements ActivitiEventListener {
@Override
public void onEvent(ActivitiEvent activitiEvent) {
if(! StringUtils.hasText(activitiEvent.getProcessInstanceId())) {return;
}
processEngine = ProcessEngines.getDefaultProcessEngine();
// Process instance
ProcessInstance processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
.processInstanceId(activitiEvent.getProcessInstanceId()).singleResult();
if (processInstance == null) {
return;
}
List<ActivitiTaskLog> taskLogList = new ArrayList<>();
switch (activitiEvent.getType()) {
// After the task is executed
case TASK_COMPLETED:
// The task is processed and given to the creator, the candidate for the next task
List<ActivitiTaskLog> taskCompletedLogs = this.taskCompleted(processInstance, activitiEvent.getExecutionId());
if(! CollectionUtils.isEmpty(taskCompletedLogs)) { taskLogList.addAll(taskCompletedLogs); }break;
case PROCESS_COMPLETED:
// When the process is complete, send a notification to the founder, facilitator, and cc
List<ActivitiTaskLog> processCompletedLogs = this.processCompleted(processInstance);
if(! CollectionUtils.isEmpty(processCompletedLogs)) { taskLogList.addAll(processCompletedLogs); }break;
default:}// Perform log operations
if(! CollectionUtils.isEmpty(taskLogList)) { activitiTaskLogService.createBatch(taskLogList); }}}Copy the code
2. Runtime status listener:
In the example, there are two example projects, activiti-api-basic-process-example and activiti-api-basic-task-example, which show how to configure process and task listening at runtime.
Activiti-api-basic-process-example is an example of a process listener:
@Bean
public ProcessRuntimeEventListener<ProcessCompletedEvent> processCompletedListener(a) {
return processCompleted -> logger.info(">>> Process Completed: '"
+ processCompleted.getEntity().getName() +
"' We can send a notification to the initiator: " + processCompleted.getEntity().getInitiator());
}
Copy the code
Activiti-api-basic-task-example:
@Bean
public TaskRuntimeEventListener<TaskAssignedEvent> taskAssignedListener(a) {
return taskAssigned -> logger.info(">>> Task Assigned: '"
+ taskAssigned.getEntity().getName() +
"' We can send a notification to the assginee: " + taskAssigned.getEntity().getAssignee());
}
@Bean
public TaskRuntimeEventListener<TaskCompletedEvent> taskCompletedListener(a) {
return taskCompleted -> logger.info(">>> Task Completed: '"
+ taskCompleted.getEntity().getName() +
"' We can send a notification to the owner: " + taskCompleted.getEntity().getOwner());
}
Copy the code
Referring to the example, we can customize the event listener configuration in the process. In this way, we do not need to implement the ActivitiEventlistener interface. Also do not need to inherit DefaultActivityBehaviorFactoryMappingConfigurer class or implement ProcessEngineConfigurationConfigurer interface, you just need to register related event listeners. The following is an example:
@Configuration
public class ActivitiConfiguration {
private final RuntimeService runtimeService;
private final ActRuTaskLogService actRuTaskLogService;
public ActivitiConfiguration(RuntimeService runtimeService, ActRuTaskLogService actRuTaskLogService) {
this.runtimeService = runtimeService;
this.actRuTaskLogService = actRuTaskLogService;
}
@Bean
public TaskRuntimeEventListener<TaskAssignedEvent> taskAssignedListener(a) {
return taskAssigned -> {
ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery()
.processInstanceId(taskAssigned.getProcessInstanceId()).singleResult();
String startUserId = execution.getStartUserId();
String fileProcInstId = this.fileProcInstId(execution);
// Exclude the task of initiating the application, sending a message to assignee
if(! taskAssigned.getEntity().getAssignee().equals(startUserId)) { Task task = taskAssigned.getEntity(); ActRuTaskLog taskLog =new ActRuTaskLog(task.getProcessInstanceId(), task.getId(),
taskAssigned.getEntity().getAssignee(), String.format(NotifyConstants.PENDING_WARN,
this.userName(startUserId), this.processType(fileProcInstId), this.projName(execution)), NotifyTypeConstants.CANDIDATE); actRuTaskLogService.create(taskLog); }}; }@Bean
public TaskRuntimeEventListener<TaskCompletedEvent> taskCompletedListener(a) {
return taskCompleted -> {
ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery()
.processInstanceId(taskCompleted.getProcessInstanceId()).singleResult();
String startUserId = execution.getStartUserId();
String fileProcInstId = this.fileProcInstId(execution);
Task task = taskCompleted.getEntity();
// Initiate approval, send a message to the cc person and the helper
if(! taskCompleted.getEntity().getAssignee().equals(startUserId)) {// Mission owner
String owner = taskCompleted.getEntity().getOwner();
ActRuTaskLog taskLog = new ActRuTaskLog(task.getProcessInstanceId(), task.getId(),
this.userName(owner), String.format(NotifyConstants.PENDING_WARN,
this.userName(startUserId), this.processType(fileProcInstId), this.projName(execution)),
NotifyTypeConstants.CANDIDATE);
actRuTaskLogService.create(taskLog);
} else {
// Send a notification to the initiator about the result of the task processing
ActRuTaskLog taskLog = new ActRuTaskLog(task.getProcessInstanceId(), task.getId(),
taskCompleted.getEntity().getAssignee(), String.format(NotifyConstants.PENDING,
this.userName(startUserId), this.processType(fileProcInstId), this.projName(execution),
this.userName(task.getAssignee()), ""), NotifyTypeConstants.PENDING); actRuTaskLogService.create(taskLog); }}; }@Bean
public TaskCandidateEventListener<TaskCandidateUserAddedEvent> taskCandidateUserEventListener(a) {
return taskCandidateEvent -> log.info(">>> Task Candidate User Add: '"
+ taskCandidateEvent.getEntity().toString());
}
@Bean
public TaskCandidateEventListener<TaskCandidateGroupAddedEvent> taskCandidateGroupEventListener(a) {
return taskCandidateEvent -> log.info(">>> Task Candidate Group Add: '"
+ taskCandidateEvent.getEntity().toString());
}
@Bean
public ProcessRuntimeEventListener<ProcessCompletedEvent> processCompletedEventListener(a) {
return processCompletedEvent -> log.info("===>>> Process Completed: '"
+ processCompletedEvent.getEntity().toString());
}
/** * get the process form name **@paramExecutionEntity Executes the object *@returnThe form of * /
private String projName(ExecutionEntity executionEntity) {
Object processInstanceName = executionEntity.getVariable("projName");
return null == processInstanceName ? "" : processInstanceName.toString();
}
/** * Get process file ID **@paramExecutionEntity Executes the object *@returnThe file ID * /
private String fileProcInstId(ExecutionEntity executionEntity) {
Object fileProcInstId = executionEntity.getVariable("fileProcInstId");
return fileProcInstId == null ? "" : fileProcInstId.toString();
}
/** * Approval type **@paramFileProcInstId File ID *@returnType * /
private String processType(String fileProcInstId) {
return StringUtils.hasText(fileProcInstId) ? "Being there" : "Contract";
}
/** * get the name **@paramUserId userId *@returnUser name */
private String userName(String userId) {
return userId + "Operator"; }}Copy the code
In the first scheme, ActivitiEvent is a superclass, and some attributes cannot be obtained directly. If you want to obtain them, you need to make strong downward transformation. In addition, the subclasses of each type of event are different, which requires a lot of judgment. Because the object in the current listener is the relevant object of the event corresponding to the change type, it can directly obtain the relevant variables and information.