1. Introduction

This guide describes the various things that are needed when migrating from Activiti v 5.x to Activiti version 6. This is a preliminary version of the migration guide, which will be updated while alpha/beta releases of Activiti v6 are being pushed out. As it goes with such releases, many things can still change on the road to a stable version. At that point, the migration guide will be formalized and the regular user guide will be updated to reflect all the features and capabilities in Activiti v6, as was the case for Activiti v5.

2. Design goals

The design goals of version 6 are:

  • Complete backwards compatibility with version 5: database-wise, concept-wise and code-wise.

  • Rewrite of the core engine: direct execution of BPMN 2.0 (vs transformation to intermediate model)

  • Simpler and cleaner runtime execution data structure, where predictability of the structure is crucial

  • Decoupling of persistence layers for future possible different implementations

3. Database migration

There is no database migration when moving from v5 to v6: the database schema is the same for v5 and v6. All data produced by Activiti v5 can simply remain in the database. When the v6 engine is started for the first time, it will do an automatic update (as with any v5 version) to the schema. Besides some small schema changes, the main update is the job table that has been split up in a job, timer job, suspended job and dead letter job table. The timer jobs that are not yet due, will be moved to the new timer table. Jobs with no more retries left will be moved to the dead letter job table and jobs for suspended process instances will be moved to the suspended job table.

4. Conceptual changes

The main reason for calling this activiti version 6 is because the core engine has been completely rewritten. The way core engine operations are executed has changed completely, along with a direct execution of BPMN (in V5 there is an intermediate model). Also the way runtime executions are represented (the execution tree) changed. In general, both areas have been simplified significantly, making execution simpler and clearer while also making the writing of custom behavior easier and more accessible.

5. Breaking changes

The following changes are breaking changes (ie there will be a compile error most likely)..

5.1. PVM classes

All classes from the org.activiti.engine.impl.pvm package (and subpackages) have been removed. This is because the PVM (Process Virtual Machine) model has been removed and replaced by a simpler and more lightweight model.

This means that usages of ActivitiImpl, ProcessDefinitionImpl, ExecutionImpl, TransitionImpl are invalid.

Generally, most of the usage of these classes in version 5 came down to getting information that was contained in the process definition. In version 6, all the process definition information can be found through the BpmnModel, which is a Java representation of the BPMN 2.0 XML for the process definition (enhanced to make certain operations and searches easier).

The quickest way to get the BpmnModel for a process definition is to use the org.activiti.engine.impl.util.ProcessDefinitionUtil class:

// The whole model
ProcessDefinitionUtil.getBpmnModel(String processDefinitionId);

// Only the specific process definition
ProcessDefinitionUtil.getProcess(String processDefinitionId);

5.2. ActivityExecution is replaced by DelegateExecution

We removed ActivityExecution and replaced it where used with the DelegateExecution class.

All methods from the ActivityExecution class are copied to the DelegateExecution class.

5.3. Job, timer, suspended and dead letter jobs

Activiti 5 had only 1 job table and this meant that a fairly complex query had to be executed to get the jobs that needed to be executed from the database.

With Activiti 6, the jobs have been split up in a job (ACT_RU_JOB), timer (ACT_RU_TIMER_JOB), suspended (ACT_RU_SUSPENDED_JOB), and (ACT_RU_DEADLETTER_JOB) dead letter table. Jobs in the job table can be directly executed (like asynchronous jobs and due timer jobs). So there’s no need for a complex query anymore, the only where clause is a lock time column that should be NULL. Timer jobs are now persisted in a dedicated timer jobs table and there’s a Runnable that checks for timer jobs due to execute. When a timer job is due to be executed, the job will be moved to the job table. When the job executor Runnable is ready to execute the job it will be fetched from the job table and executed. When a process definition and process instance is suspended, the corresponding jobs will be moved to a separate suspended job table. This simplifies the job executor query and makes it very clear which jobs are suspended. If a job execution fails, the job will be placed in the timer job table with a due date that’s set to current time + the async failed job wait time configured on the process engine configuration. When the job is due to be executed it will be moved to the job table again and be executed. When the number of retries is down to zero, the job will be moved to the dead letter table and no automatic execution will be performed. This also simplifies the default job executor queries and makes it obvious which jobs are stuck and might need manual intervention.

The embedded Activiti 5 engine in Activiti 6 works with these 4 job tables as well. But there’s only one threadpool fetching jobs from the database. When a job is fetched from the database, the engine version for the job is checked based on the process definition id, and the job is executed by the Activiti 6 or embedded Activiti 5 Engine.

5.4. Signaling an execution

In version 5, there always was confusion about signaling an execution, when using for example runtimeService.signal(executionI);. As a signal is a valid BPMN 2.0 concept and feature, it conflicts conceptually.

In version 6, the signal() methods have been renamed to trigger()..

This also means that SignalableActivityBehavior, the interface to be implemented for behaviors that can be triggered from external sources, is now called TriggerableActivityBehavior.

5.5. Checked Exceptions

In version 5, the delegate classes like JavaDelegate and ActivityBevior had throws Exception in their signature. As with any modern framework, the use of checked Exceptions has been removed in version 6.

5.6. Delegate classes

org.activiti.engine.impl.pvm.delegate.ActivityBehavior has changed package and lives now in org.activiti.engine.impl.delegate.

The following methods have been removed from DelegateExecution:

  • end()

  • createdExecution()

They have been replaced by calls to the ExecutionEntityManager, which can be fetched through Context.getCommandContext.getExecutionEntityManager().

5.7. EntityManagers

In Activiti version 5, all EntityManager (responsible for persistence but also certain logic) classes did not have an interface. In version 6, all EntityManager classes have been renamed to have Impl as suffix and an interface without the suffix. This effectively means that the version 5 EntityManager class name is now the name of the corresponding interface.

All EntityManager interfaces extend the generic org.activiti.engine.impl.persistence.entity.EntityManager interface. All implementation classes extend a generic AbstractEntityManager interface.

Also, for consistency: * The UserIdentityManager interface has been renamed to UserEntityManager * The GroupIdentityManager interface has been renamed to GroupEntityManager

5.8. PersistentObject renamed to Entity

The class org.activiti.engine.impl.db.PersistentObject has been renamed to Entity to be consistent with all the other classes (EntityManagers and such)..

All related classes that used the term persistent object have been refactored to entity too.

6. V5 compatibility

When migrating to Activiti version 6 (which basically means replacing the JAR file on the classpath), all current deployments and process definitions are tagged as being a version 5 artifact. At various points (completing a task, starting a new process instance, task assignment, …​ quite a long list) the engine will check whether the involved process definition has that version 5 tag. If so, it will delegate execution to an embedded mini version 5 engine.

This means that the compatibility approach we opted for is that of a phase out: let the current process definitions run in 'version 5 mode until the behaviour has been verified and tested to be identical on version 6.

To enable this behavior (by default it is disabled!), add the following to the engine config:

<property name="activiti5CompatibilityEnabled" value="true" />

and add the activiti5-compatibility jar to your classpath (manually or through a dependency mechanism like Maven)..

In case the default implementation org.activiti.compatibility.DefaultActiviti5CompatibilityHandler is insufficient, as custom implementation can be created. Set the activiti5CompatibilityHandlerFactory property of the engine configuration to the fully qualified classname to make that happen. That Factory class should produce an instance of the handler responsible for bridging from version 6 to 5.

To move a process definition to run on the version 6 engine, simply redeploy it. New process instances will be running in version 6 mode, while existing process instances will run in _version 5 mode).

If for some reason, you’d still want to deploy a new version of a process definition to run in version 5 mode, the following code can be used:

repositoryService.createDeployment()
      .addClasspathResource("xyz")
      .deploymentProperty(DeploymentProperties.DEPLOY_AS_ACTIVITI5_PROCESS_DEFINITION, Boolean.TRUE)
      .deploy();

If you are using the Activiti Spring module additional configuration is needed if you want Activiti 5 compatibility:

<property name="activiti5CompatibilityEnabled" value="true" />
<property name="activiti5CompatibilityHandlerFactory" ref="activiti5CompabilityFactory" />

....

<bean id="activiti5CompabilityFactory" class="org.activiti.compatibility.spring.SpringActiviti5CompatibilityHandlerFactory" />

and add the activiti5-spring and activiti5-spring-compatibility jars to your classpath (manually or through a dependency mechanism like Maven)..