Commit c11e6c76 authored by Brian Long's avatar Brian Long
Browse files

clear/unsubscribe old process definitions

parent 5d0a77f6
Loading
Loading
Loading
Loading
+9 −5
Original line number Diff line number Diff line
@@ -240,7 +240,7 @@ public class MQProcessDefinitionMonitor implements ActivitiEventListener, Applic
	protected void onProcessDefinitionAddEvent(ProcessDefinitionEntity entity) {
		this.logger.debug("Triggered by process definition addition: {}", entity);

		this.unsubscribeMqSubscribeTasks(entity.getId());
		this.unsubscribeOtherMqSubscribeTasks(entity);
		
		ServiceTask task = this.findMqStartSubscribeTask(entity.getId());
		if (task == null)
@@ -260,7 +260,7 @@ public class MQProcessDefinitionMonitor implements ActivitiEventListener, Applic
		for (ProcessDefinitionEntity procDefEntity : procDefEntities) {
			this.logger.debug("Inspecting process definition: {}: {}: {}", procDefEntity.getId(), procDefEntity.getKey(), procDefEntity.getName());

			this.unsubscribeMqSubscribeTasks(procDefEntity.getId());
			this.unsubscribeOtherMqSubscribeTasks(procDefEntity);
			
			ServiceTask task = this.findMqStartSubscribeTask(procDefEntity.getId());
			if (task == null)
@@ -273,7 +273,7 @@ public class MQProcessDefinitionMonitor implements ActivitiEventListener, Applic
	protected void onProcessDefinitionRemoveEvent(ProcessDefinitionEntity entity) {
		this.logger.debug("Triggered by process definition removal: {}", entity);

		this.unsubscribeMqSubscribeTasks(entity.getId());
		this.unsubscribeOtherMqSubscribeTasks(entity);
		
		ServiceTask task = this.findMqStartSubscribeTask(entity.getId());
		if (task == null)
@@ -282,9 +282,13 @@ public class MQProcessDefinitionMonitor implements ActivitiEventListener, Applic
		this.deloopMqSubscribeTask(entity.getId());
	}
	
	protected void unsubscribeMqSubscribeTasks(String procDefId) {
	protected void unsubscribeOtherMqSubscribeTasks(ProcessDefinitionEntity procDef) {
		this.unsubscribeOtherMqSubscribeTasks(procDef.getId());
	}
	
	protected void unsubscribeOtherMqSubscribeTasks(String procDefId) {
		try {
			Set<String> executionIds = this.subscriptionService.clear(procDefId);
			Set<String> executionIds = this.subscriptionService.clearOtherVersions(procDefId);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Subscription executions ended early: {}: {}", procDefId, executionIds);
			} else {
+51 −17
Original line number Diff line number Diff line
@@ -5,12 +5,15 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@@ -18,6 +21,29 @@ public class MqExecutionService {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private ProcessEngine services;

	/**
	 * The size of the keys is limited to the number of process definitions
	 * series are defined.  This excludes versions.  It would actually only
	 * contain ones with an MQ subscribe task.  It would never be a significant
	 * memory hog.
	 * 
	 * The size of the values is limited to the number of active process
	 * definition versions exist.  So it would never be a significant memory
	 * hog.
	 * 
	 * The size of the keys/values have nothing to do with the number of
	 * process instances or executions.
	 * 
	 * This means it does not need to be trimmed.  However, it is a good idea
	 * to remove process definition identifiers (values) that have no active
	 * executions.  You could do the same with active activities, but cleaning
	 * up process definitions will clean those up too.
	 */
    private MultiValuedMap<String, String> processDefinitionKeyMap = new HashSetValuedHashMap<>();

	/**
	 * The size of the keys is limited to the number of process definitions
	 * defined.  It would actually only contain ones with an MQ subscribe task.
@@ -55,9 +81,9 @@ public class MqExecutionService {
    private MultiValuedMap<Pair<String, String>, String> activityExecutionMap = new HashSetValuedHashMap<>();
    
    public synchronized void executing(DelegateExecution execution) {
    	this.processDefinitionActivityMap.put(execution.getProcessDefinitionId(), execution.getCurrentActivityId());
    	
    	Pair<String, String> key = this.toKey(execution);
    	this.processDefinitionKeyMap.put(execution.getProcessDefinitionId(), key.getLeft());
    	this.processDefinitionActivityMap.put(key.getLeft(), key.getRight());
    	this.activityExecutionMap.put(key, execution.getId());
    }
    
@@ -67,31 +93,39 @@ public class MqExecutionService {
    }
    
    /**
     * @param processDefinitionId A process definition identifier.
     * @param latestProcessDefinitionId A process definition identifier to NOT clear.  All other versions will be cleared.
     * @return A set of execution identifiers that were in the now cleared map.
     */
    public synchronized Set<String> clear(String processDefinitionId) throws Exception {
    	Collection<String> activityIds = this.processDefinitionActivityMap.get(processDefinitionId);
    public synchronized Set<String> clearOtherVersions(String latestProcessDefinitionId) throws Exception {
    	ProcessDefinition latestProcessDefinition = this.services.getRepositoryService().getProcessDefinition(latestProcessDefinitionId);
    	String processDefinitionKey = latestProcessDefinition.getKey();

    	Set<String> executionIds = new HashSet<>();
    	
    	Collection<String> processDefinitionIds = this.processDefinitionKeyMap.get(processDefinitionKey);
    	for (String processDefinitionId : processDefinitionIds) {
    		this.processDefinitionKeyMap.removeMapping(processDefinitionKey, processDefinitionId);
    		
	    	Collection<String> activityIds = this.processDefinitionActivityMap.remove(processDefinitionId);
	    	if (activityIds == null) {
	    		this.logger.debug("No activities/executions to clear for process definition: {}", processDefinitionId);
	    		return Collections.emptySet();
	    	}
	    	
    	Set<String> executionIds = new HashSet<>();
    	
	    	for (String activityId : activityIds) {
	    		this.logger.trace("Clearing process definition activity: {}: {}", processDefinitionId, activityId);
	    		Pair<String, String> key = this.toKey(processDefinitionId, activityId);
    		Collection<String> activityExecutionIds = this.activityExecutionMap.get(key);
	    		Collection<String> activityExecutionIds = this.activityExecutionMap.remove(key);
	    		if (activityExecutionIds != null)
	    			executionIds.addAll(activityExecutionIds);
	    	}
    	}
    	
    	return executionIds;
    }
    
    protected Pair<String, String> toKey(DelegateExecution execution) {
    	return this.toKey(execution.getProcessDefinitionId(), execution.getCurrentActivityId());
    	return Pair.of(execution.getProcessDefinitionId(), execution.getCurrentActivityId());
    }
    
    protected Pair<String, String> toKey(String processDefinitionId, String activityId) {
+14 −5
Original line number Diff line number Diff line
@@ -4,9 +4,12 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.repository.ProcessDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@@ -14,6 +17,9 @@ public class MqSubscriptionService extends MqExecutionService {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private ProcessEngine services;
    
    /**
     * The size of the map has no limit.  It will grow with the number of
     * executions (related to process instances).
@@ -40,18 +46,21 @@ public class MqSubscriptionService extends MqExecutionService {
    }
    
    /**
     * @param processDefinitionId A process definition identifier.
     * @param latestProcessDefinitionId A process definition identifier to NOT clear.  All other versions will be cleared.
     * @return A set of execution identifiers subscribed to MQ that were in the now cleared map; all subscriptions now ended. 
     */
    @Override
    public synchronized Set<String> clear(String processDefinitionId) throws Exception {
    	Set<String> executionIds = super.clear(processDefinitionId);
    public synchronized Set<String> clearOtherVersions(String latestProcessDefinitionId) throws Exception {
    	Set<String> executionIds = super.clearOtherVersions(latestProcessDefinitionId);

    	ProcessDefinition latestProcessDefinition = this.services.getRepositoryService().getProcessDefinition(latestProcessDefinitionId);
    	String processDefinitionKey = latestProcessDefinition.getKey();
    	
    	for (String executionId : executionIds) {
    		this.logger.trace("Clearing process definition execution: {}: {}", processDefinitionId, executionId);
    		this.logger.trace("Clearing process definition execution: {}: {}", latestProcessDefinitionId, executionId);
    		AutoCloseable consumer = this.executionSubscriptionMap.remove(executionId);
    		if (consumer != null) {
        		this.logger.debug("Closing MessageConsumer to terminate a subscription early: {}: {}: {}", processDefinitionId, executionId, consumer);
        		this.logger.debug("Closing MessageConsumer to terminate a subscription early: {}: {}: {}", processDefinitionKey, executionId, consumer);
    			consumer.close();
    		}
    	}