diff --git a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/NameWithNewLineTest.java b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/NameWithNewLineTest.java
index 7a59295fde6..ded6f547810 100644
--- a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/NameWithNewLineTest.java
+++ b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/NameWithNewLineTest.java
@@ -16,7 +16,6 @@
import static org.flowable.cmmn.converter.CmmnXmlConstants.ATTRIBUTE_ELEMENT_NAME;
import org.flowable.cmmn.model.CmmnModel;
-import org.flowable.cmmn.model.ExtensionElement;
import org.flowable.cmmn.model.PlanItemDefinition;
import org.flowable.test.cmmn.converter.util.CmmnXmlConverterTest;
diff --git a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ScriptServiceTaskCmmnXmlConverterTest.java b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ScriptServiceTaskCmmnXmlConverterTest.java
index a9da14dca6a..e999c9fef73 100644
--- a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ScriptServiceTaskCmmnXmlConverterTest.java
+++ b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ScriptServiceTaskCmmnXmlConverterTest.java
@@ -84,4 +84,34 @@ public void validateModel(CmmnModel cmmnModel) {
});
}
+
+ @CmmnXmlConverterTest("org/flowable/test/cmmn/converter/script-task-expression-field.cmmn")
+ public void scriptTaskWithExpressionField(CmmnModel cmmnModel) {
+ assertThat(cmmnModel).isNotNull();
+
+ PlanItem planItemTaskA = cmmnModel.findPlanItem("planItemTaskA");
+ assertThat(planItemTaskA.getEntryCriteria()).isEmpty();
+
+ PlanItemDefinition planItemDefinition = planItemTaskA.getPlanItemDefinition();
+ assertThat(planItemDefinition)
+ .isInstanceOfSatisfying(ScriptServiceTask.class, scriptTask -> {
+ assertThat(scriptTask.getType()).isEqualTo(ScriptServiceTask.SCRIPT_TASK);
+ assertThat(scriptTask.getScriptFormat()).isEqualTo("javascript");
+ assertThat(scriptTask.getScript()).isEqualTo("var a = '${testA}';");
+ assertThat(scriptTask.getResultVariableName()).isEqualTo("scriptResult");
+ assertThat(scriptTask.isAutoStoreVariables()).isFalse();
+ assertThat(scriptTask.isBlocking()).isTrue();
+ assertThat(scriptTask.isAsync()).isFalse();
+ });
+
+ PlanItem planItemTaskB = cmmnModel.findPlanItem("planItemTaskB");
+ planItemDefinition = planItemTaskB.getPlanItemDefinition();
+ assertThat(planItemDefinition)
+ .isInstanceOfSatisfying(ScriptServiceTask.class, scriptServiceTask -> {
+ assertThat(scriptServiceTask.getScriptFormat()).isEqualTo("groovy");
+ assertThat(scriptServiceTask.getScript()).isEqualTo("var b = '${testB}';");
+ assertThat(scriptServiceTask.isAutoStoreVariables()).isTrue();
+ });
+ }
+
}
diff --git a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/script-task-expression-field.cmmn b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/script-task-expression-field.cmmn
new file mode 100644
index 00000000000..f5609cc3580
--- /dev/null
+++ b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/script-task-expression-field.cmmn
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 e9837e3d3c4..61eef26eb90 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
@@ -133,6 +133,8 @@ protected void doMovePlanItemState(CaseInstanceChangeState caseInstanceChangeSta
executeTerminatePlanItemInstances(caseInstanceChangeState, caseInstance, commandContext);
+ executeTerminateNonExistingPlanItemInstancesInTargetCmmnModel(caseInstanceChangeState, commandContext);
+
setCaseDefinitionIdForPlanItemInstances(currentPlanItemInstances, caseInstanceChangeState.getCaseDefinitionToMigrateTo());
executeChangePlanItemIds(caseInstanceChangeState, originalCaseDefinitionId, commandContext);
@@ -835,6 +837,35 @@ protected void executeTerminatePlanItemInstances(CaseInstanceChangeState caseIns
}
}
+ protected void executeTerminateNonExistingPlanItemInstancesInTargetCmmnModel(CaseInstanceChangeState caseInstanceChangeState, CommandContext commandContext) {
+ if (caseInstanceChangeState.getCaseDefinitionToMigrateTo() != null) {
+ CmmnModel targetCmmnModel = CaseDefinitionUtil.getCmmnModel(caseInstanceChangeState.getCaseDefinitionToMigrateTo().getId());
+ List excludePlanItemDefinitionIds = new ArrayList<>();
+ for (TerminatePlanItemDefinitionMapping planItemDefinitionMapping : caseInstanceChangeState.getTerminatePlanItemDefinitions()) {
+ excludePlanItemDefinitionIds.add(planItemDefinitionMapping.getPlanItemDefinitionId());
+ }
+
+ for (ChangePlanItemDefinitionWithNewTargetIdsMapping newTargetIdsMapping : caseInstanceChangeState.getChangePlanItemDefinitionWithNewTargetIds()) {
+ excludePlanItemDefinitionIds.add(newTargetIdsMapping.getExistingPlanItemDefinitionId());
+ }
+
+ for (ChangePlanItemIdWithDefinitionIdMapping definitionIdMapping : caseInstanceChangeState.getChangePlanItemIdsWithDefinitionId()) {
+ excludePlanItemDefinitionIds.add(definitionIdMapping.getExistingPlanItemDefinitionId());
+ }
+
+ for (String currentPlanItemDefinitionId : caseInstanceChangeState.getCurrentPlanItemInstances().keySet()) {
+ if (!excludePlanItemDefinitionIds.contains(currentPlanItemDefinitionId) && targetCmmnModel.findPlanItemDefinition(currentPlanItemDefinitionId) == null) {
+ for (PlanItemInstanceEntity currentPlanItemInstance : caseInstanceChangeState.getCurrentPlanItemInstances().get(currentPlanItemDefinitionId)) {
+ if (!PlanItemInstanceState.TERMINAL_STATES.contains(currentPlanItemInstance.getState())) {
+ terminatePlanItemInstance(currentPlanItemInstance, commandContext);
+ caseInstanceChangeState.addTerminatedPlanItemInstance(currentPlanItemInstance.getPlanItemDefinitionId(), currentPlanItemInstance);
+ }
+ }
+ }
+ }
+ }
+ }
+
protected abstract boolean isDirectPlanItemDefinitionMigration(PlanItemDefinition currentPlanItemDefinition, PlanItemDefinition newPlanItemDefinition);
protected Map> retrievePlanItemInstances(String caseInstanceId) {
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 37f14ea8f65..ed86e51d963 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
@@ -439,6 +439,130 @@ void withSimpleOneTaskCaseChangingOnlyTaskProperties() {
}
}
}
+
+ @Test
+ void withAutomatedMigrationFromTwoTasksToOneTask() {
+ // Arrange
+ deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/two-task.cmmn.xml");
+ CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start();
+ CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/one-task.cmmn.xml");
+
+ // Act
+ cmmnMigrationService.createCaseInstanceMigrationBuilder()
+ .migrateToCaseDefinition(destinationDefinition.getId())
+ .migrate(caseInstance.getId());
+
+ // Assert
+ CaseInstance caseInstanceAfterMigration = cmmnRuntimeService.createCaseInstanceQuery()
+ .caseInstanceId(caseInstance.getId())
+ .singleResult();
+ assertThat(caseInstanceAfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId());
+ assertThat(caseInstanceAfterMigration.getCaseDefinitionVersion()).isEqualTo(2);
+ assertThat(caseInstanceAfterMigration.getCaseDefinitionDeploymentId()).isEqualTo(destinationDefinition.getDeploymentId());
+ List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery()
+ .caseInstanceId(caseInstance.getId())
+ .list();
+ assertThat(planItemInstances).hasSize(1);
+ assertThat(planItemInstances)
+ .extracting(PlanItemInstance::getCaseDefinitionId)
+ .containsOnly(destinationDefinition.getId());
+ assertThat(planItemInstances)
+ .extracting(PlanItemInstance::getName)
+ .containsExactlyInAnyOrder("Task 1");
+ assertThat(planItemInstances)
+ .extracting(PlanItemInstance::getState)
+ .containsOnly(PlanItemInstanceState.ACTIVE);
+
+ List tasks = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).list();
+ assertThat(tasks).hasSize(1);
+ assertThat(tasks.get(0).getScopeDefinitionId()).isEqualTo(destinationDefinition.getId());
+ cmmnTaskService.complete(tasks.get(0).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(2);
+ 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());
+ }
+ }
+ }
+
+ @Test
+ void withActivateTaskFromTwoTasksToOneTask() {
+ // Arrange
+ deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/two-task.cmmn.xml");
+ CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start();
+ CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/one-task.cmmn.xml");
+
+ Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("humanTask1").singleResult();
+ cmmnTaskService.complete(task.getId());
+
+ // Act
+ cmmnMigrationService.createCaseInstanceMigrationBuilder()
+ .migrateToCaseDefinition(destinationDefinition.getId())
+ .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("humanTask1"))
+ .migrate(caseInstance.getId());
+
+ // Assert
+ CaseInstance caseInstanceAfterMigration = cmmnRuntimeService.createCaseInstanceQuery()
+ .caseInstanceId(caseInstance.getId())
+ .singleResult();
+ assertThat(caseInstanceAfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId());
+ assertThat(caseInstanceAfterMigration.getCaseDefinitionVersion()).isEqualTo(2);
+ assertThat(caseInstanceAfterMigration.getCaseDefinitionDeploymentId()).isEqualTo(destinationDefinition.getDeploymentId());
+ List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery()
+ .caseInstanceId(caseInstance.getId())
+ .list();
+ assertThat(planItemInstances).hasSize(1);
+ assertThat(planItemInstances)
+ .extracting(PlanItemInstance::getCaseDefinitionId)
+ .containsOnly(destinationDefinition.getId());
+ assertThat(planItemInstances)
+ .extracting(PlanItemInstance::getName)
+ .containsExactlyInAnyOrder("Task 1");
+ assertThat(planItemInstances)
+ .extracting(PlanItemInstance::getState)
+ .containsOnly(PlanItemInstanceState.ACTIVE);
+
+ List tasks = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).list();
+ assertThat(tasks).hasSize(1);
+ assertThat(tasks.get(0).getScopeDefinitionId()).isEqualTo(destinationDefinition.getId());
+ cmmnTaskService.complete(tasks.get(0).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(3);
+ for (HistoricTaskInstance historicTask : historicTasks) {
+ assertThat(historicTask.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId());
+ }
+ }
+ }
@Test
void withTwoTasksIntroducingANewStageAroundSecondTask() {
diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java
index c3a524da287..d3715616bc8 100644
--- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java
+++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java
@@ -13,10 +13,8 @@
package org.flowable.cmmn.test.runtime;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
import static org.flowable.cmmn.api.runtime.PlanItemInstanceState.ACTIVE;
-import static org.flowable.cmmn.api.runtime.PlanItemInstanceState.AVAILABLE;
import java.util.ArrayList;
import java.util.Arrays;
@@ -32,7 +30,6 @@
import org.flowable.cmmn.engine.PlanItemLocalizationManager;
import org.flowable.cmmn.engine.test.CmmnDeployment;
import org.flowable.cmmn.engine.test.FlowableCmmnTestCase;
-import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.task.api.Task;
import org.junit.Before;
import org.junit.Test;
diff --git a/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ScriptServiceTask.java b/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ScriptServiceTask.java
index 36da3d73ae3..7d06dbcdcdf 100644
--- a/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ScriptServiceTask.java
+++ b/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ScriptServiceTask.java
@@ -12,9 +12,7 @@
*/
package org.flowable.cmmn.model;
-import java.util.Optional;
-
-import org.flowable.common.engine.api.FlowableException;
+import org.apache.commons.lang3.StringUtils;
/**
* @author Dennis
@@ -38,11 +36,16 @@ public void setScriptFormat(String scriptFormat) {
}
public String getScript() {
- Optional script = fieldExtensions.stream()
- .filter(e -> "script".equalsIgnoreCase(e.getFieldName()))
- .findFirst()
- .map(FieldExtension::getStringValue);
- return script.orElseThrow(() -> new FlowableException("Missing script"));
+ for (FieldExtension fieldExtension : fieldExtensions) {
+ if ("script".equalsIgnoreCase(fieldExtension.getFieldName())) {
+ String script = fieldExtension.getStringValue();
+ if (StringUtils.isNotEmpty(script)) {
+ return script;
+ }
+ return fieldExtension.getExpression();
+ }
+ }
+ return null;
}
public boolean isAutoStoreVariables() {
diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java
index 2d437aa0eb1..5b156847873 100755
--- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java
+++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java
@@ -15,11 +15,6 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
@@ -35,7 +30,6 @@
import javax.naming.InitialContext;
import javax.sql.DataSource;
-import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java
index f15ccc3c334..e987a1b7b35 100644
--- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java
+++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java
@@ -189,6 +189,10 @@ protected String getDbVersion() {
return getProperty(getSchemaVersionPropertyName(), false);
}
+ protected int getChangeLogVersionOrder(String changeLogVersion) {
+ return Integer.parseInt(changeLogVersion);
+ }
+
protected ChangeLogVersion getChangeLogVersion() {
String changeLogTableName = getChangeLogTableName();
if (changeLogTableName != null && isTablePresent(changeLogTableName)) {
@@ -198,22 +202,22 @@ protected ChangeLogVersion getChangeLogVersion() {
}
try (PreparedStatement statement = databaseConfiguration.getConnection()
.prepareStatement("select ID from " + changeLogTableName + " order by DATEEXECUTED")) {
- int changeLogVersion = 0;
+ int latestChangeLogVersionOrder = 0;
+ String changeLogVersion = null;
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String changeLogVersionId = resultSet.getString(1);
- int parsedChangeLogVersion = Integer.parseInt(changeLogVersionId);
- if (parsedChangeLogVersion > changeLogVersion) {
+ int changeLogVersionOrder = getChangeLogVersionOrder(changeLogVersionId);
+ if (changeLogVersionOrder > latestChangeLogVersionOrder) {
// Even though we are ordering by DATEEXECUTED, and the last ID should be the last executed one.
// It is still possible that there are multiple entries with the same DATEEXECUTED value and the order might not be correct.
// e.g. MySQL 8.0 sometimes does not return the correct order.
- changeLogVersion = parsedChangeLogVersion;
+ changeLogVersion = changeLogVersionId;
}
}
}
- if (changeLogVersion > 0) {
- String changeLogVersionString = String.valueOf(changeLogVersion);
- return new ChangeLogVersion(changeLogVersionString, getDbVersionForChangelogVersion(changeLogVersionString));
+ if (changeLogVersion != null) {
+ return new ChangeLogVersion(changeLogVersion, getDbVersionForChangelogVersion(changeLogVersion));
}
} catch (SQLException e) {
throw new RuntimeException("Failed to get change log version from " + changeLogTableName, e);
diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/DbUtil.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/DbUtil.java
index 7d6ba521af7..5f19d613014 100644
--- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/DbUtil.java
+++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/DbUtil.java
@@ -17,7 +17,6 @@
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java
index 63f6140142b..61d03b26c77 100644
--- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java
+++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java
@@ -654,11 +654,24 @@ protected void doMoveExecutionState(ProcessInstanceChangeState processInstanceCh
}
protected void processPendingEventSubProcessesStartEvents(ProcessInstanceChangeState processInstanceChangeState, CommandContext commandContext) {
- ProcessInstanceHelper processInstanceHelper = CommandContextUtil.getProcessEngineConfiguration(commandContext).getProcessInstanceHelper();
+ ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ ProcessInstanceHelper processInstanceHelper = processEngineConfiguration.getProcessInstanceHelper();
+ EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
+ ManagementService managementService = processEngineConfiguration.getManagementService();
+
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())) {
+ EventDefinition eventDefinition = startEvent.getEventDefinitions().isEmpty() ? null : startEvent.getEventDefinitions().get(0);
+
+ //Process event sub process when no subscriptions/timer jobs are found
+ boolean processEventSubProcess = false;
+ if (eventDefinition instanceof TimerEventDefinition) {
+ processEventSubProcess = managementService.createTimerJobQuery().executionId(parentExecution.getId()).list().isEmpty();
+ } else {
+ processEventSubProcess = eventSubscriptionService.findEventSubscriptionsByExecution(parentExecution.getId()).isEmpty();
+ }
+ if (processEventSubProcess) {
processInstanceHelper.processEventSubProcess(parentExecution, (EventSubProcess) startEvent.getSubProcess(), commandContext);
}
}
@@ -809,7 +822,10 @@ protected List createEmbeddedSubProcessAndExecutions(Collection
// Build the subProcess hierarchy
for (SubProcess subProcess : subProcessesToCreate.values()) {
- if (!processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(subProcess.getId())) {
+ if (subProcess instanceof EventSubProcess) {
+ ExecutionEntity embeddedSubProcess = createEmbeddedSubProcessHierarchy(subProcess, defaultContinueParentExecution, subProcessesToCreate, movingExecutionIds, processInstanceChangeState, commandContext);
+ moveExecutionEntityContainer.addCreatedEventSubProcess(subProcess.getId(), embeddedSubProcess);
+ } else if (!processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(subProcess.getId())) {
ExecutionEntity embeddedSubProcess = createEmbeddedSubProcessHierarchy(subProcess, defaultContinueParentExecution, subProcessesToCreate, movingExecutionIds, processInstanceChangeState, commandContext);
processInstanceChangeState.addCreatedEmbeddedSubProcess(subProcess.getId(), embeddedSubProcess);
}
@@ -820,7 +836,9 @@ protected List createEmbeddedSubProcessAndExecutions(Collection
for (FlowElementMoveEntry flowElementMoveEntry : moveToFlowElements) {
FlowElement newFlowElement = flowElementMoveEntry.getNewFlowElement();
ExecutionEntity parentExecution;
- if (newFlowElement.getSubProcess() != null && processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(newFlowElement.getSubProcess().getId())) {
+ if (newFlowElement.getSubProcess() != null && moveExecutionEntityContainer.getCreatedEventSubProcess(newFlowElement.getSubProcess().getId()) != null) {
+ parentExecution = moveExecutionEntityContainer.getCreatedEventSubProcess(newFlowElement.getSubProcess().getId());
+ } else if (newFlowElement.getSubProcess() != null && processInstanceChangeState.getCreatedEmbeddedSubProcesses().containsKey(newFlowElement.getSubProcess().getId())) {
parentExecution = processInstanceChangeState.getCreatedEmbeddedSubProcesses().get(newFlowElement.getSubProcess().getId());
} else if ((newFlowElement instanceof Task || newFlowElement instanceof CallActivity) && isFlowElementMultiInstance(newFlowElement) && !movingExecutions.get(0).isMultiInstanceRoot() &&
diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/MoveExecutionEntityContainer.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/MoveExecutionEntityContainer.java
index 6abb2b08869..66e0eb97aaa 100644
--- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/MoveExecutionEntityContainer.java
+++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/MoveExecutionEntityContainer.java
@@ -46,6 +46,7 @@ public class MoveExecutionEntityContainer {
protected Map currentActivityToNewElementMap = new LinkedHashMap<>();
protected Map> flowElementLocalVariableMap = new HashMap<>();
protected List newExecutionIds = new ArrayList<>();
+ protected Map createdEventSubProcesses = new HashMap<>();
public MoveExecutionEntityContainer(List executions, List moveToActivityIds) {
this.executions = executions;
@@ -216,6 +217,14 @@ public void addNewExecutionId(String executionId) {
this.newExecutionIds.add(executionId);
}
+ public ExecutionEntity getCreatedEventSubProcess(String processDefinitionId) {
+ return createdEventSubProcesses.get(processDefinitionId);
+ }
+
+ public void addCreatedEventSubProcess(String processDefinitionId, ExecutionEntity executionEntity) {
+ createdEventSubProcesses.put(processDefinitionId, executionEntity);
+ }
+
public Map> getFlowElementLocalVariableMap() {
return flowElementLocalVariableMap;
}
diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/api/runtime/migration/AbstractProcessInstanceMigrationEventRegistryConsumerTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/api/runtime/migration/AbstractProcessInstanceMigrationEventRegistryConsumerTest.java
new file mode 100644
index 00000000000..14ab5a5de91
--- /dev/null
+++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/api/runtime/migration/AbstractProcessInstanceMigrationEventRegistryConsumerTest.java
@@ -0,0 +1,122 @@
+/* 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.api.runtime.migration;
+
+import java.util.List;
+import java.util.Map;
+
+import org.flowable.common.engine.impl.interceptor.EngineConfigurationConstants;
+import org.flowable.eventregistry.api.EventDeployment;
+import org.flowable.eventregistry.api.EventRegistry;
+import org.flowable.eventregistry.api.EventRepositoryService;
+import org.flowable.eventregistry.api.InboundEventChannelAdapter;
+import org.flowable.eventregistry.impl.EventRegistryEngineConfiguration;
+import org.flowable.eventregistry.model.InboundChannelModel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Provides a test channel and test events.
+ *
+ * @author Bas Claessen
+ */
+public abstract class AbstractProcessInstanceMigrationEventRegistryConsumerTest extends AbstractProcessInstanceMigrationTest {
+
+ protected TestInboundEventChannelAdapter inboundEventChannelAdapter;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ inboundEventChannelAdapter = setupTestChannel();
+
+ getEventRepositoryService().createEventModelBuilder()
+ .key("myEvent")
+ .resourceName("myEvent.event")
+ .deploy();
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ EventRepositoryService eventRepositoryService = getEventRepositoryService();
+ List deployments = eventRepositoryService.createDeploymentQuery().list();
+ for (EventDeployment eventDeployment : deployments) {
+ eventRepositoryService.deleteDeployment(eventDeployment.getId());
+ }
+ deleteDeployments();
+ }
+
+ protected TestInboundEventChannelAdapter setupTestChannel() {
+ TestInboundEventChannelAdapter inboundEventChannelAdapter = new TestInboundEventChannelAdapter();
+ Map