Skip to content

Commit 919ebfa

Browse files
committed
Pass the source definition to migration callbacks
CaseInstanceMigrationCallback and ProcessInstanceMigrationCallback previously only received the definition an instance was migrated *to*. They now also receive the definition it was migrated *from*, so listeners can compare source and target (e.g. version-aware auditing or reacting to specific upgrade paths). This is done backwards compatibly via a new default overload on each callback method that takes the source definition as an extra parameter and delegates to the existing method. Existing implementations keep working unchanged; the engines now invoke the new overloads: - CaseInstanceMigrationCallback#caseInstanceMigrated and #historicCaseInstanceMigrated gain a sourceCaseDefinition parameter. - ProcessInstanceMigrationCallback#processInstanceMigrated gains a sourceProcessDefinition parameter. The source definition is resolved from the instance's pre-migration definition id, captured before the reference is overwritten, and only resolved when at least one callback is registered (no overhead otherwise). Adds CaseInstanceMigrationCallbackTest and ProcessInstanceMigrationCallbackTest covering the source definition on runtime and historic migrations, plus that callbacks implementing only the original methods are still invoked.
1 parent 238a0d6 commit 919ebfa

6 files changed

Lines changed: 347 additions & 6 deletions

File tree

modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/CaseInstanceMigrationCallback.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,17 @@
1919
public interface CaseInstanceMigrationCallback {
2020

2121
void caseInstanceMigrated(CaseInstance caseInstance, CaseDefinition caseDefToMigrateTo, CaseInstanceMigrationDocument document);
22-
22+
23+
default void caseInstanceMigrated(CaseInstance caseInstance, CaseDefinition sourceCaseDefinition, CaseDefinition caseDefToMigrateTo,
24+
CaseInstanceMigrationDocument document) {
25+
caseInstanceMigrated(caseInstance, caseDefToMigrateTo, document);
26+
}
27+
2328
void historicCaseInstanceMigrated(HistoricCaseInstance caseInstance, CaseDefinition caseDefToMigrateTo, HistoricCaseInstanceMigrationDocument document);
24-
29+
30+
default void historicCaseInstanceMigrated(HistoricCaseInstance caseInstance, CaseDefinition sourceCaseDefinition, CaseDefinition caseDefToMigrateTo,
31+
HistoricCaseInstanceMigrationDocument document) {
32+
historicCaseInstanceMigrated(caseInstance, caseDefToMigrateTo, document);
33+
}
34+
2535
}

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,9 @@ protected void doMigrateCaseInstance(CaseInstanceEntity caseInstance, CaseDefini
330330

331331
List<CaseInstanceMigrationCallback> migrationCallbacks = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getCaseInstanceMigrationCallbacks();
332332
if (migrationCallbacks != null && !migrationCallbacks.isEmpty()) {
333+
CaseDefinition sourceCaseDefinition = CaseDefinitionUtil.getCaseDefinition(originalCaseDefinitionId);
333334
for (CaseInstanceMigrationCallback caseInstanceMigrationCallback : migrationCallbacks) {
334-
caseInstanceMigrationCallback.caseInstanceMigrated(caseInstance, caseDefinitionToMigrateTo, document);
335+
caseInstanceMigrationCallback.caseInstanceMigrated(caseInstance, sourceCaseDefinition, caseDefinitionToMigrateTo, document);
335336
}
336337
}
337338

@@ -359,6 +360,7 @@ protected void doMigrateHistoricCaseInstance(HistoricCaseInstanceEntity historic
359360
}
360361

361362
LOGGER.debug("Updating case definition reference of case root execution with id:'{}' to '{}'", historicCaseInstance.getId(), caseDefinitionToMigrateTo.getId());
363+
String originalCaseDefinitionId = historicCaseInstance.getCaseDefinitionId();
362364
historicCaseInstance.setCaseDefinitionId(caseDefinitionToMigrateTo.getId());
363365
historicCaseInstance.setCaseDefinitionKey(caseDefinitionToMigrateTo.getKey());
364366
historicCaseInstance.setCaseDefinitionName(caseDefinitionToMigrateTo.getName());
@@ -371,8 +373,9 @@ protected void doMigrateHistoricCaseInstance(HistoricCaseInstanceEntity historic
371373

372374
List<CaseInstanceMigrationCallback> migrationCallbacks = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getCaseInstanceMigrationCallbacks();
373375
if (migrationCallbacks != null && !migrationCallbacks.isEmpty()) {
376+
CaseDefinition sourceCaseDefinition = CaseDefinitionUtil.getCaseDefinition(originalCaseDefinitionId);
374377
for (CaseInstanceMigrationCallback caseInstanceMigrationCallback : migrationCallbacks) {
375-
caseInstanceMigrationCallback.historicCaseInstanceMigrated(historicCaseInstance, caseDefinitionToMigrateTo, document);
378+
caseInstanceMigrationCallback.historicCaseInstanceMigrated(historicCaseInstance, sourceCaseDefinition, caseDefinitionToMigrateTo, document);
376379
}
377380
}
378381
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
14+
package org.flowable.cmmn.test.migration;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
22+
import org.flowable.cmmn.api.history.HistoricCaseInstance;
23+
import org.flowable.cmmn.api.migration.CaseInstanceMigrationCallback;
24+
import org.flowable.cmmn.api.migration.CaseInstanceMigrationDocument;
25+
import org.flowable.cmmn.api.migration.HistoricCaseInstanceMigrationDocument;
26+
import org.flowable.cmmn.api.repository.CaseDefinition;
27+
import org.flowable.cmmn.api.runtime.CaseInstance;
28+
import org.flowable.task.api.Task;
29+
import org.junit.jupiter.api.AfterEach;
30+
import org.junit.jupiter.api.Test;
31+
32+
/**
33+
* Tests that {@link CaseInstanceMigrationCallback} receives the source case definition the instance was migrated from,
34+
* and that callbacks implementing only the deprecated methods are still invoked (backwards compatibility).
35+
*/
36+
public class CaseInstanceMigrationCallbackTest extends AbstractCaseMigrationTest {
37+
38+
protected static final String ONE_TASK_CASE = "org/flowable/cmmn/test/migration/one-task.cmmn.xml";
39+
40+
@AfterEach
41+
void resetMigrationCallbacks() {
42+
cmmnEngineConfiguration.setCaseInstanceMigrationCallbacks(null);
43+
}
44+
45+
@Test
46+
void runtimeMigrationProvidesSourceCaseDefinition() {
47+
RecordingCallback callback = new RecordingCallback();
48+
cmmnEngineConfiguration.setCaseInstanceMigrationCallbacks(Collections.singletonList(callback));
49+
50+
CaseDefinition sourceDefinition = deployCaseDefinition("test1", ONE_TASK_CASE);
51+
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start();
52+
CaseDefinition destinationDefinition = deployCaseDefinition("test1", ONE_TASK_CASE);
53+
54+
cmmnMigrationService.createCaseInstanceMigrationBuilder()
55+
.migrateToCaseDefinition(destinationDefinition.getId())
56+
.migrate(caseInstance.getId());
57+
58+
assertThat(callback.getRuntimeMigrations()).hasSize(1);
59+
RecordedMigration migration = callback.getRuntimeMigrations().get(0);
60+
assertThat(migration.getSource().getId()).isEqualTo(sourceDefinition.getId());
61+
assertThat(migration.getSource().getVersion()).isEqualTo(1);
62+
assertThat(migration.getTarget().getId()).isEqualTo(destinationDefinition.getId());
63+
assertThat(migration.getTarget().getVersion()).isEqualTo(2);
64+
}
65+
66+
@Test
67+
void historicMigrationProvidesSourceCaseDefinition() {
68+
RecordingCallback callback = new RecordingCallback();
69+
cmmnEngineConfiguration.setCaseInstanceMigrationCallbacks(Collections.singletonList(callback));
70+
71+
CaseDefinition sourceDefinition = deployCaseDefinition("test1", ONE_TASK_CASE);
72+
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start();
73+
CaseDefinition destinationDefinition = deployCaseDefinition("test1", ONE_TASK_CASE);
74+
75+
Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult();
76+
cmmnTaskService.complete(task.getId());
77+
78+
cmmnMigrationService.createHistoricCaseInstanceMigrationBuilder()
79+
.migrateToCaseDefinition(destinationDefinition.getId())
80+
.migrate(caseInstance.getId());
81+
82+
assertThat(callback.getHistoricMigrations()).hasSize(1);
83+
RecordedMigration migration = callback.getHistoricMigrations().get(0);
84+
assertThat(migration.getSource().getId()).isEqualTo(sourceDefinition.getId());
85+
assertThat(migration.getSource().getVersion()).isEqualTo(1);
86+
assertThat(migration.getTarget().getId()).isEqualTo(destinationDefinition.getId());
87+
assertThat(migration.getTarget().getVersion()).isEqualTo(2);
88+
}
89+
90+
@Test
91+
void legacyCallbackStillInvokedForRuntimeMigration() {
92+
LegacyRecordingCallback callback = new LegacyRecordingCallback();
93+
cmmnEngineConfiguration.setCaseInstanceMigrationCallbacks(Collections.singletonList(callback));
94+
95+
deployCaseDefinition("test1", ONE_TASK_CASE);
96+
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start();
97+
CaseDefinition destinationDefinition = deployCaseDefinition("test1", ONE_TASK_CASE);
98+
99+
cmmnMigrationService.createCaseInstanceMigrationBuilder()
100+
.migrateToCaseDefinition(destinationDefinition.getId())
101+
.migrate(caseInstance.getId());
102+
103+
assertThat(callback.getRuntimeTargetDefinitionIds()).containsExactly(destinationDefinition.getId());
104+
}
105+
106+
static class RecordedMigration {
107+
108+
private final CaseDefinition source;
109+
private final CaseDefinition target;
110+
111+
RecordedMigration(CaseDefinition source, CaseDefinition target) {
112+
this.source = source;
113+
this.target = target;
114+
}
115+
116+
public CaseDefinition getSource() {
117+
return source;
118+
}
119+
120+
public CaseDefinition getTarget() {
121+
return target;
122+
}
123+
}
124+
125+
static class RecordingCallback implements CaseInstanceMigrationCallback {
126+
127+
private final List<RecordedMigration> runtimeMigrations = new ArrayList<>();
128+
private final List<RecordedMigration> historicMigrations = new ArrayList<>();
129+
130+
@Override
131+
public void caseInstanceMigrated(CaseInstance caseInstance, CaseDefinition caseDefToMigrateTo, CaseInstanceMigrationDocument document) {
132+
}
133+
134+
@Override
135+
public void historicCaseInstanceMigrated(HistoricCaseInstance caseInstance, CaseDefinition caseDefToMigrateTo,
136+
HistoricCaseInstanceMigrationDocument document) {
137+
}
138+
139+
@Override
140+
public void caseInstanceMigrated(CaseInstance caseInstance, CaseDefinition sourceCaseDefinition,
141+
CaseDefinition caseDefToMigrateTo, CaseInstanceMigrationDocument document) {
142+
runtimeMigrations.add(new RecordedMigration(sourceCaseDefinition, caseDefToMigrateTo));
143+
}
144+
145+
@Override
146+
public void historicCaseInstanceMigrated(HistoricCaseInstance caseInstance, CaseDefinition sourceCaseDefinition,
147+
CaseDefinition caseDefToMigrateTo, HistoricCaseInstanceMigrationDocument document) {
148+
historicMigrations.add(new RecordedMigration(sourceCaseDefinition, caseDefToMigrateTo));
149+
}
150+
151+
public List<RecordedMigration> getRuntimeMigrations() {
152+
return runtimeMigrations;
153+
}
154+
155+
public List<RecordedMigration> getHistoricMigrations() {
156+
return historicMigrations;
157+
}
158+
}
159+
160+
/** Implements only the old methods to verify the new overloads delegate to them. */
161+
static class LegacyRecordingCallback implements CaseInstanceMigrationCallback {
162+
163+
private final List<String> runtimeTargetDefinitionIds = new ArrayList<>();
164+
private final List<String> historicTargetDefinitionIds = new ArrayList<>();
165+
166+
@Override
167+
public void caseInstanceMigrated(CaseInstance caseInstance, CaseDefinition caseDefToMigrateTo, CaseInstanceMigrationDocument document) {
168+
runtimeTargetDefinitionIds.add(caseDefToMigrateTo.getId());
169+
}
170+
171+
@Override
172+
public void historicCaseInstanceMigrated(HistoricCaseInstance caseInstance, CaseDefinition caseDefToMigrateTo,
173+
HistoricCaseInstanceMigrationDocument document) {
174+
historicTargetDefinitionIds.add(caseDefToMigrateTo.getId());
175+
}
176+
177+
public List<String> getRuntimeTargetDefinitionIds() {
178+
return runtimeTargetDefinitionIds;
179+
}
180+
181+
public List<String> getHistoricTargetDefinitionIds() {
182+
return historicTargetDefinitionIds;
183+
}
184+
}
185+
186+
}

modules/flowable-engine/src/main/java/org/flowable/engine/impl/migration/ProcessInstanceMigrationManagerImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ protected void doMigrateProcessInstance(ProcessInstance processInstance, Process
449449
procDefToMigrateTo, document, commandContext);
450450

451451
LOGGER.debug("Updating Process definition reference of process root execution with id:'{}' to '{}'", processInstance.getId(), procDefToMigrateTo.getId());
452+
String originalProcessDefinitionId = processInstanceEntity.getProcessDefinitionId();
452453
processInstanceEntity.setProcessDefinitionId(procDefToMigrateTo.getId());
453454

454455
LOGGER.debug("Resolve activity executions to migrate");
@@ -500,8 +501,9 @@ protected void doMigrateProcessInstance(ProcessInstance processInstance, Process
500501

501502
List<ProcessInstanceMigrationCallback> migrationCallbacks = CommandContextUtil.getProcessEngineConfiguration(commandContext).getProcessInstanceMigrationCallbacks();
502503
if (migrationCallbacks != null && !migrationCallbacks.isEmpty()) {
504+
ProcessDefinition sourceProcessDefinition = ProcessDefinitionUtil.getProcessDefinition(originalProcessDefinitionId);
503505
for (ProcessInstanceMigrationCallback processInstanceMigrationCallback : migrationCallbacks) {
504-
processInstanceMigrationCallback.processInstanceMigrated(processInstance, procDefToMigrateTo, document, commandContext);
506+
processInstanceMigrationCallback.processInstanceMigrated(processInstance, sourceProcessDefinition, procDefToMigrateTo, document, commandContext);
505507
}
506508
}
507509

modules/flowable-engine/src/main/java/org/flowable/engine/migration/ProcessInstanceMigrationCallback.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,10 @@ public interface ProcessInstanceMigrationCallback {
2020

2121
void processInstanceMigrated(ProcessInstance processInstance, ProcessDefinition procDefToMigrateTo,
2222
ProcessInstanceMigrationDocument document, CommandContext commandContext);
23-
23+
24+
default void processInstanceMigrated(ProcessInstance processInstance, ProcessDefinition sourceProcessDefinition,
25+
ProcessDefinition procDefToMigrateTo, ProcessInstanceMigrationDocument document, CommandContext commandContext) {
26+
processInstanceMigrated(processInstance, procDefToMigrateTo, document, commandContext);
27+
}
28+
2429
}

0 commit comments

Comments
 (0)