activiti7 实现 flowable 自由跳转方式的可行性研究
背景
这段时间在了解activiti7相关知识,感觉activiti在一些方面真没有考虑中国式流程,尤其是自由跳转方面。对比而言,flowable6.x版本,则提供的很好的服务接口。因此就想着,能不能把 flowable6.x跳转的服务加入到activiti7中。
新flowable6.X 自由跳转API
String processInstanceId = "";
String currentActivityId = "当前节点编号";
String targetActivityId = "目标节点编号";
runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdTo(currentActivityId,targetActivityId ).changeState();
难点
flowable 拓展了很多activiti 未有的类,而这个跳转的 API也是拓展的,由于有差异性,因此不能完全赋值。在这个过程中,需要不断调整对应属性。
activiti7实现
AbstractDynamicStateManager
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.purcotton.workflow.activiti.core.MoveExecutionEntityContainer.FlowElementMoveEntry;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.event.ActivitiEventDispatcher;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.engine.impl.history.HistoryManager;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.*;
import org.activiti.engine.impl.util.CollectionUtil;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.runtime.api.impl.TaskRuntimeHelper;
import org.apache.commons.lang3.StringUtils;
import org.activiti.bpmn.model.Activity;
import org.activiti.bpmn.model.BoundaryEvent;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.CallActivity;
import org.activiti.bpmn.model.CompensateEventDefinition;
import org.activiti.bpmn.model.EventSubProcess;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowElementsContainer;
import org.activiti.bpmn.model.Gateway;
import org.activiti.bpmn.model.IOParameter;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.bpmn.model.SubProcess;
import org.activiti.bpmn.model.UserTask;
import org.activiti.bpmn.model.ValuedDataObject;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.delegate.ActivityBehavior;
import org.activiti.engine.impl.persistence.deploy.DeploymentManager;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;
import org.activiti.engine.impl.util.ProcessInstanceHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author zhoulin.zhu
*/
public abstract class AbstractDynamicStateManager {
protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
//-- Move container preparation section start
public List<MoveExecutionEntityContainer> resolveMoveExecutionEntityContainers(ChangeActivityStateBuilderImpl changeActivityStateBuilder, Optional<String> migrateToProcessDefinitionId, Map<String, Object> variables, CommandContext commandContext) {
List<MoveExecutionEntityContainer> moveExecutionEntityContainerList = new ArrayList<>();
if (changeActivityStateBuilder.getMoveExecutionIdList().size() > 0) {
for (MoveExecutionIdContainer executionContainer : changeActivityStateBuilder.getMoveExecutionIdList()) {
//Executions belonging to the same parent should move together - i.e multipleExecution to single activity
Map<String, List<ExecutionEntity>> executionsByParent = new HashMap<>();
for (String executionId : executionContainer.getExecutionIds()) {
ExecutionEntity execution = resolveActiveExecution(executionId, commandContext);
List<ExecutionEntity> executionEntities = executionsByParent.computeIfAbsent(execution.getParentId(), k -> new ArrayList<>());
executionEntities.add(execution);
}
executionsByParent.values().forEach(executions -> {
MoveExecutionEntityContainer moveExecutionEntityContainer = new MoveExecutionEntityContainer(executions, executionContainer.getMoveToActivityIds());
executionContainer.getNewAssigneeId().ifPresent(moveExecutionEntityContainer::setNewAssigneeId);
moveExecutionEntityContainerList.add(moveExecutionEntityContainer);
});
}
}
if (changeActivityStateBuilder.getMoveActivityIdList().size() > 0) {
for (MoveActivityIdContainer activityContainer : changeActivityStateBuilder.getMoveActivityIdList()) {
Map<String, List<ExecutionEntity>> activitiesExecutionsByMultiInstanceParentId = new HashMap<>();
List<ExecutionEntity> activitiesExecutionsNotInMultiInstanceParent = new ArrayList<>();
for (String activityId : activityContainer.getActivityIds()) {
List<ExecutionEntity> activityExecutions = resolveActiveExecutions(changeActivityStateBuilder.getProcessInstanceId(), activityId, commandContext);
if (!activityExecutions.isEmpty()) {
// check for a multi instance root execution
ExecutionEntity miExecution = null;
boolean isInsideMultiInstance = false;
for (ExecutionEntity possibleMIExecution : activityExecutions) {
if (possibleMIExecution.isMultiInstanceRoot()) {
miExecution = possibleMIExecution;
isInsideMultiInstance = true;
break;
}
if (isExecutionInsideMultiInstance(possibleMIExecution)) {
isInsideMultiInstance = true;
}
}
//If inside a multiInstance, we create one container for each execution
if (isInsideMultiInstance) {
//We group by the parentId (executions belonging to the same parent execution instance
// i.e. gateways nested in MultiInstance subProcesses, need to be in the same move container)
Stream<ExecutionEntity> executionEntitiesStream = activityExecutions.stream();
if (miExecution != null) {
executionEntitiesStream = executionEntitiesStream.filter(ExecutionEntity::isMultiInstanceRoot);
}
executionEntitiesStream.forEach(childExecution -> {
String parentId = childExecution.isMultiInstanceRoot() ? childExecution.getId() : childExecution.getParentId();
List<ExecutionEntity> executionEntities = activitiesExecutionsByMultiInstanceParentId.computeIfAbsent(parentId, k -> new ArrayList<>());
executionEntities.add(childExecution);
});
} else {
ExecutionEntity execution = activityExecutions.iterator().next();
activitiesExecutionsNotInMultiInstanceParent.add(execution);
}
}
}
//Create a move container for each execution group (executionList)
Stream.concat(activitiesExecutionsByMultiInstanceParentId.values().stream(), Stream.of(activitiesExecutionsNotInMultiInstanceParent))
.filter(executions -> executions != null && !executions.isEmpty())
.forEach(executions -> moveExecutionEntityContainerList.add(createMoveExecutionEntityContainer(activityContainer, executions, commandContext)));
}
}
return moveExecutionEntityContainerList;
}
protected ExecutionEntity resolveActiveExecution(String executionId, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity execution = executionEntityManager.findById(executionId);
if (execution == null) {
throw new ActivitiException("Execution could not be found with id " + executionId);
}
return execution;
}
protected List<ExecutionEntity> resolveActiveExecutions(String processInstanceId, String activityId, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity processExecution = executionEntityManager.findById(processInstanceId);
if (processExecution == null) {
throw new ActivitiException("Execution could not be found with id " + processInstanceId);
}
if (!processExecution.isProcessInstanceType()) {
throw new ActivitiException("Execution is not a process instance type execution for id " + processInstanceId);
}
List<ExecutionEntity> childExecutions = executionEntityManager.findChildExecutionsByProcessInstanceId(processExecution.getId());
List<ExecutionEntity> executions = childExecutions.stream()
.filter(e -> e.getCurrentActivityId() != null)
.filter(e -> e.getCurrentActivityId().equals(activityId))
.collect(Collectors.toList());
if (executions.isEmpty()) {
throw new ActivitiException("Active execution could not be found with activity id " + activityId);
}
return executions;
}
protected MoveExecutionEntityContainer createMoveExecutionEntityContainer(MoveActivityIdContainer activityContainer, List<ExecutionEntity> executions, CommandContext commandContext) {
MoveExecutionEntityContainer moveExecutionEntityContainer = new MoveExecutionEntityContainer(executions, activityContainer.getMoveToActivityIds());
activityContainer.getNewAssigneeId().ifPresent(moveExecutionEntityContainer::setNewAssigneeId);
if (activityContainer.isMoveToParentProcess()) {
ExecutionEntity processInstanceExecution = executions.get(0).getProcessInstance();
ExecutionEntity superExecution = processInstanceExecution.getSuperExecution();
if (superExecution == null) {
throw new ActivitiException("No parent process found for execution with activity id " + executions.get(0).getCurrentActivityId());
}
moveExecutionEntityContainer.setMoveToParentProcess(true);
moveExecutionEntityContainer.setSuperExecution(superExecution);
} else if (activityContainer.isMoveToSubProcessInstance()) {
moveExecutionEntityContainer.setMoveToSubProcessInstance(true);
moveExecutionEntityContainer.setCallActivityId(activityContainer.getCallActivityId());
moveExecutionEntityContainer.setCallActivitySubProcessVersion(activityContainer.getCallActivitySubProcessVersion());
}
return moveExecutionEntityContainer;
}
protected void prepareMoveExecutionEntityContainer(MoveExecutionEntityContainer moveExecutionContainer, Optional<String> migrateToProcessDefinitionId, CommandContext commandContext) {
ExpressionManager expressionManager = CommandContextUtil.getProcessEngineConfiguration(commandContext).getExpressionManager();
Optional<BpmnModel> bpmnModelToMigrateTo = migrateToProcessDefinitionId.map(ProcessDefinitionUtil::getBpmnModel);
boolean canContainerDirectMigrate = (moveExecutionContainer.getMoveToActivityIds().size() == 1) && (moveExecutionContainer.getExecutions().size() == 1);
for (String activityId : moveExecutionContainer.getMoveToActivityIds()) {
FlowElement currentFlowElement;
FlowElement newFlowElement;
String currentActivityId;
if (moveExecutionContainer.isMoveToParentProcess()) {
String parentProcessDefinitionId = moveExecutionContainer.getSuperExecution().getProcessDefinitionId();
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(parentProcessDefinitionId);
BpmnModel modelOfCallActivity = ProcessDefinitionUtil.getBpmnModel(moveExecutionContainer.getExecutions().get(0).getProcessDefinitionId());
currentActivityId = moveExecutionContainer.getExecutions().get(0).getCurrentActivityId();
currentFlowElement = resolveFlowElementFromBpmnModel(modelOfCallActivity, currentActivityId);
newFlowElement = resolveFlowElementFromBpmnModel(bpmnModelToMigrateTo.orElse(bpmnModel), activityId);
canContainerDirectMigrate = false;
} else if (moveExecutionContainer.isMoveToSubProcessInstance()) {
//The subProcess model is defined in the callActivity of the current processDefinition or the migrateProcessDefinition if defined
ExecutionEntity firstExecution = moveExecutionContainer.getExecutions().get(0);
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(firstExecution.getProcessDefinitionId());
currentActivityId = firstExecution.getCurrentActivityId();
currentFlowElement = resolveFlowElementFromBpmnModel(bpmnModel, currentActivityId);
String processDefinitionIdOfCallActivity = migrateToProcessDefinitionId.orElse(firstExecution.getProcessDefinitionId());
CallActivity callActivity = (CallActivity) resolveFlowElementFromBpmnModel(bpmnModelToMigrateTo.orElse(bpmnModel), moveExecutionContainer.getCallActivityId());
moveExecutionContainer.setCallActivity(callActivity);
ProcessDefinition callActivityProcessDefinition = ProcessDefinitionUtil.getProcessDefinition(processDefinitionIdOfCallActivity);
String tenantId = callActivityProcessDefinition.getTenantId();
Integer calledProcessVersion = moveExecutionContainer.getCallActivitySubProcessVersion();
String calledProcessDefKey = callActivity.getCalledElement();
if (isExpression(calledProcessDefKey)) {
try {
calledProcessDefKey = expressionManager.createExpression(calledProcessDefKey).getValue(firstExecution.getProcessInstance()).toString();
} catch (ActivitiException e) {
throw new ActivitiException("Cannot resolve calledElement expression '" + calledProcessDefKey + "' of callActivity '" + callActivity.getId() + "'", e);
}
}
moveExecutionContainer.setSubProcessDefKey(calledProcessDefKey);
ProcessDefinition subProcessDefinition = resolveProcessDefinition(calledProcessDefKey, calledProcessVersion, tenantId, commandContext);
BpmnModel subProcessModel = ProcessDefinitionUtil.getBpmnModel(subProcessDefinition.getId());
moveExecutionContainer.setSubProcessDefinition(subProcessDefinition);
moveExecutionContainer.setSubProcessModel(subProcessModel);
newFlowElement = resolveFlowElementFromBpmnModel(subProcessModel, activityId);
canContainerDirectMigrate = false;
} else {
// Get first execution to get process definition id
ExecutionEntity firstExecution = moveExecutionContainer.getExecutions().get(0);
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(firstExecution.getProcessDefinitionId());
currentActivityId = firstExecution.getCurrentActivityId();
currentFlowElement = resolveFlowElementFromBpmnModel(bpmnModel, currentActivityId);
newFlowElement = resolveFlowElementFromBpmnModel(bpmnModelToMigrateTo.orElse(bpmnModel), activityId);
}
moveExecutionContainer.addMoveToFlowElement(activityId, currentFlowElement, newFlowElement);
canContainerDirectMigrate = canContainerDirectMigrate && isDirectFlowElementExecutionMigration(currentFlowElement, newFlowElement);
}
moveExecutionContainer.setDirectExecutionMigration(canContainerDirectMigrate && migrateToProcessDefinitionId.isPresent());
}
protected FlowElement resolveFlowElementFromBpmnModel(BpmnModel bpmnModel, String activityId) {
FlowElement flowElement = bpmnModel.getFlowElement(activityId);
if (flowElement == null) {
throw new ActivitiException("Cannot find activity '" + activityId + "' in process definition with id '" + bpmnModel.getMainProcess().getId() + "'");
}
return flowElement;
}
//-- Move container preparation section end
protected void doMoveExecutionState(ProcessInstanceChangeState processInstanceChangeState, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
Map<String, List<ExecutionEntity>> activeEmbeddedSubProcesses = resolveActiveEmbeddedSubProcesses(processInstanceChangeState.getProcessInstanceId(), commandContext);
processInstanceChangeState.setProcessInstanceActiveEmbeddedExecutions(activeEmbeddedSubProcesses);
//Set the processInstance variables first so they are available during te change state
ExecutionEntity processInstanceExecution = executionEntityManager.findById(processInstanceChangeState.getProcessInstanceId());
processInstanceExecution.setVariables(processInstanceChangeState.getProcessInstanceVariables());
for (MoveExecutionEntityContainer moveExecutionContainer : processInstanceChangeState.getMoveExecutionEntityContainers()) {
prepareMoveExecutionEntityContainer(moveExecutionContainer, processInstanceChangeState.getProcessDefinitionToMigrateTo().map(ProcessDefinition::getId), commandContext);
// Action the moves (changeState)
if (moveExecutionContainer.isMoveToParentProcess()) {
String callActivityInstanceId = moveExecutionContainer.getExecutions().get(0).getProcessInstanceId();
String deleteReason = "Change activity to parent process activity ids: " + printFlowElementIds(moveExecutionContainer.getMoveToFlowElements());
safeDeleteSubProcessInstance(callActivityInstanceId, moveExecutionContainer.getExecutions(), deleteReason, commandContext);
}
List<ExecutionEntity> executionsToMove;
if (moveExecutionContainer.isMoveToParentProcess()) {
executionsToMove = Collections.singletonList(moveExecutionContainer.getSuperExecution());
} else {
executionsToMove = moveExecutionContainer.getExecutions();
}
Collection<FlowElementMoveEntry> moveToFlowElements;
if (moveExecutionContainer.isMoveToSubProcessInstance()) {
moveToFlowElements = Collections.singletonList(new FlowElementMoveEntry(moveExecutionContainer.getCallActivity(), moveExecutionContainer.getCallActivity()));
} else {
moveToFlowElements = moveExecutionContainer.getMoveToFlowElements();
}
String flowElementIdsLine = printFlowElementIds(moveToFlowElements);
Collection<String> executionIdsNotToDelete = new HashSet<>();
for (ExecutionEntity execution : executionsToMove) {
executionIdsNotToDelete.add(execution.getId());
executionEntityManager.deleteChildExecutions(execution, "Change parent activity to " + flowElementIdsLine, true);
if (!moveExecutionContainer.isDirectExecutionMigration()) {
//executionEntityManager.deleteExecutionAndRelatedData(execution, "Change activity to " + flowElementIdsLine, false, false, true, execution.getCurrentFlowElement());
executionEntityManager.deleteExecutionAndRelatedData(execution, "Change activity to " + flowElementIdsLine, true);
}
// Make sure we are not moving the root execution
if (execution.getParentId() == null) {
throw new ActivitiException("Execution has no parent execution " + execution.getParentId());
}
// Delete the parent executions for each current execution when the move to activity id has the same subProcess scope
ExecutionEntity continueParentExecution;
if (processInstanceChangeState.getProcessDefinitionToMigrateTo().isPresent()) {
continueParentExecution = deleteDirectParentExecutions(execution.getParentId(), moveToFlowElements, executionIdsNotToDelete, commandContext);
} else {
continueParentExecution = deleteParentExecutions(execution.getParentId(), moveToFlowElements, executionIdsNotToDelete, commandContext);
}
moveExecutionContainer.addContinueParentExecution(execution.getId(), continueParentExecution);
}
List<ExecutionEntity> newChildExecutions = createEmbeddedSubProcessAndExecutions(moveToFlowElements, executionsToMove, moveExecutionContainer, processInstanceChangeState, commandContext);
if (moveExecutionContainer.isMoveToSubProcessInstance()) {
CallActivity callActivity = moveExecutionContainer.getCallActivity();
Process subProcess = moveExecutionContainer.getSubProcessModel().getProcessById(moveExecutionContainer.getSubProcessDefKey());
ExecutionEntity callActivityInstanceExecution = createCallActivityInstance(callActivity, moveExecutionContainer.getSubProcessDefinition(), newChildExecutions.get(0), subProcess.getInitialFlowElement().getId(), commandContext);
List<ExecutionEntity> moveExecutions = moveExecutionContainer.getExecutions();
MoveExecutionEntityContainer subProcessMoveExecutionEntityContainer = new MoveExecutionEntityContainer(moveExecutions, moveExecutionContainer.getMoveToActivityIds());
subProcessMoveExecutionEntityContainer.setNewAssigneeId(moveExecutionContainer.getNewAssigneeId());
moveExecutions.forEach(executionEntity -> subProcessMoveExecutionEntityContainer.addContinueParentExecution(executionEntity.getId(), callActivityInstanceExecution));
newChildExecutions = createEmbeddedSubProcessAndExecutions(moveExecutionContainer.getMoveToFlowElements(), moveExecutions, subProcessMoveExecutionEntityContainer, new ProcessInstanceChangeState(), commandContext);
}
if (!processInstanceChangeState.getLocalVariables().isEmpty()) {
Map<String, Map<String, Object>> localVariables = processInstanceChangeState.getLocalVariables();
Iterator<ExecutionEntity> newChildExecutionsIterator = newChildExecutions.iterator();
while (newChildExecutionsIterator.hasNext()) {
//With changeState Api we can set local variables to the parents of moved executions (i.e. subProcesses created during the move)
//Thus we traverse up in the hierarchy from the newly created executions
ExecutionEntity execution = newChildExecutionsIterator.next();
while (execution != null) {
if (execution.getActivityId() != null && localVariables.containsKey(execution.getActivityId())) {
if (execution.isScope() || execution.getCurrentFlowElement() instanceof UserTask) {
execution.setVariablesLocal(localVariables.get(execution.getActivityId()));
} else {
ExecutionEntity scopedExecutionCandidate = execution;
while (scopedExecutionCandidate.getParent() != null) {
ExecutionEntity parentExecution = scopedExecutionCandidate.getParent();
if (parentExecution.isScope()) {
parentExecution.setVariablesLocal(localVariables.get(execution.getActivityId()));
break;
}
scopedExecutionCandidate = scopedExecutionCandidate.getParent();
}
}
}
execution = execution.getParent();
}
}
}
if (!moveExecutionContainer.isDirectExecutionMigration()) {
for (ExecutionEntity newChildExecution : newChildExecutions) {
if (moveExecutionContainer.getNewAssigneeId() != null && moveExecutionContainer.hasNewExecutionId(newChildExecution.getId())) {
// MigrationContext migrationContext = new MigrationContext();
// migrationContext.setAssignee(moveExecutionContainer.getNewAssigneeId());
// CommandContextUtil.getAgenda(commandContext).planContinueProcessOperation();
// CommandContextUtil.getAgenda(commandContext).planContinueProcessWithMigrationContextOperation(newChildExecution, migrationContext);
CommandContextUtil.getAgenda(commandContext).planContinueProcessOperation(newChildExecution);
} else {
CommandContextUtil.getAgenda(commandContext).planContinueProcessOperation(newChildExecution);
}
}
}
}
processPendingEventSubProcessesStartEvents(processInstanceChangeState, commandContext);
}
protected void processPendingEventSubProcessesStartEvents(ProcessInstanceChangeState processInstanceChangeState, CommandContext commandContext) {
ProcessInstanceHelper processInstanceHelper = CommandContextUtil.getProcessEngineConfiguration(commandContext).getProcessInstanceHelper();
for (Map.Entry<? extends StartEvent, ExecutionEntity> pendingStartEventEntry : processInstanceChangeState.getPendingEventSubProcessesStartEvents().entrySet()) {
StartEvent startEvent = pendingStartEventEntry.getKey();
ExecutionEntity parentExecution = pendingStartEventEntry.getValue();
if (!processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(startEvent.getSubProcess().getId())) {
// processInstanceHelper.processEventSubProcess(parentExecution, (EventSubProcess) startEvent.getSubProcess(), commandContext);
//@todo 事件子流程处理 许重写
}
}
}
protected abstract Map<String, List<ExecutionEntity>> resolveActiveEmbeddedSubProcesses(String processInstanceId, CommandContext commandContext);
protected abstract boolean isDirectFlowElementExecutionMigration(FlowElement currentFlowElement, FlowElement newFlowElement);
protected void safeDeleteSubProcessInstance(String processInstanceId, List<ExecutionEntity> executionsPool, String deleteReason, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
//Confirm that all the subProcessExecutions are in the executionsPool
List<ExecutionEntity> subProcessExecutions = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
HashSet<String> executionIdsToMove = executionsPool.stream().map(ExecutionEntity::getId).collect(Collectors.toCollection(HashSet::new));
Optional<ExecutionEntity> notIncludedExecution = subProcessExecutions.stream().filter(e -> !executionIdsToMove.contains(e.getId())).findAny();
if (notIncludedExecution.isPresent()) {
throw new ActivitiException("Execution of sub process instance is not moved " + notIncludedExecution.get().getId());
}
// delete the sub process instance
executionEntityManager.deleteProcessInstance(processInstanceId, deleteReason, true);
}
protected ExecutionEntity deleteParentExecutions(String parentExecutionId, Collection<FlowElementMoveEntry> moveToFlowElements, CommandContext commandContext) {
return deleteParentExecutions(parentExecutionId, moveToFlowElements, null, commandContext);
}
protected ExecutionEntity deleteParentExecutions(String parentExecutionId, Collection<FlowElementMoveEntry> moveToFlowElements, Collection<String> executionIdsNotToDelete, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity parentExecution = executionEntityManager.findById(parentExecutionId);
if (parentExecution != null && parentExecution.getCurrentFlowElement() instanceof SubProcess) {
SubProcess parentSubProcess = (SubProcess) parentExecution.getCurrentFlowElement();
if (!isSubProcessAncestorOfAnyNewFlowElements(parentSubProcess.getId(), moveToFlowElements)) {
ExecutionEntity toDeleteParentExecution = resolveParentExecutionToDelete(parentExecution, moveToFlowElements);
ExecutionEntity finalDeleteExecution = null;
if (toDeleteParentExecution != null) {
finalDeleteExecution = toDeleteParentExecution;
} else {
finalDeleteExecution = parentExecution;
}
parentExecution = finalDeleteExecution.getParent();
String flowElementIdsLine = printFlowElementIds(moveToFlowElements);
executionEntityManager.deleteChildExecutions(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);
executionEntityManager.deleteExecutionAndRelatedData(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);
}
}
return parentExecution;
}
protected ExecutionEntity deleteDirectParentExecutions(String parentExecutionId, Collection<FlowElementMoveEntry> moveToFlowElements, Collection<String> executionIdsNotToDelete, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity parentExecution = executionEntityManager.findById(parentExecutionId);
if (parentExecution.getCurrentFlowElement() instanceof SubProcess) {
SubProcess parentSubProcess = (SubProcess) parentExecution.getCurrentFlowElement();
if (!isSubProcessContainerOfAnyFlowElement(parentSubProcess.getId(), moveToFlowElements)) {
ExecutionEntity toDeleteParentExecution = resolveParentExecutionToDelete(parentExecution, moveToFlowElements);
ExecutionEntity finalDeleteExecution = null;
if (toDeleteParentExecution != null) {
finalDeleteExecution = toDeleteParentExecution;
} else {
finalDeleteExecution = parentExecution;
}
parentExecution = finalDeleteExecution.getParent();
String flowElementIdsLine = printFlowElementIds(moveToFlowElements);
executionEntityManager.deleteChildExecutions(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);
executionEntityManager.deleteExecutionAndRelatedData(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);
}
}
return parentExecution;
}
protected boolean isSubProcessContainerOfAnyFlowElement(String subProcessId, Collection<FlowElementMoveEntry> moveToFlowElements) {
Optional<SubProcess> isUsed = moveToFlowElements.stream()
.map(FlowElementMoveEntry::getNewFlowElement)
.map(FlowElement::getSubProcess)
.filter(Objects::nonNull)
.filter(elementSubProcess -> elementSubProcess.getId().equals(subProcessId))
.findAny();
return isUsed.isPresent();
}
protected ExecutionEntity resolveParentExecutionToDelete(ExecutionEntity execution, Collection<FlowElementMoveEntry> moveToFlowElements) {
ExecutionEntity parentExecution = execution.getParent();
if (parentExecution.isProcessInstanceType()) {
return null;
}
if (!isSubProcessContainerOfAnyFlowElement(parentExecution.getActivityId(), moveToFlowElements)) {
ExecutionEntity subProcessParentExecution = resolveParentExecutionToDelete(parentExecution, moveToFlowElements);
if (subProcessParentExecution != null) {
return subProcessParentExecution;
} else {
return parentExecution;
}
}
return null;
}
protected List<ExecutionEntity> createEmbeddedSubProcessAndExecutions(Collection<FlowElementMoveEntry> moveToFlowElements, List<ExecutionEntity> movingExecutions,
MoveExecutionEntityContainer moveExecutionEntityContainer, ProcessInstanceChangeState processInstanceChangeState, CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
ExecutionEntityManager executionEntityManager = processEngineConfiguration.getExecutionEntityManager();
// Resolve the sub process elements that need to be created for each move to flow element
HashMap<String, SubProcess> subProcessesToCreate = new HashMap<>();
for (FlowElementMoveEntry flowElementMoveEntry : moveToFlowElements) {
FlowElement newFlowElement = flowElementMoveEntry.getNewFlowElement();
SubProcess subProcess = newFlowElement.getSubProcess();
//If the new flowElement is the StartEvent of and EventSubProcess, we skip the subProcess creation, the startEvent is contained in a level above
if (isEventSubProcessStart(newFlowElement)) {
subProcess = subProcess.getSubProcess();
}
while (subProcess != null) {
if (!processInstanceChangeState.getProcessInstanceActiveEmbeddedExecutions().containsKey(subProcess.getId()) && !isSubProcessAncestorOfAnyExecution(subProcess.getId(), movingExecutions)) {
subProcessesToCreate.put(subProcess.getId(), subProcess);
}
subProcess = subProcess.getSubProcess();
}
}
// The default parent execution is retrieved from the match with the first source execution
ExecutionEntity defaultContinueParentExecution = moveExecutionEntityContainer.getContinueParentExecution(movingExecutions.get(0).getId());
Set<String> movingExecutionIds = movingExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toSet());
//Build the subProcess hierarchy
for (SubProcess subProcess : subProcessesToCreate.values()) {
if (!processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(subProcess.getId())) {
ExecutionEntity embeddedSubProcess = createEmbeddedSubProcessHierarchy(subProcess, defaultContinueParentExecution, subProcessesToCreate, movingExecutionIds, processInstanceChangeState, commandContext);
processInstanceChangeState.addCreatedEmbeddedSubProcess(subProcess.getId(), embeddedSubProcess);
}
}
//Adds the execution (leaf) to the subProcess
List<ExecutionEntity> newChildExecutions = new ArrayList<>();
for (FlowElementMoveEntry flowElementMoveEntry : moveToFlowElements) {
FlowElement newFlowElement = flowElementMoveEntry.getNewFlowElement();
ExecutionEntity parentExecution;
if (newFlowElement.getSubProcess() != null && processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(newFlowElement.getSubProcess().getId())) {
parentExecution = processInstanceChangeState.getCreatedEmbeddedSubProcesses().get(newFlowElement.getSubProcess().getId());
} else {
parentExecution = defaultContinueParentExecution;
}
if (isEventSubProcessStart(newFlowElement)) {
//EventSubProcessStarts are created later if the eventSubProcess was not created already during another move
processInstanceChangeState.addPendingEventSubProcessStartEvent((StartEvent) newFlowElement, parentExecution);
} else {
ExecutionEntity newChildExecution;
if (moveExecutionEntityContainer.isDirectExecutionMigration() && isDirectFlowElementExecutionMigration(flowElementMoveEntry.originalFlowElement, flowElementMoveEntry.newFlowElement)) {
newChildExecution = migrateExecutionEntity(parentExecution, movingExecutions.get(0), newFlowElement, commandContext);
} else {
newChildExecution = executionEntityManager.createChildExecution(parentExecution);
newChildExecution.setCurrentFlowElement(newFlowElement);
moveExecutionEntityContainer.addNewExecutionId(newChildExecution.getId());
}
if (newChildExecution != null) {
if (moveExecutionEntityContainer.getNewAssigneeId() != null && newFlowElement instanceof UserTask &&
!moveExecutionEntityContainer.hasNewExecutionId(newChildExecution.getId())) {
handleUserTaskNewAssignee(newChildExecution, moveExecutionEntityContainer.getNewAssigneeId(), commandContext);
}
if (newFlowElement instanceof CallActivity) {
processEngineConfiguration.getHistoryManager().recordActivityStart(newChildExecution);
ActivitiEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
eventDispatcher.dispatchEvent(
ActivitiEventBuilder.createActivityEvent(ActivitiEventType.ACTIVITY_STARTED, newFlowElement.getId(), newFlowElement.getName(), newChildExecution.getId(),
newChildExecution.getProcessInstanceId(), newChildExecution.getProcessDefinitionId(), newFlowElement));
}
}
newChildExecutions.add(newChildExecution);
}
// Parallel gateway joins needs each incoming execution to enter the gateway naturally as it checks the number of executions to be able to progress/continue
// If we have multiple executions going into a gateway, usually into a gateway join using xxxToSingleActivityId
if (newFlowElement instanceof Gateway) {
//Skip one that was already added
movingExecutions.stream().skip(1).forEach(e -> {
ExecutionEntity childExecution = executionEntityManager.createChildExecution(defaultContinueParentExecution);
childExecution.setCurrentFlowElement(newFlowElement);
newChildExecutions.add(childExecution);
});
}
}
}
return newChildExecutions;
}
protected boolean isSubProcessAncestorOfAnyExecution(String subProcessId, List<ExecutionEntity> executions) {
for (ExecutionEntity execution : executions) {
FlowElement executionElement = execution.getCurrentFlowElement();
if (isSubProcessAncestor(subProcessId, executionElement)) {
return true;
}
}
return false;
}
protected boolean isSubProcessAncestorOfAnyNewFlowElements(String subProcessId, Collection<FlowElementMoveEntry> flowElements) {
for (FlowElementMoveEntry flowElementMoveEntry : flowElements) {
if (isSubProcessAncestor(subProcessId, flowElementMoveEntry.getNewFlowElement())) {
return true;
}
}
return false;
}
private boolean isSubProcessAncestor(String subProcessId, FlowElement flowElement) {
while (flowElement.getSubProcess() != null) {
String execElemSubProcessId = flowElement.getSubProcess().getId();
if (execElemSubProcessId != null && execElemSubProcessId.equals(subProcessId)) {
return true;
}
flowElement = flowElement.getSubProcess();
}
return false;
}
protected List<FlowElement> getFlowElementsInSubProcess(SubProcess subProcess, Collection<FlowElement> flowElements) {
return flowElements.stream()
.filter(e -> e.getSubProcess() != null)
.filter(e -> e.getSubProcess().getId().equals(subProcess.getId()))
.collect(Collectors.toList());
}
protected ExecutionEntity createEmbeddedSubProcessHierarchy(SubProcess subProcess, ExecutionEntity defaultParentExecution, Map<String, SubProcess> subProcessesToCreate, Set<String> movingExecutionIds, ProcessInstanceChangeState processInstanceChangeState, CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
if (processInstanceChangeState.getProcessInstanceActiveEmbeddedExecutions().containsKey(subProcess.getId())) {
return processInstanceChangeState.getProcessInstanceActiveEmbeddedExecutions().get(subProcess.getId()).get(0);
}
if (processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(subProcess.getId())) {
return processInstanceChangeState.getCreatedEmbeddedSubProcesses().get(subProcess.getId());
}
//Create the parent, if needed
ExecutionEntity parentSubProcess = defaultParentExecution;
if (subProcess.getSubProcess() != null) {
parentSubProcess = createEmbeddedSubProcessHierarchy(subProcess.getSubProcess(), defaultParentExecution, subProcessesToCreate, movingExecutionIds, processInstanceChangeState, commandContext);
processInstanceChangeState.getCreatedEmbeddedSubProcesses().put(subProcess.getSubProcess().getId(), parentSubProcess);
}
ExecutionEntityManager executionEntityManager = processEngineConfiguration.getExecutionEntityManager();
ExecutionEntity subProcessExecution = executionEntityManager.createChildExecution(parentSubProcess);
subProcessExecution.setCurrentFlowElement(subProcess);
subProcessExecution.setScope(true);
ActivitiEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
eventDispatcher.dispatchEvent(
ActivitiEventBuilder.createActivityEvent(ActivitiEventType.ACTIVITY_STARTED, subProcess.getId(), subProcess.getName(), subProcessExecution.getId(),
subProcessExecution.getProcessInstanceId(), subProcessExecution.getProcessDefinitionId(), subProcess));
}
subProcessExecution.setVariablesLocal(processDataObjects(subProcess.getDataObjects()));
processEngineConfiguration.getHistoryManager().recordActivityStart(subProcessExecution);
List<BoundaryEvent> boundaryEvents = subProcess.getBoundaryEvents();
if (CollectionUtil.isNotEmpty(boundaryEvents)) {
executeBoundaryEvents(boundaryEvents, subProcessExecution);
}
if (subProcess instanceof EventSubProcess) {
processCreatedEventSubProcess((EventSubProcess) subProcess, subProcessExecution, movingExecutionIds, commandContext);
}
ProcessInstanceHelper processInstanceHelper = processEngineConfiguration.getProcessInstanceHelper();
//Process containing child Event SubProcesses not contained in this creation hierarchy
// List<EventSubProcess> childEventSubProcesses = subProcess.findAllSubFlowElementInFlowMapOfType(EventSubProcess.class);
// childEventSubProcesses.stream()
// .filter(childEventSubProcess -> !subProcessesToCreate.containsKey(childEventSubProcess.getId()))
// .forEach(childEventSubProcess -> processInstanceHelper.processEventSubProcess(subProcessExecution, childEventSubProcess, commandContext));
return subProcessExecution;
}
protected Map<String, Object> processDataObjects(Collection<ValuedDataObject> dataObjects) {
Map<String, Object> variablesMap = new HashMap<>();
// convert data objects to process variables
if (dataObjects != null) {
variablesMap = new HashMap<>(dataObjects.size());
for (ValuedDataObject dataObject : dataObjects) {
variablesMap.put(dataObject.getName(), dataObject.getValue());
}
}
return variablesMap;
}
protected void executeBoundaryEvents(Collection<BoundaryEvent> boundaryEvents, ExecutionEntity execution) {
// The parent execution becomes a scope, and a child execution is created for each of the boundary events
for (BoundaryEvent boundaryEvent : boundaryEvents) {
if (CollectionUtil.isEmpty(boundaryEvent.getEventDefinitions())
|| (boundaryEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition)) {
continue;
}
// A Child execution of the current execution is created to represent the boundary event being active
ExecutionEntity childExecutionEntity = CommandContextUtil.getExecutionEntityManager().createChildExecution(execution);
childExecutionEntity.setParentId(execution.getId());
childExecutionEntity.setCurrentFlowElement(boundaryEvent);
childExecutionEntity.setScope(false);
CommandContextUtil.getProcessEngineConfiguration().getHistoryManager().recordActivityStart(childExecutionEntity);
ActivityBehavior boundaryEventBehavior = ((ActivityBehavior) boundaryEvent.getBehavior());
LOGGER.debug("Executing boundary event activityBehavior {} with execution {}", boundaryEventBehavior.getClass(), childExecutionEntity.getId());
boundaryEventBehavior.execute(childExecutionEntity);
}
}
protected ExecutionEntity createCallActivityInstance(CallActivity callActivity, ProcessDefinition subProcessDefinition, ExecutionEntity parentExecution, String initialActivityId, CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
ExecutionEntityManager executionEntityManager = processEngineConfiguration.getExecutionEntityManager();
Process subProcess = ProcessDefinitionUtil.getProcess(subProcessDefinition.getId());
if (subProcess == null) {
throw new ActivitiException("Cannot start a sub process instance. Process model " + subProcessDefinition.getName() + " (id = " + subProcessDefinition.getId() + ") could not be found");
}
String businessKey = null;
if (!StringUtils.isEmpty(callActivity.getBusinessKey())) {
Expression expression = expressionManager.createExpression(callActivity.getBusinessKey());
businessKey = expression.getValue(parentExecution).toString();
} else if (callActivity.isInheritBusinessKey()) {
ExecutionEntity processInstance = executionEntityManager.findById(parentExecution.getProcessInstanceId());
businessKey = processInstance.getBusinessKey();
}
ExecutionEntity subProcessInstance = executionEntityManager.createSubprocessInstance(subProcessDefinition, parentExecution, businessKey);
//@todo
// EntityLinkUtil.createEntityLinks(parentExecution.getProcessInstanceId(), parentExecution.getId(), callActivity.getId(),
// subProcessInstance.getId(), ScopeTypes.BPMN);
CommandContextUtil.getHistoryManager().recordSubProcessInstanceStart(parentExecution, subProcessInstance,callActivity);
ActivitiEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
CommandContextUtil.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createEntityEvent(
ActivitiEventType.PROCESS_STARTED, subProcessInstance));
}
// process template-defined data objects
subProcessInstance.setVariables(processDataObjects(subProcess.getDataObjects()));
Map<String, Object> variables = new HashMap<>();
if (callActivity.isInheritVariables()) {
Map<String, Object> executionVariables = parentExecution.getVariables();
for (Map.Entry<String, Object> entry : executionVariables.entrySet()) {
variables.put(entry.getKey(), entry.getValue());
}
}
// copy process variables
for (IOParameter ioParameter : callActivity.getInParameters()) {
Object value;
if (StringUtils.isNotEmpty(ioParameter.getSourceExpression())) {
Expression expression = expressionManager.createExpression(ioParameter.getSourceExpression().trim());
value = expression.getValue(parentExecution);
} else {
value = parentExecution.getVariable(ioParameter.getSource());
}
variables.put(ioParameter.getTarget(), value);
}
if (!variables.isEmpty()) {
subProcessInstance.setVariables(variables);
}
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_INITIALIZED, subProcessInstance));
}
return subProcessInstance;
}
protected ExecutionEntity migrateExecutionEntity(ExecutionEntity parentExecutionEntity, ExecutionEntity childExecution, FlowElement newFlowElement, CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
// manage the bidirectional parent-child relation
childExecution.setProcessInstanceId(parentExecutionEntity.getProcessInstanceId());
childExecution.setProcessInstance(parentExecutionEntity.getProcessInstance());
childExecution.setProcessDefinitionId(parentExecutionEntity.getProcessDefinitionId());
ExecutionEntity oldParent = childExecution.getParent();
if (oldParent != null && !oldParent.getId().equals(parentExecutionEntity.getId())) {
oldParent.getExecutions().remove(childExecution);
}
childExecution.setParent(parentExecutionEntity);
parentExecutionEntity.addChildExecution(childExecution);
//Additional changes if the new activity Id doesn't match
String oldActivityId = childExecution.getCurrentActivityId();
if (!childExecution.getCurrentActivityId().equals(newFlowElement.getId())) {
ExecutionEntityImpl childExecution1 = (ExecutionEntityImpl) childExecution;
childExecution1.setCurrentFlowElement(newFlowElement);
}
// If we are moving a UserTask we need to update its processDefinition references
if (newFlowElement instanceof UserTask) {
TaskEntityImpl task = (TaskEntityImpl) taskService.createTaskQuery()
.executionId(childExecution.getId()).singleResult();
task.setProcessDefinitionId(childExecution.getProcessDefinitionId());
task.setTaskDefinitionKey(newFlowElement.getId());
task.setName(newFlowElement.getName());
task.setProcessInstanceId(childExecution.getProcessInstanceId());
//Sync history
syncUserTaskExecutionActivityInstance(childExecution,oldActivityId,newFlowElement);
updateActivity( processEngineConfiguration,childExecution, oldActivityId, newFlowElement, task, new Date());
}
// Boundary Events - only applies to Activities and up to this point we have a UserTask or ReceiveTask execution, both are Activities
List<BoundaryEvent> boundaryEvents = ((Activity) newFlowElement).getBoundaryEvents();
if (boundaryEvents != null && !boundaryEvents.isEmpty()) {
List<ExecutionEntity> boundaryEventsExecutions = createBoundaryEvents(boundaryEvents, childExecution, commandContext);
executeBoundaryEvents(boundaryEvents, boundaryEventsExecutions);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Child execution {} updated with parent {}", childExecution, parentExecutionEntity.getId());
}
return childExecution;
}
public void updateActivity(ProcessEngineConfigurationImpl processEngineConfiguration,ExecutionEntity childExecution, String oldActivityId, FlowElement newFlowElement, TaskEntity task, Date updateTime) {
HistoryManager historyManager = processEngineConfiguration.getHistoryManager();
if (historyManager.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
HistoricActivityInstanceEntityManager historicActivityInstanceEntityManager = CommandContextUtil.getHistoricActivityInstanceEntityManager();
List<HistoricActivityInstanceEntity> historicActivityInstances = historicActivityInstanceEntityManager.findUnfinishedHistoricActivityInstancesByExecutionAndActivityId(childExecution.getId(), oldActivityId);
for (HistoricActivityInstanceEntity historicActivityInstance : historicActivityInstances) {
historicActivityInstance.setProcessDefinitionId(childExecution.getProcessDefinitionId());
historicActivityInstance.setActivityId(childExecution.getActivityId());
historicActivityInstance.setActivityName(newFlowElement.getName());
}
}
if (historyManager.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
historyManager.recordTaskAssigneeChange(task.getId(),task.getAssignee());
}
}
protected void syncUserTaskExecutionActivityInstance(ExecutionEntity childExecution, String oldActivityId,
FlowElement newFlowElement) {
HistoricActivityInstanceEntityManager activityInstanceEntityManager = CommandContextUtil.getActivityInstanceEntityManager();
List<HistoricActivityInstanceEntity> activityInstances = activityInstanceEntityManager.findUnfinishedHistoricActivityInstancesByExecutionAndActivityId(childExecution.getId(), oldActivityId);
for (HistoricActivityInstanceEntity activityInstance : activityInstances) {
activityInstance.setProcessDefinitionId(childExecution.getProcessDefinitionId());
activityInstance.setActivityId(childExecution.getActivityId());
activityInstance.setActivityName(newFlowElement.getName());
}
}
protected void handleUserTaskNewAssignee(ExecutionEntity taskExecution, String newAssigneeId, CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
TaskEntityImpl task = (TaskEntityImpl) taskService.createTaskQuery()
.executionId(taskExecution.getId()).singleResult();
if (task != null) {
taskService.setAssignee(task.getId(), newAssigneeId);
}
}
protected boolean isEventSubProcessStart(FlowElement flowElement) {
return flowElement instanceof StartEvent && flowElement.getSubProcess() != null && flowElement.getSubProcess() instanceof EventSubProcess;
}
protected List<ExecutionEntity> createBoundaryEvents(List<BoundaryEvent> boundaryEvents, ExecutionEntity execution, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
List<ExecutionEntity> boundaryEventExecutions = new ArrayList<>(boundaryEvents.size());
// The parent execution becomes a scope, and a child execution is created for each of the boundary events
for (BoundaryEvent boundaryEvent : boundaryEvents) {
if (CollectionUtil.isEmpty(boundaryEvent.getEventDefinitions())
|| (boundaryEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition)) {
continue;
}
// A Child execution of the current execution is created to represent the boundary event being active
ExecutionEntity childExecutionEntity = executionEntityManager.createChildExecution(execution);
childExecutionEntity.setParentId(execution.getId());
childExecutionEntity.setCurrentFlowElement(boundaryEvent);
childExecutionEntity.setScope(false);
boundaryEventExecutions.add(childExecutionEntity);
}
return boundaryEventExecutions;
}
protected void executeBoundaryEvents(List<BoundaryEvent> boundaryEvents, List<ExecutionEntity> boundaryEventExecutions) {
if (!CollectionUtil.isEmpty(boundaryEventExecutions)) {
Iterator<BoundaryEvent> boundaryEventsIterator = boundaryEvents.iterator();
Iterator<ExecutionEntity> boundaryEventExecutionsIterator = boundaryEventExecutions.iterator();
while (boundaryEventsIterator.hasNext() && boundaryEventExecutionsIterator.hasNext()) {
BoundaryEvent boundaryEvent = boundaryEventsIterator.next();
ExecutionEntity boundaryEventExecution = boundaryEventExecutionsIterator.next();
ActivityBehavior boundaryEventBehavior = ((ActivityBehavior) boundaryEvent.getBehavior());
LOGGER.debug("Executing boundary event activityBehavior {} with execution {}", boundaryEventBehavior.getClass(), boundaryEventExecution.getId());
boundaryEventBehavior.execute(boundaryEventExecution);
}
}
}
protected boolean isExecutionInsideMultiInstance(ExecutionEntity execution) {
return getFlowElementMultiInstanceParentId(execution.getCurrentFlowElement()).isPresent();
}
protected Optional<String> getFlowElementMultiInstanceParentId(FlowElement flowElement) {
FlowElementsContainer parentContainer = flowElement.getParentContainer();
while (parentContainer instanceof Activity) {
if (isFlowElementMultiInstance((Activity) parentContainer)) {
return Optional.of(((Activity) parentContainer).getId());
}
parentContainer = ((Activity) parentContainer).getParentContainer();
}
return Optional.empty();
}
protected boolean isFlowElementMultiInstance(FlowElement flowElement) {
if (flowElement instanceof Activity) {
return ((Activity) flowElement).getLoopCharacteristics() != null;
}
return false;
}
protected void processCreatedEventSubProcess(EventSubProcess eventSubProcess, ExecutionEntity eventSubProcessExecution, Set<String> movingExecutionIds, CommandContext commandContext) {
// @todo如果后续使用到 EventSubProcess,则需要拓展
}
protected boolean isOnlyRemainingExecutionAtParentScope(ExecutionEntity executionEntity, Set<String> ignoreExecutionIds, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
List<ExecutionEntity> siblingExecutions = executionEntityManager.findChildExecutionsByParentExecutionId(executionEntity.getParentId());
return siblingExecutions.stream()
.filter(ExecutionEntity::isActive)
.filter(execution -> !execution.getId().equals(executionEntity.getId()))
.filter(execution -> !ignoreExecutionIds.contains(execution.getId()))
.count() == 0;
}
protected boolean isExpression(String variableName) {
return variableName.startsWith("${") || variableName.startsWith("#{");
}
protected ProcessDefinition resolveProcessDefinition(String processDefinitionKey, Integer processDefinitionVersion, String tenantId, CommandContext commandContext) {
ProcessDefinitionEntityManager processDefinitionEntityManager = CommandContextUtil.getProcessDefinitionEntityManager(commandContext);
ProcessDefinition processDefinition;
if (processDefinitionVersion != null) {
processDefinition = processDefinitionEntityManager.findProcessDefinitionByKeyAndVersionAndTenantId(processDefinitionKey, processDefinitionVersion, tenantId);
} else {
if (tenantId == null || ProcessEngineConfiguration.NO_TENANT_ID.equals(tenantId)) {
processDefinition = processDefinitionEntityManager.findLatestProcessDefinitionByKey(processDefinitionKey);
} else {
processDefinition = processDefinitionEntityManager.findLatestProcessDefinitionByKeyAndTenantId(processDefinitionKey, tenantId);
}
}
if (processDefinition == null) {
DeploymentManager deploymentManager = CommandContextUtil.getProcessEngineConfiguration(commandContext).getDeploymentManager();
if (tenantId == null || ProcessEngineConfiguration.NO_TENANT_ID.equals(tenantId)) {
processDefinition = deploymentManager.findDeployedLatestProcessDefinitionByKey(processDefinitionKey);
} else {
processDefinition = deploymentManager.findDeployedLatestProcessDefinitionByKeyAndTenantId(processDefinitionKey, tenantId);
}
}
return processDefinition;
}
private String printFlowElementIds(Collection<FlowElementMoveEntry> flowElements) {
return flowElements.stream().map(FlowElementMoveEntry::getNewFlowElement).map(FlowElement::getId).collect(Collectors.joining(","));
}
}
CommandContextUtil
import org.activiti.engine.ActivitiEngineAgenda;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.event.ActivitiEventDispatcher;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.db.DbSqlSession;
import org.activiti.engine.impl.history.HistoryManager;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.jobexecutor.FailedJobCommandFactory;
import org.activiti.engine.impl.persistence.cache.EntityCache;
import org.activiti.engine.impl.persistence.entity.*;
import org.activiti.engine.impl.util.ProcessInstanceHelper;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhoulin.zhu
*/
public class CommandContextUtil {
public static final String ATTRIBUTE_INVOLVED_EXECUTIONS = "ctx.attribute.involvedExecutions";
public static ProcessEngineConfigurationImpl getProcessEngineConfiguration() {
return getProcessEngineConfiguration(getCommandContext());
}
public static ProcessEngineConfigurationImpl getProcessEngineConfiguration(CommandContext commandContext) {
if (commandContext != null) {
return (ProcessEngineConfigurationImpl) commandContext.getProcessEngineConfiguration().getProcessEngineConfiguration();
}
return null;
}
public static TaskService getTaskService() {
return getTaskService(getCommandContext());
}
public static TaskService getTaskService(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getTaskService();
}
public static ActivitiEngineAgenda getAgenda() {
return getAgenda(getCommandContext());
}
public static ActivitiEngineAgenda getAgenda(CommandContext commandContext) {
return commandContext.getAgenda();
//return commandContext.getSession(ActivitiEngineAgenda.class);
}
public static DbSqlSession getDbSqlSession() {
return getDbSqlSession(getCommandContext());
}
public static DbSqlSession getDbSqlSession(CommandContext commandContext) {
return commandContext.getSession(DbSqlSession.class);
}
public static EntityCache getEntityCache() {
return getEntityCache(getCommandContext());
}
public static EntityCache getEntityCache(CommandContext commandContext) {
return commandContext.getSession(EntityCache.class);
}
@SuppressWarnings("unchecked")
public static void addInvolvedExecution(CommandContext commandContext, ExecutionEntity executionEntity) {
if (executionEntity.getId() != null) {
Map<String, ExecutionEntity> involvedExecutions = null;
Object obj = commandContext.getAttribute(ATTRIBUTE_INVOLVED_EXECUTIONS);
if (obj != null) {
involvedExecutions = (Map<String, ExecutionEntity>) obj;
} else {
involvedExecutions = new HashMap<>();
commandContext.addAttribute(ATTRIBUTE_INVOLVED_EXECUTIONS, involvedExecutions);
}
involvedExecutions.put(executionEntity.getId(), executionEntity);
}
}
@SuppressWarnings("unchecked")
public static Map<String, ExecutionEntity> getInvolvedExecutions(CommandContext commandContext) {
Object obj = commandContext.getAttribute(ATTRIBUTE_INVOLVED_EXECUTIONS);
if (obj != null) {
return (Map<String, ExecutionEntity>) obj;
}
return null;
}
public static boolean hasInvolvedExecutions(CommandContext commandContext) {
return getInvolvedExecutions(commandContext) != null;
}
public static TableDataManager getTableDataManager() {
return getTableDataManager(getCommandContext());
}
public static TableDataManager getTableDataManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getTableDataManager();
}
public static ByteArrayEntityManager getByteArrayEntityManager() {
return getByteArrayEntityManager(getCommandContext());
}
public static ByteArrayEntityManager getByteArrayEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getByteArrayEntityManager();
}
public static ResourceEntityManager getResourceEntityManager() {
return getResourceEntityManager(getCommandContext());
}
public static ResourceEntityManager getResourceEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getResourceEntityManager();
}
public static DeploymentEntityManager getDeploymentEntityManager() {
return getDeploymentEntityManager(getCommandContext());
}
public static DeploymentEntityManager getDeploymentEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getDeploymentEntityManager();
}
public static PropertyEntityManager getPropertyEntityManager() {
return getPropertyEntityManager(getCommandContext());
}
public static PropertyEntityManager getPropertyEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getPropertyEntityManager();
}
public static ProcessDefinitionEntityManager getProcessDefinitionEntityManager() {
return getProcessDefinitionEntityManager(getCommandContext());
}
public static ProcessDefinitionEntityManager getProcessDefinitionEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getProcessDefinitionEntityManager();
}
public static ProcessDefinitionInfoEntityManager getProcessDefinitionInfoEntityManager() {
return getProcessDefinitionInfoEntityManager(getCommandContext());
}
public static ProcessDefinitionInfoEntityManager getProcessDefinitionInfoEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getProcessDefinitionInfoEntityManager();
}
public static ExecutionEntityManager getExecutionEntityManager() {
return getExecutionEntityManager(getCommandContext());
}
public static ExecutionEntityManager getExecutionEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getExecutionEntityManager();
}
public static CommentEntityManager getCommentEntityManager() {
return getCommentEntityManager(getCommandContext());
}
public static CommentEntityManager getCommentEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getCommentEntityManager();
}
public static ModelEntityManager getModelEntityManager() {
return getModelEntityManager(getCommandContext());
}
public static ModelEntityManager getModelEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getModelEntityManager();
}
public static HistoryManager getHistoryManager() {
return getHistoryManager(getCommandContext());
}
public static HistoricProcessInstanceEntityManager getHistoricProcessInstanceEntityManager() {
return getHistoricProcessInstanceEntityManager(getCommandContext());
}
public static HistoricProcessInstanceEntityManager getHistoricProcessInstanceEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getHistoricProcessInstanceEntityManager();
}
public static HistoricActivityInstanceEntityManager getActivityInstanceEntityManager() {
return getActivityInstanceEntityManager(getCommandContext());
}
public static HistoricActivityInstanceEntityManager getActivityInstanceEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getHistoricActivityInstanceEntityManager();
}
public static HistoricActivityInstanceEntityManager getHistoricActivityInstanceEntityManager() {
return getHistoricActivityInstanceEntityManager(getCommandContext());
}
public static HistoricActivityInstanceEntityManager getHistoricActivityInstanceEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getHistoricActivityInstanceEntityManager();
}
public static HistoryManager getHistoryManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getHistoryManager();
}
public static HistoricDetailEntityManager getHistoricDetailEntityManager() {
return getHistoricDetailEntityManager(getCommandContext());
}
public static HistoricDetailEntityManager getHistoricDetailEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getHistoricDetailEntityManager();
}
public static AttachmentEntityManager getAttachmentEntityManager() {
return getAttachmentEntityManager(getCommandContext());
}
public static AttachmentEntityManager getAttachmentEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getAttachmentEntityManager();
}
public static EventLogEntryEntityManager getEventLogEntryEntityManager() {
return getEventLogEntryEntityManager(getCommandContext());
}
public static EventLogEntryEntityManager getEventLogEntryEntityManager(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getEventLogEntryEntityManager();
}
public static ActivitiEventDispatcher getEventDispatcher() {
return getEventDispatcher(getCommandContext());
}
public static ActivitiEventDispatcher getEventDispatcher(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getEventDispatcher();
}
public static FailedJobCommandFactory getFailedJobCommandFactory() {
return getFailedJobCommandFactory(getCommandContext());
}
public static FailedJobCommandFactory getFailedJobCommandFactory(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getFailedJobCommandFactory();
}
public static ProcessInstanceHelper getProcessInstanceHelper() {
return getProcessInstanceHelper(getCommandContext());
}
public static ProcessInstanceHelper getProcessInstanceHelper(CommandContext commandContext) {
return getProcessEngineConfiguration(commandContext).getProcessInstanceHelper();
}
public static CommandContext getCommandContext() {
return Context.getCommandContext();
}
}
ChangeActivityStateBuilder
import java.util.List;
import java.util.Map;
/**
* @author zhoulin.zhu
*/
public interface ChangeActivityStateBuilder {
/**
* Set the id of the process instance
**/
ChangeActivityStateBuilder processInstanceId(String processInstanceId);
/**
* Set the id of the execution for which the activity should be changed
**/
ChangeActivityStateBuilder moveExecutionToActivityId(String executionId, String activityId);
/**
* Set the ids of the executions which should be changed to a single execution with the provided activity id.
* This can be used for parallel execution like parallel/inclusive gateways, multiinstance, event sub processes etc.
**/
ChangeActivityStateBuilder moveExecutionsToSingleActivityId(List<String> executionIds, String activityId);
/**
* Set the id of an execution which should be changed to multiple executions with the provided activity ids.
* This can be used for parallel execution like parallel/inclusive gateways, multiinstance, event sub processes etc.
**/
ChangeActivityStateBuilder moveSingleExecutionToActivityIds(String executionId, List<String> activityId);
/**
* Moves the execution with the current activity id to the provided new activity id
*/
ChangeActivityStateBuilder moveActivityIdTo(String currentActivityId, String newActivityId);
/**
* Set the activity ids that should be changed to a single activity id.
* This can be used for parallel execution like parallel/inclusive gateways, multiinstance, event sub processes etc.
*/
ChangeActivityStateBuilder moveActivityIdsToSingleActivityId(List<String> currentActivityIds, String newActivityId);
/**
* Set the activity id that should be changed to multiple activity ids.
* This can be used for parallel execution like parallel/inclusive gateways, multiinstance, event sub processes etc.
*/
ChangeActivityStateBuilder moveSingleActivityIdToActivityIds(String currentActivityId, List<String> newActivityIds);
/**
* Moves the execution with the current activity id to an activity id in the parent process instance.
* The sub process instance will be terminated, so all sub process instance executions need to be moved.
*/
ChangeActivityStateBuilder moveActivityIdToParentActivityId(String currentActivityId, String newActivityId);
/**
* Moves the execution with the current activity id to an activity id in a new sub process instance for the provided call activity id.
*/
ChangeActivityStateBuilder moveActivityIdToSubProcessInstanceActivityId(String currentActivityId, String newActivityId, String callActivityId);
/**
* Moves the execution with the current activity id to an activity id in a new sub process instance of the specific definition version for the provided call activity id.
*/
ChangeActivityStateBuilder moveActivityIdToSubProcessInstanceActivityId(String currentActivityId, String newActivityId, String callActivityId, Integer subProcessDefinitionVersion);
/**
* Sets a process scope variable
*/
ChangeActivityStateBuilder processVariable(String processVariableName, Object processVariableValue);
/**
* Sets multiple process scope variables
*/
ChangeActivityStateBuilder processVariables(Map<String, Object> processVariables);
/**
* Sets a local scope variable for a start activity id
*/
ChangeActivityStateBuilder localVariable(String startActivityId, String localVariableName, Object localVariableValue);
/**
* Sets multiple local scope variables for a start activity id
*/
ChangeActivityStateBuilder localVariables(String startActivityId, Map<String, Object> localVariables);
/**
* Start the process instance
*
* @throws org.activiti.engine.ActivitiObjectNotFoundException
* when no process instance is found
* @throws org.activiti.engine.ActivitiException
* activity could not be canceled or started
**/
void changeState();
}
ChangeActivityStateBuilderImpl
import org.activiti.engine.ActivitiException;
import org.activiti.engine.impl.RuntimeServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author zhoulin.zhu
*/
public class ChangeActivityStateBuilderImpl implements ChangeActivityStateBuilder {
protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
protected RuntimeServiceImpl runtimeService;
protected String processInstanceId;
protected List<MoveExecutionIdContainer> moveExecutionIdList = new ArrayList<>();
protected List<MoveActivityIdContainer> moveActivityIdList = new ArrayList<>();
protected Map<String, Object> processVariables = new HashMap<>();
protected Map<String, Map<String, Object>> localVariables = new HashMap<>();
public ChangeActivityStateBuilderImpl() {
}
public ChangeActivityStateBuilderImpl(RuntimeServiceImpl runtimeService) {
this.runtimeService = runtimeService;
}
@Override
public ChangeActivityStateBuilder processInstanceId(String processInstanceId) {
this.processInstanceId = processInstanceId;
return this;
}
@Override
public ChangeActivityStateBuilder moveExecutionToActivityId(String executionId, String activityId) {
return moveExecutionToActivityId(executionId, activityId, null);
}
public ChangeActivityStateBuilder moveExecutionToActivityId(String executionId, String activityId, String newAssigneeId) {
moveExecutionIdList.add(new MoveExecutionIdContainer(executionId, activityId, newAssigneeId));
return this;
}
@Override
public ChangeActivityStateBuilder moveExecutionsToSingleActivityId(List<String> executionIds, String activityId) {
return moveExecutionsToSingleActivityId(executionIds, activityId, null);
}
public ChangeActivityStateBuilder moveExecutionsToSingleActivityId(List<String> executionIds, String activityId, String newAssigneeId) {
moveExecutionIdList.add(new MoveExecutionIdContainer(executionIds, activityId, newAssigneeId));
return this;
}
@Override
public ChangeActivityStateBuilder moveSingleExecutionToActivityIds(String executionId, List<String> activityIds) {
return moveSingleExecutionToActivityIds(executionId, activityIds, null);
}
public ChangeActivityStateBuilder moveSingleExecutionToActivityIds(String executionId, List<String> activityIds, String newAssigneeId) {
moveExecutionIdList.add(new MoveExecutionIdContainer(executionId, activityIds, newAssigneeId));
return this;
}
@Override
public ChangeActivityStateBuilder moveActivityIdTo(String currentActivityId, String newActivityId) {
return moveActivityIdTo(currentActivityId, newActivityId, null);
}
public ChangeActivityStateBuilder moveActivityIdTo(String currentActivityId, String newActivityId, String newAssigneeId) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Change activity processInstanceId:[{}] from: [{}] to: [{}] assigneeId: [{}], start", processInstanceId, currentActivityId, newActivityId, newAssigneeId);
}
moveActivityIdList.add(new MoveActivityIdContainer(currentActivityId, newActivityId, newAssigneeId));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Change activity processInstanceId:[{}] from: [{}] to: [{}] assigneeId: [{}], end", processInstanceId, currentActivityId, newActivityId, newAssigneeId);
}
return this;
}
@Override
public ChangeActivityStateBuilder moveActivityIdsToSingleActivityId(List<String> activityIds, String activityId) {
return moveActivityIdsToSingleActivityId(activityIds, activityId, null);
}
public ChangeActivityStateBuilder moveActivityIdsToSingleActivityId(List<String> activityIds, String activityId, String newAssigneeId) {
moveActivityIdList.add(new MoveActivityIdContainer(activityIds, activityId, newAssigneeId));
return this;
}
@Override
public ChangeActivityStateBuilder moveSingleActivityIdToActivityIds(String currentActivityId, List<String> newActivityIds) {
return moveSingleActivityIdToActivityIds(currentActivityId, newActivityIds, null);
}
public ChangeActivityStateBuilder moveSingleActivityIdToActivityIds(String currentActivityId, List<String> newActivityIds, String newAssigneeId) {
moveActivityIdList.add(new MoveActivityIdContainer(currentActivityId, newActivityIds, newAssigneeId));
return this;
}
@Override
public ChangeActivityStateBuilder moveActivityIdToParentActivityId(String currentActivityId, String newActivityId) {
return moveActivityIdToParentActivityId(currentActivityId, newActivityId, null);
}
public ChangeActivityStateBuilder moveActivityIdToParentActivityId(String currentActivityId, String newActivityId, String newAssigneeId) {
MoveActivityIdContainer moveActivityIdContainer = new MoveActivityIdContainer(currentActivityId, newActivityId, newAssigneeId);
moveActivityIdContainer.setMoveToParentProcess(true);
moveActivityIdList.add(moveActivityIdContainer);
return this;
}
public ChangeActivityStateBuilder moveActivityIdsToParentActivityId(List<String> currentActivityIds, String newActivityId, String newAssigneeId) {
MoveActivityIdContainer moveActivityIdContainer = new MoveActivityIdContainer(currentActivityIds, newActivityId, newAssigneeId);
moveActivityIdContainer.setMoveToParentProcess(true);
moveActivityIdList.add(moveActivityIdContainer);
return this;
}
public ChangeActivityStateBuilder moveSingleActivityIdToParentActivityIds(String currentActivityId, List<String> newActivityIds) {
MoveActivityIdContainer moveActivityIdContainer = new MoveActivityIdContainer(currentActivityId, newActivityIds);
moveActivityIdContainer.setMoveToParentProcess(true);
moveActivityIdList.add(moveActivityIdContainer);
return this;
}
@Override
public ChangeActivityStateBuilder moveActivityIdToSubProcessInstanceActivityId(String currentActivityId, String newActivityId, String callActivityId) {
return moveActivityIdToSubProcessInstanceActivityId(currentActivityId, newActivityId, callActivityId, null, null);
}
@Override
public ChangeActivityStateBuilder moveActivityIdToSubProcessInstanceActivityId(String currentActivityId, String newActivityId, String callActivityId, Integer subProcessDefinitionVersion) {
return moveActivityIdToSubProcessInstanceActivityId(currentActivityId, newActivityId, callActivityId, subProcessDefinitionVersion, null);
}
public ChangeActivityStateBuilder moveActivityIdToSubProcessInstanceActivityId(String currentActivityId, String newActivityId, String callActivityId, Integer callActivitySubProcessVersion, String newAssigneeId) {
MoveActivityIdContainer moveActivityIdContainer = new MoveActivityIdContainer(currentActivityId, newActivityId, newAssigneeId);
moveActivityIdContainer.setMoveToSubProcessInstance(true);
moveActivityIdContainer.setCallActivityId(callActivityId);
moveActivityIdContainer.setCallActivitySubProcessVersion(callActivitySubProcessVersion);
moveActivityIdList.add(moveActivityIdContainer);
return this;
}
public ChangeActivityStateBuilder moveActivityIdsToSubProcessInstanceActivityId(List<String> activityIds, String newActivityId, String callActivityId, Integer callActivitySubProcessVersion, String newAssigneeId) {
MoveActivityIdContainer moveActivityIdsContainer = new MoveActivityIdContainer(activityIds, newActivityId, newAssigneeId);
moveActivityIdsContainer.setMoveToSubProcessInstance(true);
moveActivityIdsContainer.setCallActivityId(callActivityId);
moveActivityIdsContainer.setCallActivitySubProcessVersion(callActivitySubProcessVersion);
moveActivityIdList.add(moveActivityIdsContainer);
return this;
}
public ChangeActivityStateBuilder moveSingleActivityIdToSubProcessInstanceActivityIds(String currentActivityId, List<String> newActivityIds, String callActivityId, Integer callActivitySubProcessVersion) {
MoveActivityIdContainer moveActivityIdsContainer = new MoveActivityIdContainer(currentActivityId, newActivityIds);
moveActivityIdsContainer.setMoveToSubProcessInstance(true);
moveActivityIdsContainer.setCallActivityId(callActivityId);
moveActivityIdsContainer.setCallActivitySubProcessVersion(callActivitySubProcessVersion);
moveActivityIdList.add(moveActivityIdsContainer);
return this;
}
@Override
public ChangeActivityStateBuilder processVariable(String processVariableName, Object processVariableValue) {
if (this.processVariables == null) {
this.processVariables = new HashMap<>();
}
this.processVariables.put(processVariableName, processVariableValue);
return this;
}
@Override
public ChangeActivityStateBuilder processVariables(Map<String, Object> processVariables) {
this.processVariables = processVariables;
return this;
}
@Override
public ChangeActivityStateBuilder localVariable(String startActivityId, String localVariableName, Object localVariableValue) {
if (this.localVariables == null) {
this.localVariables = new HashMap<>();
}
Map<String, Object> localVariableMap = null;
if (localVariables.containsKey(startActivityId)) {
localVariableMap = localVariables.get(startActivityId);
} else {
localVariableMap = new HashMap<>();
}
localVariableMap.put(localVariableName, localVariableValue);
this.localVariables.put(startActivityId, localVariableMap);
return this;
}
@Override
public ChangeActivityStateBuilder localVariables(String startActivityId, Map<String, Object> localVariables) {
if (this.localVariables == null) {
this.localVariables = new HashMap<>();
}
this.localVariables.put(startActivityId, localVariables);
return this;
}
@Override
public void changeState() {
if (runtimeService == null) {
throw new ActivitiException("RuntimeService cannot be null, Obtain your builder instance from the RuntimeService to access this feature");
}
changeActivityState(this);
}
public void changeActivityState(ChangeActivityStateBuilderImpl changeActivityStateBuilder) {
runtimeService.getCommandExecutor().execute(new ChangeActivityStateCmd(changeActivityStateBuilder));
}
public String getProcessInstanceId() {
return processInstanceId;
}
public List<MoveExecutionIdContainer> getMoveExecutionIdList() {
return moveExecutionIdList;
}
public List<MoveActivityIdContainer> getMoveActivityIdList() {
return moveActivityIdList;
}
public Map<String, Object> getProcessInstanceVariables() {
return processVariables;
}
public Map<String, Map<String, Object>> getLocalVariables() {
return localVariables;
}
}
ChangeActivityStateCmd
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
/**
* @author zhoulin.zhu
*/
public class ChangeActivityStateCmd implements Command<Void> {
protected ChangeActivityStateBuilderImpl changeActivityStateBuilder;
public ChangeActivityStateCmd(ChangeActivityStateBuilderImpl changeActivityStateBuilder) {
this.changeActivityStateBuilder = changeActivityStateBuilder;
}
@Override
public Void execute(CommandContext commandContext) {
if (changeActivityStateBuilder.getMoveExecutionIdList().size() == 0 && changeActivityStateBuilder.getMoveActivityIdList().size() == 0) {
throw new ActivitiIllegalArgumentException("No move execution or activity ids provided");
} else if (changeActivityStateBuilder.getMoveActivityIdList().size() > 0 && changeActivityStateBuilder.getProcessInstanceId() == null) {
throw new ActivitiIllegalArgumentException("Process instance id is required");
}
DynamicStateManager dynamicStateManager = new DefaultDynamicStateManager();
dynamicStateManager.moveExecutionState(changeActivityStateBuilder, commandContext);
return null;
}
}
DefaultDynamicStateManager
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.SubProcess;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* @author zhoulin.zhu
*/
public class DefaultDynamicStateManager extends AbstractDynamicStateManager implements DynamicStateManager {
@Override
public void moveExecutionState(ChangeActivityStateBuilderImpl changeActivityStateBuilder, CommandContext commandContext) {
List<MoveExecutionEntityContainer> moveExecutionEntityContainerList = resolveMoveExecutionEntityContainers(changeActivityStateBuilder, Optional.empty(), changeActivityStateBuilder.getProcessInstanceVariables(), commandContext);
List<ExecutionEntity> executions = moveExecutionEntityContainerList.iterator().next().getExecutions();
String processInstanceId = executions.iterator().next().getProcessInstanceId();
ProcessInstanceChangeState processInstanceChangeState = new ProcessInstanceChangeState()
.setProcessInstanceId(processInstanceId)
.setMoveExecutionEntityContainers(moveExecutionEntityContainerList)
.setLocalVariables(changeActivityStateBuilder.getLocalVariables())
.setProcessInstanceVariables(changeActivityStateBuilder.getProcessInstanceVariables());
doMoveExecutionState(processInstanceChangeState, commandContext);
}
@Override
protected Map<String, List<ExecutionEntity>> resolveActiveEmbeddedSubProcesses(String processInstanceId, CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
List<ExecutionEntity> childExecutions = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
Map<String, List<ExecutionEntity>> activeSubProcessesByActivityId = childExecutions.stream()
.filter(ExecutionEntity::isActive)
.filter(executionEntity -> executionEntity.getCurrentFlowElement() instanceof SubProcess)
.collect(Collectors.groupingBy(ExecutionEntity::getActivityId));
return activeSubProcessesByActivityId;
}
@Override
protected boolean isDirectFlowElementExecutionMigration(FlowElement currentFlowElement, FlowElement newFlowElement) {
return false;
}
}
DynamicStateManager
import org.activiti.engine.impl.interceptor.CommandContext;
/**
* @author zhoulin.zhu
*/
public interface DynamicStateManager {
void moveExecutionState(ChangeActivityStateBuilderImpl changeActivityStateBuilder, CommandContext commandContext);
}
MoveActivityIdContainer
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* @author zhoulin.zhu
*/
public class MoveActivityIdContainer {
protected List<String> activityIds;
protected List<String> moveToActivityIds;
protected boolean moveToParentProcess;
protected boolean moveToSubProcessInstance;
protected String callActivityId;
protected Integer callActivitySubProcessVersion;
protected String newAssigneeId;
public MoveActivityIdContainer(String singleActivityId, String moveToActivityId) {
this(singleActivityId, moveToActivityId, null);
}
public MoveActivityIdContainer(String singleActivityId, String moveToActivityId, String newAssigneeId) {
this.activityIds = Collections.singletonList(singleActivityId);
this.moveToActivityIds = Collections.singletonList(moveToActivityId);
this.newAssigneeId = newAssigneeId;
}
public MoveActivityIdContainer(List<String> activityIds, String moveToActivityId) {
this(activityIds, moveToActivityId, null);
}
public MoveActivityIdContainer(List<String> activityIds, String moveToActivityId, String newAssigneeId) {
this.activityIds = activityIds;
this.moveToActivityIds = Collections.singletonList(moveToActivityId);
this.newAssigneeId = newAssigneeId;
}
public MoveActivityIdContainer(String singleActivityId, List<String> moveToActivityIds) {
this(singleActivityId, moveToActivityIds, null);
}
public MoveActivityIdContainer(String singleActivityId, List<String> moveToActivityIds, String newAssigneeId) {
this.activityIds = Collections.singletonList(singleActivityId);
this.moveToActivityIds = moveToActivityIds;
this.newAssigneeId = newAssigneeId;
}
public List<String> getActivityIds() {
return Optional.ofNullable(activityIds).orElse(Collections.emptyList());
}
public List<String> getMoveToActivityIds() {
return Optional.ofNullable(moveToActivityIds).orElse(Collections.emptyList());
}
public boolean isMoveToParentProcess() {
return moveToParentProcess;
}
public void setMoveToParentProcess(boolean moveToParentProcess) {
this.moveToParentProcess = moveToParentProcess;
}
public boolean isMoveToSubProcessInstance() {
return moveToSubProcessInstance;
}
public void setMoveToSubProcessInstance(boolean moveToSubProcessInstance) {
this.moveToSubProcessInstance = moveToSubProcessInstance;
}
public String getCallActivityId() {
return callActivityId;
}
public void setCallActivityId(String callActivityId) {
this.callActivityId = callActivityId;
}
public Integer getCallActivitySubProcessVersion() {
return callActivitySubProcessVersion;
}
public void setCallActivitySubProcessVersion(Integer callActivitySubProcessVersion) {
this.callActivitySubProcessVersion = callActivitySubProcessVersion;
}
public Optional<String> getNewAssigneeId() {
return Optional.ofNullable(newAssigneeId);
}
}
MoveExecutionEntityContainer
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.CallActivity;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import java.util.*;
/**
* @author zhoulin.zhu
*/
public class MoveExecutionEntityContainer {
protected List<ExecutionEntity> executions;
protected List<String> moveToActivityIds;
protected boolean moveToParentProcess;
protected boolean moveToSubProcessInstance;
protected boolean directExecutionMigration;
protected String callActivityId;
protected Integer callActivitySubProcessVersion;
protected CallActivity callActivity;
protected String subProcessDefKey;
protected ProcessDefinition subProcessDefinition;
protected BpmnModel subProcessModel;
protected BpmnModel processModel;
protected ExecutionEntity superExecution;
protected String newAssigneeId;
protected Map<String, ExecutionEntity> continueParentExecutionMap = new HashMap<>();
protected Map<String, FlowElementMoveEntry> moveToFlowElementMap = new LinkedHashMap<>();
protected List<String> newExecutionIds = new ArrayList<>();
public MoveExecutionEntityContainer(List<ExecutionEntity> executions, List<String> moveToActivityIds) {
this.executions = executions;
this.moveToActivityIds = moveToActivityIds;
}
public List<ExecutionEntity> getExecutions() {
return executions;
}
public List<String> getMoveToActivityIds() {
return moveToActivityIds;
}
public boolean isMoveToParentProcess() {
return moveToParentProcess;
}
public void setMoveToParentProcess(boolean moveToParentProcess) {
this.moveToParentProcess = moveToParentProcess;
}
public boolean isMoveToSubProcessInstance() {
return moveToSubProcessInstance;
}
public void setMoveToSubProcessInstance(boolean moveToSubProcessInstance) {
this.moveToSubProcessInstance = moveToSubProcessInstance;
}
public boolean isDirectExecutionMigration() {
return directExecutionMigration;
}
public void setDirectExecutionMigration(boolean directMigrateUserTask) {
this.directExecutionMigration = directMigrateUserTask;
}
public String getCallActivityId() {
return callActivityId;
}
public void setCallActivityId(String callActivityId) {
this.callActivityId = callActivityId;
}
public Integer getCallActivitySubProcessVersion() {
return callActivitySubProcessVersion;
}
public void setCallActivitySubProcessVersion(Integer callActivitySubProcessVersion) {
this.callActivitySubProcessVersion = callActivitySubProcessVersion;
}
public CallActivity getCallActivity() {
return callActivity;
}
public void setCallActivity(CallActivity callActivity) {
this.callActivity = callActivity;
}
public String getSubProcessDefKey() {
return subProcessDefKey;
}
public void setSubProcessDefKey(String subProcessDefKey) {
this.subProcessDefKey = subProcessDefKey;
}
public ProcessDefinition getSubProcessDefinition() {
return subProcessDefinition;
}
public void setSubProcessDefinition(ProcessDefinition subProcessDefinition) {
this.subProcessDefinition = subProcessDefinition;
}
public BpmnModel getProcessModel() {
return processModel;
}
public void setProcessModel(BpmnModel processModel) {
this.processModel = processModel;
}
public BpmnModel getSubProcessModel() {
return subProcessModel;
}
public void setSubProcessModel(BpmnModel subProcessModel) {
this.subProcessModel = subProcessModel;
}
public ExecutionEntity getSuperExecution() {
return superExecution;
}
public void setNewAssigneeId(String newAssigneeId) {
this.newAssigneeId = newAssigneeId;
}
public String getNewAssigneeId() {
return newAssigneeId;
}
public void setSuperExecution(ExecutionEntity superExecution) {
this.superExecution = superExecution;
}
public void addContinueParentExecution(String executionId, ExecutionEntity continueParentExecution) {
continueParentExecutionMap.put(executionId, continueParentExecution);
}
public ExecutionEntity getContinueParentExecution(String executionId) {
return continueParentExecutionMap.get(executionId);
}
public void addMoveToFlowElement(String activityId, FlowElementMoveEntry flowElementMoveEntry) {
moveToFlowElementMap.put(activityId, flowElementMoveEntry);
}
public void addMoveToFlowElement(String activityId, FlowElement originalFlowElement, FlowElement newFlowElement) {
moveToFlowElementMap.put(activityId, new FlowElementMoveEntry(originalFlowElement, newFlowElement));
}
public void addMoveToFlowElement(String activityId, FlowElement originalFlowElement) {
moveToFlowElementMap.put(activityId, new FlowElementMoveEntry(originalFlowElement, originalFlowElement));
}
public FlowElementMoveEntry getMoveToFlowElement(String activityId) {
return moveToFlowElementMap.get(activityId);
}
public List<FlowElementMoveEntry> getMoveToFlowElements() {
return new ArrayList<>(moveToFlowElementMap.values());
}
public List<String> getNewExecutionIds() {
return newExecutionIds;
}
public boolean hasNewExecutionId(String executionId) {
return newExecutionIds.contains(executionId);
}
public void setNewExecutionIds(List<String> newExecutionIds) {
this.newExecutionIds = newExecutionIds;
}
public void addNewExecutionId(String executionId) {
this.newExecutionIds.add(executionId);
}
public static class FlowElementMoveEntry {
protected FlowElement originalFlowElement;
protected FlowElement newFlowElement;
public FlowElementMoveEntry(FlowElement originalFlowElement, FlowElement newFlowElement) {
this.originalFlowElement = originalFlowElement;
this.newFlowElement = newFlowElement;
}
public FlowElement getOriginalFlowElement() {
return originalFlowElement;
}
public FlowElement getNewFlowElement() {
return newFlowElement;
}
}
}
MoveExecutionIdContainer
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* @author zhoulin.zhu
*/
public class MoveExecutionIdContainer {
protected List<String> executionIds;
protected List<String> moveToActivityIds;
protected Optional<String> newAssigneeId;
public MoveExecutionIdContainer(String singleExecutionId, String moveToActivityId) {
this(singleExecutionId, moveToActivityId, null);
}
public MoveExecutionIdContainer(String singleExecutionId, String moveToActivityId, String newAssigneeId) {
this.executionIds = Collections.singletonList(singleExecutionId);
this.moveToActivityIds = Collections.singletonList(moveToActivityId);
this.newAssigneeId = Optional.ofNullable(newAssigneeId);
}
public MoveExecutionIdContainer(List<String> executionIds, String moveToActivityId) {
this(executionIds, moveToActivityId, null);
}
public MoveExecutionIdContainer(List<String> executionIds, String moveToActivityId, String newAssigneeId) {
this.executionIds = executionIds;
this.moveToActivityIds = Collections.singletonList(moveToActivityId);
this.newAssigneeId = Optional.ofNullable(newAssigneeId);
}
public MoveExecutionIdContainer(String singleExecutionId, List<String> moveToActivityIds) {
this(singleExecutionId, moveToActivityIds, null);
}
public MoveExecutionIdContainer(String singleExecutionId, List<String> moveToActivityIds, String newAssigneeId) {
this.executionIds = Collections.singletonList(singleExecutionId);
this.moveToActivityIds = moveToActivityIds;
this.newAssigneeId = Optional.ofNullable(newAssigneeId);
}
public List<String> getExecutionIds() {
return Optional.ofNullable(executionIds).orElse(Collections.emptyList());
}
public List<String> getMoveToActivityIds() {
return Optional.ofNullable(moveToActivityIds).orElse(Collections.emptyList());
}
public Optional<String> getNewAssigneeId() {
return newAssigneeId;
}
}
ProcessInstanceChangeState
import org.activiti.bpmn.model.StartEvent;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* @author zhoulin.zhu
*/
public class ProcessInstanceChangeState {
protected String processInstanceId;
protected ProcessDefinition processDefinitionToMigrateTo;
protected Map<String, Object> processVariables = new HashMap<>();
protected Map<String, Map<String, Object>> localVariables = new HashMap<>();
protected Map<String, List<ExecutionEntity>> processInstanceActiveEmbeddedExecutions;
protected List<MoveExecutionEntityContainer> moveExecutionEntityContainers;
protected HashMap<String, ExecutionEntity> createdEmbeddedSubProcess = new HashMap<>();
protected HashMap<StartEvent, ExecutionEntity> pendingEventSubProcessesStartEvents = new HashMap<>();
public ProcessInstanceChangeState() {
}
public String getProcessInstanceId() {
return processInstanceId;
}
public ProcessInstanceChangeState setProcessInstanceId(String processInstanceId) {
this.processInstanceId = processInstanceId;
return this;
}
public Optional<ProcessDefinition> getProcessDefinitionToMigrateTo() {
return Optional.ofNullable(processDefinitionToMigrateTo);
}
public ProcessInstanceChangeState setProcessDefinitionToMigrateTo(ProcessDefinition processDefinitionToMigrateTo) {
this.processDefinitionToMigrateTo = processDefinitionToMigrateTo;
return this;
}
public boolean isMigrateToProcessDefinition() {
return getProcessDefinitionToMigrateTo().isPresent();
}
public Map<String, Object> getProcessInstanceVariables() {
return processVariables;
}
public ProcessInstanceChangeState setProcessInstanceVariables(Map<String, Object> processVariables) {
this.processVariables = processVariables;
return this;
}
public Map<String, Map<String, Object>> getLocalVariables() {
return localVariables;
}
public ProcessInstanceChangeState setLocalVariables(Map<String, Map<String, Object>> localVariables) {
this.localVariables = localVariables;
return this;
}
public List<MoveExecutionEntityContainer> getMoveExecutionEntityContainers() {
return moveExecutionEntityContainers;
}
public ProcessInstanceChangeState setMoveExecutionEntityContainers(List<MoveExecutionEntityContainer> moveExecutionEntityContainers) {
this.moveExecutionEntityContainers = moveExecutionEntityContainers;
return this;
}
public HashMap<String, ExecutionEntity> getCreatedEmbeddedSubProcesses() {
return createdEmbeddedSubProcess;
}
public Optional<ExecutionEntity> getCreatedEmbeddedSubProcessByKey(String key) {
return Optional.ofNullable(createdEmbeddedSubProcess.get(key));
}
public void addCreatedEmbeddedSubProcess(String key, ExecutionEntity executionEntity) {
this.createdEmbeddedSubProcess.put(key, executionEntity);
}
public Map<String, List<ExecutionEntity>> getProcessInstanceActiveEmbeddedExecutions() {
return processInstanceActiveEmbeddedExecutions;
}
public ProcessInstanceChangeState setProcessInstanceActiveEmbeddedExecutions(Map<String, List<ExecutionEntity>> processInstanceActiveEmbeddedExecutions) {
this.processInstanceActiveEmbeddedExecutions = processInstanceActiveEmbeddedExecutions;
return this;
}
public HashMap<StartEvent, ExecutionEntity> getPendingEventSubProcessesStartEvents() {
return pendingEventSubProcessesStartEvents;
}
public void addPendingEventSubProcessStartEvent(StartEvent startEvent, ExecutionEntity eventSubProcessParent) {
this.pendingEventSubProcessesStartEvents.put(startEvent, eventSubProcessParent);
}
}
activiti7中 单元测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@Slf4j
public class ChangeActivityTest {
@Autowired
private RuntimeService runtimeService;
@Test
public void testChanegActivity(){
String pid = "f6dd4449-e5d6-11eb-82bc-0242b27c9aea";
String currentActivityId = "task_zce00nhw";
String newActivityId = "task_9vm24ce2";
new ChangeActivityStateBuilderImpl((RuntimeServiceImpl)runtimeService).processInstanceId(pid).moveActivityIdTo(currentActivityId,newActivityId).changeState();
System.out.printf("");
}
}