This tool will allow you to automatically or semi-automatically migrate all of your Camunda 7, Operaton and CIB seven Process Instances whenever you release a new version.
| Engine | ofProcessEngine overload |
|---|---|
| Camunda Platform 7 | org.camunda.bpm.engine.ProcessEngine |
| Operaton | org.operaton.bpm.engine.ProcessEngine |
| CIB seven | org.cibseven.bpm.engine.ProcessEngine |
Camunda 7 provides a low-level Migration API that requires you to manually construct a migration plan for a specific source and target process definition ID, then execute it instance by instance. This library builds a fully automated migration layer on top of that API:
| Feature | Engine's native Migration API | + camunda-process-instance-migrator |
|---|---|---|
| Find process definitions to migrate | Manual | Automatic |
| Detect newest deployed version | Manual | Automatic |
| Find instances on older versions | Manual | Automatic |
| Build migration plan (patch-level) | Manual (map activities yourself) | Automatic |
| Build migration plan (minor-level) | Manual | Semi-automatic (you supply instructions) |
| Execute migration | Manual, instance by instance | Automatic, engine-wide |
| Skip major-version instances | Manual | Automatic |
| Logging of results | Manual | Built-in |
Operaton and CIB seven are Camunda 7 forks exposing the same low-level Migration API, so this comparison applies to all three supported engines.
If you develop Process Models in an agile environment, these models will change regularly. As soon as the resulting process definitions are instantiated, be it in a test- or productive environment, you would be advised to migrate these created Process Instances whenever a new process definition is released. This is for two reasons:
- Without migration your process instances will not gain the features added in the new release
- Without migration, you are forced to maintain the existing Java API: you may not rename Java Delegates or change the signature of called Bean's methods.
The migrator scans the Camunda 7, Operaton and CIB seven engine for all deployed process definition keys, finds running instances on older versions, and attempts to migrate them to the newest deployed version.
flowchart TD
A([migrateInstancesOfAllProcesses]) --> B[Load all process definition keys]
B --> C{For each key}
C --> D[Load newest deployed version]
D --> E{Newest version\nfound?}
E -- No --> F[Log: no definition with key]
E -- Yes --> G{Has valid\nversion tag?}
G -- No --> H[Log: no version tag]
G -- Yes --> I[Get instances on older versions]
I --> J{For each\nolder instance}
J --> K{Version\ndifference?}
K -- Patch --> L[Create patch plan\nmap equal activity IDs]
K -- Minor --> M[Create patch plan\n+ fetch MigrationInstructions\n+ combine and merge]
K -- Major --> N[Skip - no migration]
L --> O[Perform migration]
M --> O
O -- Success --> P[Log success]
O -- Failure --> Q[Log error]
All process models must use the Version Tag property with the format MAJOR.MINOR.PATCH
(e.g. 1.0.0):
flowchart LR
A["1.0.0"] -- "patch bump\n(rename/add activity)" --> B["1.0.1"]
A -- "minor bump\n(wait-state change)" --> C["1.1.0"]
A -- "major bump\n(breaking change)" --> D["2.0.0"]
B -.->|automatic migration| B2[running instances migrated]
C -.->|migration + custom instructions| C2[running instances migrated]
D -.->|no migration| D2[running instances untouched]
| Version level | When to increase | Migration behavior |
|---|---|---|
| Patch | Simple changes: rename/add activities, change Java delegates | Automatic — equal activity IDs are mapped |
| Minor | Wait-state ID changes, wait-state removals, activities moved into subprocesses | Requires custom MigrationInstructions |
| Major | Breaking changes where no migration is wanted | No migration attempted |
Notes:
- Process definitions with a missing or malformed version tag are excluded from migration
- Process definitions with major version
0(e.g.0.0.1) are excluded from migration
<dependency>
<groupId>de.envite.bpm</groupId>
<artifactId>camunda-process-instance-migrator</artifactId>
<version>2.1.0</version>
</dependency>Please check https://central.sonatype.com/artifact/de.envite.bpm/camunda-process-instance-migrator for the latest version.
Inject Camunda, Operaton and/or CIB seven ProcessEngine and build the migrator. No further configuration is required for
patch-level migrations:
@Configuration
public class MigratorConfiguration {
@Autowired
private ProcessEngine processEngine;
@Bean
public ProcessInstanceMigrator processInstanceMigrator() {
return ProcessInstanceMigrator.builder()
.ofProcessEngine(processEngine)
.build();
}
}ofProcessEngine is overloaded for org.camunda.bpm.engine.ProcessEngine,
org.operaton.bpm.engine.ProcessEngine, and org.cibseven.bpm.engine.ProcessEngine — usage is
identical regardless of engine.
You may then use the ProcessInstanceMigrator bean to manually trigger migration (e.g. via a
REST endpoint), or automatically on each deployment via @PostConstruct or ApplicationReadyEvent:
@Component
public class OnStartupMigrator {
@Autowired
private ProcessInstanceMigrator processInstanceMigrator;
@EventListener(ApplicationReadyEvent.class)
public void migrateAllProcessInstances() {
processInstanceMigrator.migrateInstancesOfAllProcesses();
}
}Whenever a wait-state activity is removed or its ID is changed, you must supply migration instructions so the migrator knows how to remap the old activity IDs to the new ones:
@Configuration
public class MigratorConfiguration {
@Autowired
private ProcessEngine processEngine;
@Bean
public ProcessInstanceMigrator processInstanceMigrator() {
return ProcessInstanceMigrator.builder()
.ofProcessEngine(processEngine)
.withMigrationInstructions(generateMigrationInstructions())
.build();
}
private MigrationInstructions generateMigrationInstructions() {
return new MigrationInstructionsImpl()
.putInstructions("Some_process_definition_key", Arrays.asList(
MinorMigrationInstructions.builder()
.sourceMinorVersion(0)
.targetMinorVersion(2)
.majorVersion(1)
.migrationInstructions(Arrays.asList(
new MigrationInstructionImpl("UserTask1", "UserTask3"),
new MigrationInstructionImpl("UserTask2", "UserTask3")))
.build()));
}
}Each putInstructions call defines a migration path for one specific process key and version range
(here: from 1.0.x to 1.2.x). You may also break it up into smaller steps (1.0.x → 1.1.x and
1.1.x → 1.2.x) — the migrator will chain them automatically.
There is no requirement for all intermediate versions to actually be deployed on the target
environment. If a production environment jumps from 1.5.x to 1.8.x (skipping intermediate
versions), instructions for 1.5→1.6, 1.6→1.7, and 1.7→1.8 are sufficient.
You can configure the migration behavior per process definition key using MigrationProperties:
@Bean
public ProcessInstanceMigrator processInstanceMigrator() {
MigrationProperties properties = new MigrationPropertiesImpl()
.putSkipCustomListeners("Some_process_definition_key", true) // default: true
.putSkipIoMappings("Some_process_definition_key", true) // default: true
.putExecuteAsync("Some_process_definition_key", false); // default: false
return ProcessInstanceMigrator.builder()
.ofProcessEngine(processEngine)
.withMigrationProperties(properties)
.build();
}Of course, you can always submit issues or create a pull request. But if you are looking for a quick change in functionality, it is recommended that you create your own implementation of the interfaces that provide the migrator's functionality. If, for example, you want to provide minor migration instructions via a JSON file, or you wish to modify logging, provide your own implementation:
@Bean
public ProcessInstanceMigrator processInstanceMigrator() {
return ProcessInstanceMigrator.builder()
.ofProcessEngine(processEngine)
//CustomJsonMigrationInstructionReader implements MigrationInstructions
.withMigrationInstructions(new CustomJsonMigrationInstructionReader())
//CustomMigratorLogger implements MigratorLogger
.withMigratorLogger(new CustomMigratorLogger())
.build();
}All major components are behind interfaces with default implementations that can be swapped via the builder:
| Builder method | Interface | Purpose |
|---|---|---|
withMigrationInstructions |
MigrationInstructions |
Source for minor migration instructions |
withMigrationProperties |
MigrationProperties |
Migration execution options per key |
withMigratorLogger |
MigratorLogger |
Log migration results |
withGetOlderProcessInstances |
GetOlderProcessInstances |
Find instances eligible for migration |
withCreatePatchMigrationPlanToSet |
CreatePatchMigrationPlan |
Create patch-level migration plans |
withLoadProcessDefinitionKeys |
LoadProcessDefinitionKeys |
Discover all process definition keys |
withLoadNewestDeployedVersion |
LoadNewestDeployedVersion |
Find the newest deployed version per key |
withGenerateAllInstancesLoggingData |
GenerateAllInstancesLoggingData |
Aggregate logging data |
The tool was developed against Camunda 7 and its compatible forks (Operaton, CIB seven) — see Supported engines for tested versions. It is not compatible with Camunda 8.
Requires Java 17.
There are also no restrictions to the specifiable migration instructions for minor migrations, unlike in the migration wizard of Camunda 7 EE Cockpit. So this migrator will not prevent you from trying to migrate activities to different types of activities (i.e. from wait-states to non-wait-states or from receive tasks to user tasks). This might, however, result in undefined states and has not been tested whatsoever. So handle with care!
Operations that go beyond migration, like Process Instance Modifications or the setting of variables upon migration are also not implemented yet.
Firstly, migration of process instances takes "real" time. Migrating thousands of Process Instances
may take several minutes. So it is advisable to carry out the migration asynchronously (see
putExecuteAsync in Migration properties).
Secondly, the migrator was built to be robust and informative. Any action the migrator takes will be logged, and any issue that may come up during migration, will just cause the migration of that specific process instance to fail and be logged accordingly. So it is advised to check your logs after each migration for faulty process instances. It is very rare that a migration attempt fails, but when it does, you may want to correct it manually.
Of course! Add an issue, submit a pull request. We will be happy to extend the tool with your help.