diff --git a/distro/src/notice.txt b/distro/src/notice.txt index 73a45701d33..4bcd443a5c8 100644 --- a/distro/src/notice.txt +++ b/distro/src/notice.txt @@ -104,9 +104,9 @@ org.liquibase liquibase-core 4.5.0 Apac org.mybatis mybatis 3.5.13 The Apache Software License, Version 2.0 org.mybatis mybatis-spring 3.0.3 The Apache Software License, Version 2.0 org.mvel mvel2 2.2.6.Final The Apache Software License, Version 2.0 -org.slf4j jcl-over-slf4j 2.0.11 MIT License -org.slf4j slf4j-api 2.0.11 MIT License -org.slf4j slf4j-log4j12 2.0.11 MIT License +org.slf4j jcl-over-slf4j 2.0.11 MIT License +org.slf4j slf4j-api 2.0.11 MIT License +org.slf4j slf4j-log4j12 2.0.11 MIT License org.springframework spring-beans 6.1.3 The Apache Software License, Version 2.0 org.springframework spring-core 6.1.3 The Apache Software License, Version 2.0 org.springframework spring-context 6.1.3 The Apache Software License, Version 2.0 @@ -127,3 +127,11 @@ org.tinyjee.jgraphx jgraphx 1.10.4.1 JGra org.yaml snakeyaml 1.17 The Apache Software License, Version 2.0 xerces xercesImpl 2.12.1 The Apache Software License, Version 2.0 xml-apis xml-apis 1.4.01 The Apache Software License, Version 2.0-The SAX License-The W3C License + +When using the Flowable Rest app, there are a number of additional libraries included, which are otherwise not a dependency of Flowable: + +org.eclipse.angus angus-mail 2.0.2 EPL 2.0, GPL with classpath exception +jakarta.annotation jakarta.annotation-api 2.1.1 EPL 2.0, GPL with classpath exception +jakarta.jms jakarta.jms-api 3.1.0 EPL 2.0, GPL with classpath exception +jakarta.mail jakarta.mail-api 2.1.2 EPL 2.0, GPL with classpath exception +org.openjdk.nashorn nashorn-core 15.4 GPL with classpath exception diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java index 90101f14c96..dd8b8e1145c 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java @@ -12,9 +12,27 @@ */ package org.flowable.cmmn.api.migration; +import java.util.LinkedHashMap; +import java.util.Map; + public class MoveToAvailablePlanItemDefinitionMapping extends PlanItemDefinitionMapping { + + protected Map withLocalVariables = new LinkedHashMap<>(); public MoveToAvailablePlanItemDefinitionMapping(String planItemDefinitionId) { super(planItemDefinitionId); } + + public MoveToAvailablePlanItemDefinitionMapping(String planItemDefinitionId, Map withLocalVariables) { + super(planItemDefinitionId); + this.withLocalVariables = withLocalVariables; + } + + public Map getWithLocalVariables() { + return withLocalVariables; + } + + public void setWithLocalVariables(Map withLocalVariables) { + this.withLocalVariables = withLocalVariables; + } } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java index 190a92c4d0b..0bc49afde71 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java @@ -38,6 +38,12 @@ public static MoveToAvailablePlanItemDefinitionMapping createMoveToAvailablePlan return new MoveToAvailablePlanItemDefinitionMapping(planItemDefinitionId); } + public static MoveToAvailablePlanItemDefinitionMapping createMoveToAvailablePlanItemDefinitionMappingFor( + String planItemDefinitionId, Map withLocalVariables) { + + return new MoveToAvailablePlanItemDefinitionMapping(planItemDefinitionId, withLocalVariables); + } + public static WaitingForRepetitionPlanItemDefinitionMapping createWaitingForRepetitionPlanItemDefinitionMappingFor(String planItemDefinitionId) { return new WaitingForRepetitionPlanItemDefinitionMapping(planItemDefinitionId); } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java index d361ef8791c..232960515d7 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java @@ -16,6 +16,7 @@ import java.util.Map; import org.flowable.cmmn.api.migration.ActivatePlanItemDefinitionMapping; +import org.flowable.cmmn.api.migration.MoveToAvailablePlanItemDefinitionMapping; import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableObjectNotFoundException; @@ -63,6 +64,11 @@ public interface ChangePlanItemStateBuilder { */ ChangePlanItemStateBuilder changeToAvailableStateByPlanItemDefinitionIds(List planItemDefinitionIds); + /** + * Set a plan item to available state by definition mapping. + */ + ChangePlanItemStateBuilder changeToAvailableStateByPlanItemDefinition(MoveToAvailablePlanItemDefinitionMapping planItemDefinitionMapping); + /** * Terminate a plan item by definition id without terminating another plan item instance. */ diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/CmmnEngineAgenda.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/CmmnEngineAgenda.java index a9f8debf953..5423f6eff63 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/CmmnEngineAgenda.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/CmmnEngineAgenda.java @@ -92,6 +92,8 @@ public interface CmmnEngineAgenda extends Agenda { void planEvaluateCriteriaOperation(String caseInstanceEntityId, PlanItemLifeCycleEvent lifeCycleEvent); + void planEvaluateCriteriaOperation(String caseInstanceEntityId, MigrationContext migrationContext); + void planEvaluateVariableEventListenersOperation(String caseInstanceEntityId); } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/DefaultCmmnEngineAgenda.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/DefaultCmmnEngineAgenda.java index ac9ce880e92..66aae05a5fd 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/DefaultCmmnEngineAgenda.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/DefaultCmmnEngineAgenda.java @@ -144,6 +144,14 @@ public void planEvaluateCriteriaOperation(String caseInstanceEntityId, PlanItemL internalPlanEvaluateCriteria(caseInstanceEntityId, lifeCycleEvent, false); } + @Override + public void planEvaluateCriteriaOperation(String caseInstanceEntityId, MigrationContext migrationContext) { + EvaluateCriteriaOperation evaluateCriteriaOperation = new EvaluateCriteriaOperation(commandContext, caseInstanceEntityId, null); + evaluateCriteriaOperation.setEvaluateStagesAndCaseInstanceCompletion(false); + evaluateCriteriaOperation.setMigrationContext(migrationContext); + addOperation(evaluateCriteriaOperation); + } + protected void internalPlanEvaluateCriteria(String caseInstanceEntityId, PlanItemLifeCycleEvent planItemLifeCycleEvent, boolean evaluateCaseInstanceCompleted) { EvaluateCriteriaOperation evaluateCriteriaOperation = new EvaluateCriteriaOperation(commandContext, caseInstanceEntityId, planItemLifeCycleEvent); evaluateCriteriaOperation.setEvaluateStagesAndCaseInstanceCompletion(evaluateCaseInstanceCompleted); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/AbstractEvaluationCriteriaOperation.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/AbstractEvaluationCriteriaOperation.java index 79eadcd96d2..152be2b31b2 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/AbstractEvaluationCriteriaOperation.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/AbstractEvaluationCriteriaOperation.java @@ -44,6 +44,7 @@ import org.flowable.cmmn.engine.impl.util.ExpressionUtil; import org.flowable.cmmn.engine.impl.util.PlanItemInstanceContainerUtil; import org.flowable.cmmn.engine.impl.util.PlanItemInstanceUtil; +import org.flowable.cmmn.engine.interceptor.MigrationContext; import org.flowable.cmmn.model.Criterion; import org.flowable.cmmn.model.EventListener; import org.flowable.cmmn.model.HasExitCriteria; @@ -107,6 +108,7 @@ public void evaluateForActivation(PlanItemInstanceEntity planItemInstanceEntity, // if we need to activate the plan item, mark the result as some criteria changed and plan the activation of the plan item by adding // this as an operation to the agenda if (activatePlanItemInstance) { + planItemInstanceEntity.setPlannedForActivationInMigration(true); evaluationResult.markCriteriaChanged(); CommandContextUtil.getAgenda(commandContext) .planActivatePlanItemInstanceOperation(planItemInstanceEntity, satisfiedEntryCriterion != null ? satisfiedEntryCriterion.getId() : null); @@ -144,7 +146,7 @@ public boolean evaluateForCompletion(PlanItemInstanceEntity planItemInstanceEnti } else if (planItem.getPlanItemDefinition() instanceof Stage) { if (PlanItemInstanceState.ACTIVE.equals(state)) { - boolean criteriaChangeOrActiveChildrenForStage = evaluatePlanItemsCriteria(planItemInstanceEntity); + boolean criteriaChangeOrActiveChildrenForStage = evaluatePlanItemsCriteria(planItemInstanceEntity, null); if (criteriaChangeOrActiveChildrenForStage) { evaluationResult.markCriteriaChanged(); planItemInstanceEntity.setCompletable(false); // an active child = stage cannot be completed anymore @@ -189,15 +191,21 @@ public boolean evaluateForCompletion(PlanItemInstanceEntity planItemInstanceEnti * Returns false if no sentry changes happened and none of the passed plan item instances are active. * This means that the parent of these plan item instances also now can change its state. */ - protected boolean evaluatePlanItemsCriteria(PlanItemInstanceContainer planItemInstanceContainer) { + protected boolean evaluatePlanItemsCriteria(PlanItemInstanceContainer planItemInstanceContainer, MigrationContext migrationContext) { List planItemInstances = planItemInstanceContainer.getChildPlanItemInstances(); // needed because when doing case instance migration the child plan item instances can be null - if (planItemInstances == null && planItemInstanceContainer instanceof CaseInstanceEntity) { + if ((planItemInstances == null || (migrationContext != null && migrationContext.isFetchPlanItemInstances())) && + planItemInstanceContainer instanceof CaseInstanceEntity) { + PlanItemInstanceEntityManager planItemInstanceEntityManager = CommandContextUtil.getPlanItemInstanceEntityManager(commandContext); CaseInstanceEntity caseInstance = (CaseInstanceEntity) planItemInstanceContainer; planItemInstances = planItemInstanceEntityManager.findByCaseInstanceId(caseInstance.getId()); planItemInstanceContainer.setChildPlanItemInstances(planItemInstances); + + if (migrationContext != null && migrationContext.isFetchPlanItemInstances()) { + migrationContext.setFetchPlanItemInstances(false); + } } // create an evaluation result object, holding all evaluation results as well as a list of newly created child plan items, as to avoid concurrent @@ -212,7 +220,7 @@ protected boolean evaluatePlanItemsCriteria(PlanItemInstanceContainer planItemIn String state = planItemInstanceEntity.getState(); // check, if the plan item is in an evaluation state (e.g. available or waiting for repetition) to check for its activation - if (PlanItemInstanceState.EVALUATE_ENTRY_CRITERIA_STATES.contains(state)) { + if (PlanItemInstanceState.EVALUATE_ENTRY_CRITERIA_STATES.contains(state) && !planItemInstanceEntity.isPlannedForActivationInMigration()) { evaluateForActivation(planItemInstanceEntity, planItemInstanceContainer, evaluationResult); } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateCriteriaOperation.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateCriteriaOperation.java index ec109c15779..5395af2efa4 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateCriteriaOperation.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateCriteriaOperation.java @@ -15,6 +15,7 @@ import org.flowable.cmmn.api.runtime.CaseInstanceState; import org.flowable.cmmn.engine.impl.criteria.PlanItemLifeCycleEvent; import org.flowable.cmmn.engine.impl.util.CommandContextUtil; +import org.flowable.cmmn.engine.interceptor.MigrationContext; import org.flowable.cmmn.model.Criterion; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.slf4j.Logger; @@ -27,6 +28,8 @@ public class EvaluateCriteriaOperation extends AbstractEvaluationCriteriaOperation { private static final Logger LOGGER = LoggerFactory.getLogger(EvaluateCriteriaOperation.class); + + protected MigrationContext migrationContext; public EvaluateCriteriaOperation(CommandContext commandContext, String caseInstanceEntityId) { super(commandContext, caseInstanceEntityId, null, null); @@ -53,7 +56,7 @@ public void run() { satisfiedExitCriterion.getExitType(), satisfiedExitCriterion.getExitEventType()); } else { - boolean criteriaChangeOrActiveChildren = evaluatePlanItemsCriteria(caseInstanceEntity); + boolean criteriaChangeOrActiveChildren = evaluatePlanItemsCriteria(caseInstanceEntity, migrationContext); if (evaluateStagesAndCaseInstanceCompletion && evaluatePlanModelComplete() && !criteriaChangeOrActiveChildren @@ -91,5 +94,12 @@ public String toString() { return stringBuilder.toString(); } - + + public MigrationContext getMigrationContext() { + return migrationContext; + } + + public void setMigrationContext(MigrationContext migrationContext) { + this.migrationContext = migrationContext; + } } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java index 2b1dde9173a..ec189f7ef67 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java @@ -371,7 +371,7 @@ protected ChangePlanItemStateBuilderImpl prepareChangeStateBuilder(CaseInstance } for (MoveToAvailablePlanItemDefinitionMapping planItemDefinitionMapping : document.getMoveToAvailablePlanItemDefinitionMappings()) { - changePlanItemStateBuilder.changeToAvailableStateByPlanItemDefinitionId(planItemDefinitionMapping.getPlanItemDefinitionId()); + changePlanItemStateBuilder.changeToAvailableStateByPlanItemDefinition(planItemDefinitionMapping); } for (WaitingForRepetitionPlanItemDefinitionMapping planItemDefinitionMapping : document.getWaitingForRepetitionPlanItemDefinitionMappings()) { diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntity.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntity.java index b4dc7a33c03..0ab87907f19 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntity.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntity.java @@ -26,4 +26,7 @@ public interface PlanItemInstanceEntity extends Entity, HasRevision, DelegatePla VariableScope getParentVariableScope(); + boolean isPlannedForActivationInMigration(); + + void setPlannedForActivationInMigration(boolean plannedForActivationInMigration); } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntityImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntityImpl.java index c6cb0be4463..94df68d8934 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntityImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/PlanItemInstanceEntityImpl.java @@ -92,6 +92,7 @@ public class PlanItemInstanceEntityImpl extends AbstractCmmnEngineVariableScopeE protected PlanItemInstanceLifecycleListener currentLifecycleListener; // Only set when executing an plan item lifecycle listener protected FlowableListener currentFlowableListener; // Only set when executing an plan item lifecycle listener + protected boolean plannedForActivationInMigration; public PlanItemInstanceEntityImpl() { } @@ -646,6 +647,16 @@ public void setLocalizedName(String localizedName) { this.localizedName = localizedName; } + @Override + public boolean isPlannedForActivationInMigration() { + return plannedForActivationInMigration; + } + + @Override + public void setPlannedForActivationInMigration(boolean plannedForActivationInMigration) { + this.plannedForActivationInMigration = plannedForActivationInMigration; + } + @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java index 9ad975fb52e..338b632e2dc 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java @@ -147,7 +147,9 @@ protected void doMovePlanItemState(CaseInstanceChangeState caseInstanceChangeSta executeRemoveWaitingForRepetitionPlanItemInstances(caseInstanceChangeState, caseInstance, commandContext); CmmnEngineAgenda agenda = CommandContextUtil.getAgenda(commandContext); - agenda.planEvaluateCriteriaOperation(caseInstance.getId()); + MigrationContext migrationContext = new MigrationContext(); + migrationContext.setFetchPlanItemInstances(true); + agenda.planEvaluateCriteriaOperation(caseInstance.getId(), migrationContext); } protected void executeChangePlanItemIds(CaseInstanceChangeState caseInstanceChangeState, String originalCaseDefinitionId, CommandContext commandContext) { @@ -228,6 +230,10 @@ protected void executeActivatePlanItemInstances(CaseInstanceChangeState caseInst PlanItemInstanceEntity newPlanItemInstance = createStagesAndPlanItemInstances(planItem, caseInstance, caseInstanceChangeState, commandContext); + + if (planItemDefinitionMapping.getWithLocalVariables() != null && !planItemDefinitionMapping.getWithLocalVariables().isEmpty()) { + newPlanItemInstance.setVariablesLocal(planItemDefinitionMapping.getWithLocalVariables()); + } CmmnEngineAgenda agenda = CommandContextUtil.getAgenda(commandContext); if (planItemDefinitionMapping.getNewAssignee() != null && planItem.getPlanItemDefinition() instanceof HumanTask) { @@ -316,6 +322,10 @@ protected void executeChangePlanItemInstancesToAvailableState(CaseInstanceChange caseInstanceChangeState.addCreatedStageInstance(planItemDefinitionMapping.getPlanItemDefinitionId(), availablePlanItemInstance); } + if (planItemDefinitionMapping.getWithLocalVariables() != null && !planItemDefinitionMapping.getWithLocalVariables().isEmpty()) { + availablePlanItemInstance.setVariablesLocal(planItemDefinitionMapping.getWithLocalVariables()); + } + CmmnHistoryManager cmmnHistoryManager = cmmnEngineConfiguration.getCmmnHistoryManager(); cmmnHistoryManager.recordPlanItemInstanceCreated(availablePlanItemInstance); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java index 71e4f04a9ec..72205d50e02 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java @@ -103,6 +103,12 @@ public ChangePlanItemStateBuilder changeToAvailableStateByPlanItemDefinitionIds( return this; } + @Override + public ChangePlanItemStateBuilder changeToAvailableStateByPlanItemDefinition(MoveToAvailablePlanItemDefinitionMapping planItemDefinitionMapping) { + changeToAvailableStatePlanItemDefinitions.add(planItemDefinitionMapping); + return this; + } + @Override public ChangePlanItemStateBuilder terminatePlanItemDefinitionId(String planItemDefinitionId) { terminatePlanItemDefinitions.add(new TerminatePlanItemDefinitionMapping(planItemDefinitionId)); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/interceptor/MigrationContext.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/interceptor/MigrationContext.java index babe86db66c..531701e69e9 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/interceptor/MigrationContext.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/interceptor/MigrationContext.java @@ -14,7 +14,16 @@ public class MigrationContext { + protected boolean fetchPlanItemInstances; protected String assignee; + + public boolean isFetchPlanItemInstances() { + return fetchPlanItemInstances; + } + + public void setFetchPlanItemInstances(boolean fetchPlanItemInstances) { + this.fetchPlanItemInstances = fetchPlanItemInstances; + } public String getAssignee() { return assignee; diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java index a7a4c39ff06..dc258466912 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java @@ -404,18 +404,18 @@ void withTwoTasksIntroducingANewStageAroundSecondTask() { assertThat(planItem2.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); assertThat(planItem2.getName()).isEqualTo("Task 2"); assertThat(planItem2.getStageInstanceId()).isNotNull(); - assertThat(planItem2.getState()).isEqualTo(PlanItemInstanceState.AVAILABLE); + assertThat(planItem2.getState()).isEqualTo(PlanItemInstanceState.ACTIVE); PlanItemInstance planItem3 = planItemsByElementId.get("planItem3").get(0); assertThat(planItem3.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); assertThat(planItem3.getPlanItemDefinitionId()).isEqualTo("expandedStage1"); assertThat(planItem3.getState()).isEqualTo(PlanItemInstanceState.AVAILABLE); - Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult(); + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("humanTask1").singleResult(); assertThat(task.getTaskDefinitionKey()).isEqualTo("humanTask1"); assertThat(task.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); cmmnTaskService.complete(task.getId()); - task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult(); + task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("humanTask2").singleResult(); assertThat(task.getTaskDefinitionKey()).isEqualTo("humanTask2"); assertThat(task.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); cmmnTaskService.complete(task.getId()); @@ -4395,10 +4395,261 @@ void activatePlanItemWithCompletedStage() { assertThat(historicPlanItemInstances.size()).isEqualTo(1); assertThat(historicPlanItemInstances.get(0).getState()).isEqualTo("completed"); } + + @Test + void migrateCaseInstancesWithLocalVariablesForStage() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-local-variables.cmmn.xml"); + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-local-variables-extra-task.cmmn.xml"); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + + Map localStageVarMap = new HashMap<>(); + localStageVarMap.put("stageNr", 1); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("extra-task")) + .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("stage", null, localStageVarMap)) + .migrateCaseInstances(caseInstance.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstanceAfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .singleResult(); + assertThat(caseInstanceAfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances).hasSize(4); + assertThat(planItemInstances) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Start Task", "Stage", "Stage Task 1", "Extra Task"); + assertThat(planItemInstances) + .filteredOn("name", "Start Task") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances) + .filteredOn("name", "Stage") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances) + .filteredOn("name", "Stage Task 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances) + .filteredOn("name", "Extra Task") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(3); + + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("stage-task").singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(2); + + task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("task").singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + + task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("extra-task").singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnRuntimeService.createCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isZero(); + + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).singleResult().getCaseDefinitionId()) + .isEqualTo(destinationDefinition.getId()); + + List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery() + .planItemInstanceCaseInstanceId(caseInstance.getId()).list(); + assertThat(historicPlanItemInstances).hasSize(4); + for (HistoricPlanItemInstance historicPlanItemInstance : historicPlanItemInstances) { + assertThat(historicPlanItemInstance.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + } + + List historicTasks = cmmnHistoryService.createHistoricTaskInstanceQuery().caseInstanceId(caseInstance.getId()).list(); + assertThat(historicTasks).hasSize(3); + for (HistoricTaskInstance historicTask : historicTasks) { + assertThat(historicTask.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + } + } + } + + @Test + void migrateCaseInstancesWithLocalVariablesForRepeatingStage() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-repetition-local-variables.cmmn.xml"); + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-repetition-local-variables-extra-task.cmmn.xml"); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(2); + + Map localStageVarMap = new HashMap<>(); + localStageVarMap.put("stageNr", 1); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("extra-task")) + .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("repeating-stage", null, localStageVarMap)) + .migrateCaseInstances(caseInstance.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstanceAfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .singleResult(); + assertThat(caseInstanceAfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances).hasSize(6); + assertThat(planItemInstances) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Exit Task", "Start Repeating Task", "Repeating Stage", "Repeating Stage", "Stage repeating Task 1", "Extra Task"); + assertThat(planItemInstances) + .filteredOn("name", "Start Repeating Task") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances) + .filteredOn("name", "Repeating Stage") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE, PlanItemInstanceState.WAITING_FOR_REPETITION); + + assertThat(planItemInstances) + .filteredOn("name", "Stage repeating Task 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances) + .filteredOn("name", "Extra Task") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(4); + + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("stage-repeating-task").singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(3); + + task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("exit-task").singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnRuntimeService.createCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isZero(); + + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).singleResult().getCaseDefinitionId()) + .isEqualTo(destinationDefinition.getId()); + + List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery() + .planItemInstanceCaseInstanceId(caseInstance.getId()).list(); + assertThat(historicPlanItemInstances).hasSize(6); + for (HistoricPlanItemInstance historicPlanItemInstance : historicPlanItemInstances) { + assertThat(historicPlanItemInstance.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + } + + List historicTasks = cmmnHistoryService.createHistoricTaskInstanceQuery().caseInstanceId(caseInstance.getId()).list(); + assertThat(historicTasks).hasSize(4); + for (HistoricTaskInstance historicTask : historicTasks) { + assertThat(historicTask.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + } + } + } + + @Test + void migrateCaseInstancesWithLocalVariablesForAvailableStage() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/task-local-variables.cmmn.xml"); + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-local-variables.cmmn.xml"); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + + Map localStageVarMap = new HashMap<>(); + localStageVarMap.put("stageNr", 1); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addMoveToAvailablePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createMoveToAvailablePlanItemDefinitionMappingFor("stage", localStageVarMap)) + .migrateCaseInstances(caseInstance.getCaseDefinitionId()); - // with sentries - // with stages - // with new expected case variables + // Assert + CaseInstance caseInstanceAfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .singleResult(); + assertThat(caseInstanceAfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances).hasSize(2); + assertThat(planItemInstances) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Start Task", "Stage"); + assertThat(planItemInstances) + .filteredOn("name", "Start Task") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances) + .filteredOn("name", "Stage") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.AVAILABLE); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("task").singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + + task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("stage-task").singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnRuntimeService.createCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isZero(); + + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).singleResult().getCaseDefinitionId()) + .isEqualTo(destinationDefinition.getId()); + + List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery() + .planItemInstanceCaseInstanceId(caseInstance.getId()).list(); + assertThat(historicPlanItemInstances).hasSize(3); + for (HistoricPlanItemInstance historicPlanItemInstance : historicPlanItemInstances) { + assertThat(historicPlanItemInstance.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + } + + List historicTasks = cmmnHistoryService.createHistoricTaskInstanceQuery().caseInstanceId(caseInstance.getId()).list(); + assertThat(historicTasks).hasSize(2); + for (HistoricTaskInstance historicTask : historicTasks) { + assertThat(historicTask.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + } + } + } protected class CustomTenantProvider implements DefaultTenantProvider { diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-local-variables-extra-task.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-local-variables-extra-task.cmmn.xml new file mode 100644 index 00000000000..3394cee14ae --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-local-variables-extra-task.cmmn.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + complete + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-local-variables.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-local-variables.cmmn.xml new file mode 100644 index 00000000000..518ea058f8e --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-local-variables.cmmn.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + complete + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-repetition-local-variables-extra-task.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-repetition-local-variables-extra-task.cmmn.xml new file mode 100644 index 00000000000..f026e045bc3 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-repetition-local-variables-extra-task.cmmn.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + complete + + + + + complete + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-repetition-local-variables.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-repetition-local-variables.cmmn.xml new file mode 100644 index 00000000000..03187f0f013 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-repetition-local-variables.cmmn.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + complete + + + + + complete + + + + + + + + + + + + complete + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/task-local-variables.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/task-local-variables.cmmn.xml new file mode 100644 index 00000000000..cb40e1d86f6 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/task-local-variables.cmmn.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/history/BaseHistoricDecisionExecutionResource.java b/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/history/BaseHistoricDecisionExecutionResource.java index e6a53797bf2..97680b56100 100644 --- a/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/history/BaseHistoricDecisionExecutionResource.java +++ b/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/history/BaseHistoricDecisionExecutionResource.java @@ -51,13 +51,14 @@ protected DmnHistoricDecisionExecution getHistoricDecisionExecutionFromRequest(S DmnHistoricDecisionExecution decisionExecution = historicDecisionExecutionQuery.singleResult(); + if (decisionExecution == null) { + throw new FlowableObjectNotFoundException("Could not find a decision execution with id '" + decisionExecutionId + "'"); + } + if (restApiInterceptor != null) { restApiInterceptor.accessDecisionHistoryInfoById(decisionExecution); } - if (decisionExecution == null) { - throw new FlowableObjectNotFoundException("Could not find a decision execution with id '" + decisionExecutionId + "'"); - } return decisionExecution; } diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryConfigurationSettings.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryConfigurationSettings.java index a6faa53ed47..2be47fd6c62 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryConfigurationSettings.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryConfigurationSettings.java @@ -19,7 +19,6 @@ import org.flowable.bpmn.model.Process; import org.flowable.common.engine.api.scope.ScopeTypes; import org.flowable.common.engine.impl.history.HistoryLevel; -import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.util.ProcessDefinitionUtil; @@ -284,8 +283,8 @@ public boolean isHistoryEnabledForVariableInstance(String processDefinitionId, V } @Override - public boolean isHistoryEnabledForVariables(HistoricProcessInstance historicProcessInstance) { - return processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.ACTIVITY); + public boolean isHistoryEnabledForVariables(String processDefinitionId) { + return isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, processDefinitionId); } @Override diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java index c6b6b9df43e..da18dedd7b2 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/DefaultHistoryManager.java @@ -129,8 +129,10 @@ public void recordProcessInstanceDeleted(String processInstanceId, String proces HistoricProcessInstanceEntity historicProcessInstance = getHistoricProcessInstanceEntityManager().findById(processInstanceId); getHistoricDetailEntityManager().deleteHistoricDetailsByProcessInstanceId(processInstanceId); - if (getHistoryConfigurationSettings().isHistoryEnabledForVariables(historicProcessInstance)) { - processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService().deleteHistoricVariableInstancesByProcessInstanceId(processInstanceId); + + if (getHistoryConfigurationSettings().isHistoryEnabledForVariables(processDefinitionId)) { + processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService() + .deleteHistoricVariableInstancesByProcessInstanceId(processInstanceId); } getHistoricActivityInstanceEntityManager().deleteHistoricActivityInstancesByProcessInstanceId(processInstanceId); TaskHelper.deleteHistoricTaskInstancesByProcessInstanceId(processInstanceId); diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/HistoryConfigurationSettings.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/HistoryConfigurationSettings.java index d8bf38015e8..b69e34053f0 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/HistoryConfigurationSettings.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/history/HistoryConfigurationSettings.java @@ -13,7 +13,6 @@ package org.flowable.engine.impl.history; import org.flowable.common.engine.impl.history.HistoryLevel; -import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.runtime.ActivityInstance; import org.flowable.entitylink.service.impl.persistence.entity.EntityLinkEntity; @@ -86,7 +85,7 @@ public interface HistoryConfigurationSettings { /** * Returns whether history is enabled for variables for the provided historic process instance. */ - boolean isHistoryEnabledForVariables(HistoricProcessInstance historicProcessInstance); + boolean isHistoryEnabledForVariables(String processDefinitionId); /** * Returns whether variable history is enabled for the provided historic task instance. diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/HistoricProcessInstanceEntityManagerImpl.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/HistoricProcessInstanceEntityManagerImpl.java index 41294cc0eb7..e5a1ad8a9ae 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/HistoricProcessInstanceEntityManagerImpl.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/HistoricProcessInstanceEntityManagerImpl.java @@ -14,7 +14,6 @@ package org.flowable.engine.impl.persistence.entity; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -43,28 +42,19 @@ public HistoricProcessInstanceEntity create(ExecutionEntity processInstanceExecu @Override public long findHistoricProcessInstanceCountByQueryCriteria(HistoricProcessInstanceQueryImpl historicProcessInstanceQuery) { - if (getHistoryManager().isHistoryEnabled()) { - return dataManager.findHistoricProcessInstanceCountByQueryCriteria(historicProcessInstanceQuery); - } - return 0; + return dataManager.findHistoricProcessInstanceCountByQueryCriteria(historicProcessInstanceQuery); } @Override @SuppressWarnings("unchecked") public List findHistoricProcessInstancesByQueryCriteria(HistoricProcessInstanceQueryImpl historicProcessInstanceQuery) { - if (getHistoryManager().isHistoryEnabled()) { - return dataManager.findHistoricProcessInstancesByQueryCriteria(historicProcessInstanceQuery); - } - return Collections.EMPTY_LIST; + return dataManager.findHistoricProcessInstancesByQueryCriteria(historicProcessInstanceQuery); } @Override @SuppressWarnings("unchecked") public List findHistoricProcessInstancesAndVariablesByQueryCriteria(HistoricProcessInstanceQueryImpl historicProcessInstanceQuery) { - if (getHistoryManager().isHistoryEnabled()) { - return dataManager.findHistoricProcessInstancesAndVariablesByQueryCriteria(historicProcessInstanceQuery); - } - return Collections.EMPTY_LIST; + return dataManager.findHistoricProcessInstancesAndVariablesByQueryCriteria(historicProcessInstanceQuery); } @Override diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/DeleteHistoricProcessInstanceTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/DeleteHistoricProcessInstanceTest.java new file mode 100644 index 00000000000..ba3f97c0f89 --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/DeleteHistoricProcessInstanceTest.java @@ -0,0 +1,188 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * 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.flowable.engine.test.history; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import java.util.List; + +import org.flowable.common.engine.impl.history.HistoryLevel; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.test.PluggableFlowableTestCase; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.test.Deployment; +import org.flowable.variable.api.history.HistoricVariableInstance; +import org.flowable.variable.api.persistence.entity.VariableInstance; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +/** + * @author Christopher Welsch + */ +public class DeleteHistoricProcessInstanceTest extends PluggableFlowableTestCase { + + protected HistoryLevel engineHistoryLevel; + + @BeforeEach + public void prepare() { + engineHistoryLevel = processEngineConfiguration.getHistoryLevel(); + } + + @AfterEach + public void cleanup() { + processEngineConfiguration.setHistoryLevel(engineHistoryLevel); + } + + @ParameterizedTest + @EnumSource + @Deployment(resources = { "org/flowable/engine/test/api/history/oneTaskHistoryLevelNoneProcess.bpmn20.xml" }) + public void testDeleteVariableInstancesWithHistoryLevelNone(HistoryLevel historyLevel) { + processEngineConfiguration.setHistoryLevel(historyLevel); + + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess") + .variable("testVariable", "testValue").start(); + + List variableInstances = runtimeService.createVariableInstanceQuery().processInstanceId(processInstance.getId()).list(); + assertThat(variableInstances).extracting(VariableInstance::getName, VariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + taskService.complete(taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()); + + List historicVariableInstances = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()).list(); + + assertThat(historicVariableInstances).isEmpty(); + assertThat(historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()).singleResult()).isNull(); + } + + @ParameterizedTest + @EnumSource + @Deployment(resources = { "org/flowable/engine/test/api/history/oneTaskHistoryLevelTaskProcess.bpmn20.xml" }) + public void testDeleteVariableInstancesWithHistoryLevelTask(HistoryLevel historyLevel) { + processEngineConfiguration.setHistoryLevel(historyLevel); + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess") + .variable("testVariable", "testValue").start(); + + List variableInstances = runtimeService.createVariableInstanceQuery().processInstanceId(processInstance.getId()).list(); + assertThat(variableInstances).extracting(VariableInstance::getName, VariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + taskService.complete(taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()); + + List historicVariableInstances = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()).list(); + + assertThat(historicVariableInstances).isEmpty(); + + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()) + .singleResult(); + assertThat(historicProcessInstance).isNotNull(); + assertThat(historicProcessInstance.getId()).isEqualTo(processInstance.getId()); + + } + + @ParameterizedTest + @EnumSource + @Deployment(resources = { "org/flowable/engine/test/api/history/oneTaskHistoryLevelInstanceProcess.bpmn20.xml" }) + public void testDeleteVariableInstancesWithHistoryLevelInstance(HistoryLevel historyLevel) { + processEngineConfiguration.setHistoryLevel(historyLevel); + + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess") + .variable("testVariable", "testValue").start(); + + List variableInstances = runtimeService.createVariableInstanceQuery().processInstanceId(processInstance.getId()).list(); + assertThat(variableInstances).extracting(VariableInstance::getName, VariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + taskService.complete(taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()); + + List historicVariableInstances = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()).list(); + + assertThat(historicVariableInstances).isEmpty(); + + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()) + .singleResult(); + assertThat(historicProcessInstance).isNotNull(); + + } + + @ParameterizedTest + @EnumSource + @Deployment(resources = { "org/flowable/engine/test/api/history/oneTaskHistoryLevelActivityProcess.bpmn20.xml" }) + public void testDeleteVariableInstancesWithHistoryLevelActivity(HistoryLevel historyLevel) { + processEngineConfiguration.setHistoryLevel(historyLevel); + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess") + .variable("testVariable", "testValue").start(); + + List variableInstances = runtimeService.createVariableInstanceQuery().processInstanceId(processInstance.getId()).list(); + assertThat(variableInstances).extracting(VariableInstance::getName, VariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + taskService.complete(taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()); + + List historicVariableInstances = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()).list(); + + assertThat(historicVariableInstances).extracting(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + } + + @ParameterizedTest + @EnumSource + @Deployment(resources = { "org/flowable/engine/test/api/history/oneTaskHistoryLevelAuditProcess.bpmn20.xml" }) + public void testDeleteVariableInstancesWithHistoryLevelAudit(HistoryLevel historyLevel) { + processEngineConfiguration.setHistoryLevel(historyLevel); + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess") + .variable("testVariable", "testValue").start(); + + List variableInstances = runtimeService.createVariableInstanceQuery().processInstanceId(processInstance.getId()).list(); + assertThat(variableInstances).extracting(VariableInstance::getName, VariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + taskService.complete(taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()); + + List historicVariableInstances = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()).list(); + + assertThat(historicVariableInstances).extracting(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + } + + @ParameterizedTest + @EnumSource + @Deployment(resources = { "org/flowable/engine/test/api/history/oneTaskHistoryLevelFullProcess.bpmn20.xml" }) + public void testDeleteVariableInstancesWithHistoryLevelFull(HistoryLevel historyLevel) { + processEngineConfiguration.setHistoryLevel(historyLevel); + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess") + .variable("testVariable", "testValue").start(); + + List variableInstances = runtimeService.createVariableInstanceQuery().processInstanceId(processInstance.getId()).list(); + assertThat(variableInstances).extracting(VariableInstance::getName, VariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + taskService.complete(taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()); + + List historicVariableInstances = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstance.getId()).list(); + + assertThat(historicVariableInstances).extracting(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue).contains( + tuple("testVariable", "testValue") + ); + } +} diff --git a/pom.xml b/pom.xml index 983d9f12a03..fce28755964 100644 --- a/pom.xml +++ b/pom.xml @@ -389,7 +389,7 @@ org.postgresql postgresql - 42.5.4 + 42.7.2 xerces