All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.camunda.bpm.engine.impl.cmd.GetActivityInstanceCmd Maven / Gradle / Ivy

There is a newer version: 7.23.0-alpha1
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.camunda.bpm.engine.impl.cmd;

import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.impl.cfg.CommandChecker;
import org.camunda.bpm.engine.impl.interceptor.Command;
import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.persistence.entity.ActivityInstanceImpl;
import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity;
import org.camunda.bpm.engine.impl.persistence.entity.IncidentEntity;
import org.camunda.bpm.engine.impl.persistence.entity.TransitionInstanceImpl;
import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
import org.camunda.bpm.engine.impl.pvm.process.ScopeImpl;
import org.camunda.bpm.engine.impl.pvm.runtime.CompensationBehavior;
import org.camunda.bpm.engine.impl.pvm.runtime.LegacyBehavior;
import org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl;
import org.camunda.bpm.engine.impl.util.CollectionUtil;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.runtime.Incident;

/**
 * 

Creates an activity instance tree according to the following strategy: * *

    *
  • Event scope executions are not considered at all *
  • For every leaf execution, generate an activity/transition instance; * the activity instance id is set in the leaf execution and the parent instance id is set in the parent execution *
  • For every non-leaf scope execution, generate an activity instance; * the activity instance id is always set in the parent execution and the parent activity * instance id is always set in the parent's parent (because of tree compactation, we ensure * that an activity instance id for a scope activity is always stored in the corresponding scope execution's parent, * unless the execution is a leaf) *
  • Compensation is an exception to the above procedure: A compensation throw event is not a scope, however the compensating executions * are added as child executions of the (probably non-scope) execution executing the throw event. Logically, the compensating executions * are children of the scope execution the throwing event is executed in. Due to this oddity, the activity instance id are stored on different * executions *
* * @author Thorben Lindhauer * */ public class GetActivityInstanceCmd implements Command { protected String processInstanceId; public GetActivityInstanceCmd(String processInstanceId) { this.processInstanceId = processInstanceId; } public ActivityInstance execute(CommandContext commandContext) { ensureNotNull("processInstanceId", processInstanceId); List executionList = loadProcessInstance(processInstanceId, commandContext); if (executionList.isEmpty()) { return null; } checkGetActivityInstance(processInstanceId, commandContext); List nonEventScopeExecutions = filterNonEventScopeExecutions(executionList); List leaves = filterLeaves(nonEventScopeExecutions); // Leaves must be ordered in a predictable way (e.g. by ID) // in order to return a stable execution tree with every repeated invocation of this command. // For legacy process instances, there may miss scope executions for activities that are now a scope. // In this situation, there may be multiple scope candidates for the same instance id; which one // can depend on the order the leaves are iterated. orderById(leaves); ExecutionEntity processInstance = filterProcessInstance(executionList); if (processInstance.isEnded()) { return null; } Map> incidents = groupIncidentIdsByExecutionId(commandContext); // create act instance for process instance ActivityInstanceImpl processActInst = createActivityInstance( processInstance, processInstance.getProcessDefinition(), processInstanceId, null, incidents); Map activityInstances = new HashMap(); activityInstances.put(processInstanceId, processActInst); Map transitionInstances = new HashMap(); for (ExecutionEntity leaf : leaves) { // skip leafs without activity, e.g. if only the process instance exists after cancellation // it will not have an activity set if (leaf.getActivity() == null) { continue; } Map activityExecutionMapping = leaf.createActivityExecutionMapping(); Map scopeInstancesToCreate = new HashMap(activityExecutionMapping); // create an activity/transition instance for each leaf that executes a non-scope activity // and does not throw compensation if (leaf.getActivityInstanceId() != null) { if (!CompensationBehavior.isCompensationThrowing(leaf) || LegacyBehavior.isCompensationThrowing(leaf, activityExecutionMapping)) { String parentActivityInstanceId = null; parentActivityInstanceId = activityExecutionMapping .get(leaf.getActivity().getFlowScope()) .getParentActivityInstanceId(); ActivityInstanceImpl leafInstance = createActivityInstance(leaf, leaf.getActivity(), leaf.getActivityInstanceId(), parentActivityInstanceId, incidents); activityInstances.put(leafInstance.getId(), leafInstance); scopeInstancesToCreate.remove(leaf.getActivity()); } } else { TransitionInstanceImpl transitionInstance = createTransitionInstance(leaf, incidents); transitionInstances.put(transitionInstance.getId(), transitionInstance); scopeInstancesToCreate.remove(leaf.getActivity()); } LegacyBehavior.removeLegacyNonScopesFromMapping(scopeInstancesToCreate); scopeInstancesToCreate.remove(leaf.getProcessDefinition()); // create an activity instance for each scope (including compensation throwing executions) for (Map.Entry scopeExecutionEntry : scopeInstancesToCreate.entrySet()) { ScopeImpl scope = scopeExecutionEntry.getKey(); PvmExecutionImpl scopeExecution = scopeExecutionEntry.getValue(); String activityInstanceId = null; String parentActivityInstanceId = null; activityInstanceId = scopeExecution.getParentActivityInstanceId(); parentActivityInstanceId = activityExecutionMapping .get(scope.getFlowScope()) .getParentActivityInstanceId(); if (activityInstances.containsKey(activityInstanceId)) { continue; } else { // regardless of the tree structure (compacted or not), the scope's activity instance id // is the activity instance id of the parent execution and the parent activity instance id // of that is the actual parent activity instance id ActivityInstanceImpl scopeInstance = createActivityInstance( scopeExecution, scope, activityInstanceId, parentActivityInstanceId, incidents); activityInstances.put(activityInstanceId, scopeInstance); } } } LegacyBehavior.repairParentRelationships(activityInstances.values(), processInstanceId); populateChildInstances(activityInstances, transitionInstances); return processActInst; } protected void checkGetActivityInstance(String processInstanceId, CommandContext commandContext) { for(CommandChecker checker : commandContext.getProcessEngineConfiguration().getCommandCheckers()) { checker.checkReadProcessInstance(processInstanceId); } } protected void orderById(List leaves) { Collections.sort(leaves, ExecutionIdComparator.INSTANCE); } protected ActivityInstanceImpl createActivityInstance(PvmExecutionImpl scopeExecution, ScopeImpl scope, String activityInstanceId, String parentActivityInstanceId, Map> incidentsByExecution) { ActivityInstanceImpl actInst = new ActivityInstanceImpl(); actInst.setId(activityInstanceId); actInst.setParentActivityInstanceId(parentActivityInstanceId); actInst.setProcessInstanceId(scopeExecution.getProcessInstanceId()); actInst.setProcessDefinitionId(scopeExecution.getProcessDefinitionId()); actInst.setBusinessKey(scopeExecution.getBusinessKey()); actInst.setActivityId(scope.getId()); String name = scope.getName(); if (name == null) { name = (String) scope.getProperty("name"); } actInst.setActivityName(name); if (scope.getId().equals(scopeExecution.getProcessDefinition().getId())) { actInst.setActivityType("processDefinition"); } else { actInst.setActivityType((String) scope.getProperty("type")); } List executionIds = new ArrayList(); List incidentIds = new ArrayList<>(); List incidents = new ArrayList<>(); executionIds.add(scopeExecution.getId()); ActivityImpl executionActivity = scopeExecution.getActivity(); // do not collect incidents if scopeExecution is a compacted subtree // and we currently create the scope activity instance if (executionActivity == null || executionActivity == scope) { incidentIds.addAll(getIncidentIds(incidentsByExecution, scopeExecution)); incidents.addAll(getIncidents(incidentsByExecution, scopeExecution)); } for (PvmExecutionImpl childExecution : scopeExecution.getNonEventScopeExecutions()) { // add all concurrent children that are not in an activity if (childExecution.isConcurrent() && childExecution.getActivityId() == null) { executionIds.add(childExecution.getId()); incidentIds.addAll(getIncidentIds(incidentsByExecution, childExecution)); incidents.addAll(getIncidents(incidentsByExecution, childExecution)); } } actInst.setExecutionIds(executionIds.toArray(new String[executionIds.size()])); actInst.setIncidentIds(incidentIds.toArray(new String[incidentIds.size()])); actInst.setIncidents(incidents.toArray(new Incident[0])); return actInst; } protected TransitionInstanceImpl createTransitionInstance(PvmExecutionImpl execution, Map> incidentsByExecution) { TransitionInstanceImpl transitionInstance = new TransitionInstanceImpl(); // can use execution id as persistent ID for transition as an execution // can execute as most one transition at a time. transitionInstance.setId(execution.getId()); transitionInstance.setParentActivityInstanceId(execution.getParentActivityInstanceId()); transitionInstance.setProcessInstanceId(execution.getProcessInstanceId()); transitionInstance.setProcessDefinitionId(execution.getProcessDefinitionId()); transitionInstance.setExecutionId(execution.getId()); transitionInstance.setActivityId(execution.getActivityId()); ActivityImpl activity = execution.getActivity(); if (activity != null) { String name = activity.getName(); if (name == null) { name = (String) activity.getProperty("name"); } transitionInstance.setActivityName(name); transitionInstance.setActivityType((String) activity.getProperty("type")); } List incidentIdList = getIncidentIds(incidentsByExecution, execution); List incidents = getIncidents(incidentsByExecution, execution); transitionInstance.setIncidentIds(incidentIdList.toArray(new String[0])); transitionInstance.setIncidents(incidents.toArray(new Incident[0])); return transitionInstance; } protected void populateChildInstances(Map activityInstances, Map transitionInstances) { Map> childActivityInstances = new HashMap>(); Map> childTransitionInstances = new HashMap>(); for (ActivityInstanceImpl instance : activityInstances.values()) { if (instance.getParentActivityInstanceId() != null) { ActivityInstanceImpl parentInstance = activityInstances.get(instance.getParentActivityInstanceId()); if (parentInstance == null) { throw new ProcessEngineException("No parent activity instance with id " + instance.getParentActivityInstanceId() + " generated"); } putListElement(childActivityInstances, parentInstance, instance); } } for (TransitionInstanceImpl instance : transitionInstances.values()) { if (instance.getParentActivityInstanceId() != null) { ActivityInstanceImpl parentInstance = activityInstances.get(instance.getParentActivityInstanceId()); if (parentInstance == null) { throw new ProcessEngineException("No parent activity instance with id " + instance.getParentActivityInstanceId() + " generated"); } putListElement(childTransitionInstances, parentInstance, instance); } } for (Map.Entry> entry : childActivityInstances.entrySet()) { ActivityInstanceImpl instance = entry.getKey(); List childInstances = entry.getValue(); if (childInstances != null) { instance.setChildActivityInstances(childInstances.toArray(new ActivityInstanceImpl[childInstances.size()])); } } for (Map.Entry> entry : childTransitionInstances.entrySet()) { ActivityInstanceImpl instance = entry.getKey(); List childInstances = entry.getValue(); if (childTransitionInstances != null) { instance.setChildTransitionInstances(childInstances.toArray(new TransitionInstanceImpl[childInstances.size()])); } } } protected void putListElement(Map> mapOfLists, S key, T listElement) { List list = mapOfLists.get(key); if (list == null) { list = new ArrayList(); mapOfLists.put(key, list); } list.add(listElement); } protected ExecutionEntity filterProcessInstance(List executionList) { for (ExecutionEntity execution : executionList) { if (execution.isProcessInstanceExecution()) { return execution; } } throw new ProcessEngineException("Could not determine process instance execution"); } protected List filterLeaves(List executionList) { List leaves = new ArrayList(); for (ExecutionEntity execution : executionList) { // although executions executing throwing compensation events are not leaves in the tree, // they are treated as leaves since their child executions are logical children of their parent scope execution if (execution.getNonEventScopeExecutions().isEmpty() || CompensationBehavior.isCompensationThrowing(execution)) { leaves.add(execution); } } return leaves; } protected List filterNonEventScopeExecutions(List executionList) { List nonEventScopeExecutions = new ArrayList(); for (ExecutionEntity execution : executionList) { if (!execution.isEventScope()) { nonEventScopeExecutions.add(execution); } } return nonEventScopeExecutions; } protected List loadProcessInstance(String processInstanceId, CommandContext commandContext) { List result = null; // first try to load from cache // check whether the process instance is already (partially) loaded in command context List cachedExecutions = commandContext.getDbEntityManager().getCachedEntitiesByType(ExecutionEntity.class); for (ExecutionEntity executionEntity : cachedExecutions) { if(processInstanceId.equals(executionEntity.getProcessInstanceId())) { // found one execution from process instance result = new ArrayList(); ExecutionEntity processInstance = executionEntity.getProcessInstance(); // add process instance result.add(processInstance); loadChildExecutionsFromCache(processInstance, result); break; } } if(result == null) { // if the process instance could not be found in cache, load from database result = loadFromDb(processInstanceId, commandContext); } return result; } protected List loadFromDb(final String processInstanceId, final CommandContext commandContext) { List executions = commandContext.getExecutionManager().findExecutionsByProcessInstanceId(processInstanceId); ExecutionEntity processInstance = commandContext.getExecutionManager().findExecutionById(processInstanceId); // initialize parent/child sets if (processInstance != null) { processInstance.restoreProcessInstance(executions, null, null, null, null, null, null); } return executions; } /** * Loads all executions that are part of this process instance tree from the dbSqlSession cache. * (optionally querying the db if a child is not already loaded. * * @param execution the current root execution (already contained in childExecutions) * @param childExecutions the list in which all child executions should be collected */ protected void loadChildExecutionsFromCache(ExecutionEntity execution, List childExecutions) { List childrenOfThisExecution = execution.getExecutions(); if(childrenOfThisExecution != null) { childExecutions.addAll(childrenOfThisExecution); for (ExecutionEntity child : childrenOfThisExecution) { loadChildExecutionsFromCache(child, childExecutions); } } } protected Map> groupIncidentIdsByExecutionId(CommandContext commandContext) { List incidents = commandContext.getIncidentManager().findIncidentsByProcessInstance(processInstanceId); Map> result = new HashMap<>(); for (IncidentEntity incidentEntity : incidents) { CollectionUtil.addToMapOfLists(result, incidentEntity.getExecutionId(), incidentEntity); } return result; } protected List getIncidentIds(Map> incidents, PvmExecutionImpl execution) { List incidentIds = new ArrayList<>(); List incidentList = incidents.get(execution.getId()); if (incidentList != null) { for (Incident incident : incidentList) { incidentIds.add(incident.getId()); } return incidentIds; } else { return Collections.emptyList(); } } protected List getIncidents(Map> incidents, PvmExecutionImpl execution) { List incidentList = incidents.get(execution.getId()); if (incidentList != null) { return incidentList; } else { return Collections.emptyList(); } } public static class ExecutionIdComparator implements Comparator { public static final ExecutionIdComparator INSTANCE = new ExecutionIdComparator(); @Override public int compare(ExecutionEntity o1, ExecutionEntity o2) { return o1.getId().compareTo(o2.getId()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy