protected void migrateProcessInstance(MigratingProcessInstance migratingProcessInstance) {
    MigratingActivityInstance rootActivityInstance =
        migratingProcessInstance.getMigratingInstance(
            migratingProcessInstance.getProcessInstanceId());

    migrateActivityInstance(
        migratingProcessInstance, new MigratingExecutionBranch(), rootActivityInstance);
  }
  protected void validateInstructions(MigratingProcessInstance migratingProcessInstance) {

    List<MigrationInstructionInstanceValidator> validators =
        Arrays.asList(new AdditionalFlowScopeValidator(), new RemoveFlowScopeValidator());
    MigrationInstructionInstanceValidationReport validationReport =
        new MigrationInstructionInstanceValidationReport(migratingProcessInstance);

    for (MigratingActivityInstance migratingActivityInstance :
        migratingProcessInstance.getMigratingActivityInstances()) {
      for (MigrationInstructionInstanceValidator validator : validators) {
        validator.validate(migratingProcessInstance, migratingActivityInstance, validationReport);
      }
    }

    if (validationReport.hasFailures()) {
      throw LOGGER.failingInstructionInstanceValidation(validationReport);
    }
  }
  public Void migrateProcessInstance(CommandContext commandContext, String processInstanceId) {

    ExecutionEntity processInstance =
        commandContext.getExecutionManager().findExecutionById(processInstanceId);

    ProcessDefinitionEntity targetProcessDefinition =
        Context.getProcessEngineConfiguration()
            .getDeploymentCache()
            .findDeployedProcessDefinitionById(migrationPlan.getTargetProcessDefinitionId());

    // Initialize migration: match migration instructions to activity instances and collect required
    // entities
    MigratingProcessInstance migratingProcessInstance =
        MigratingProcessInstance.initializeFrom(
            commandContext, migrationPlan, processInstance, targetProcessDefinition);

    validateInstructions(migratingProcessInstance);

    migrateProcessInstance(migratingProcessInstance);

    return null;
  }
  protected void migrateActivityInstance(
      MigratingProcessInstance migratingProcessInstance,
      MigratingExecutionBranch migratingExecutionBranch,
      MigratingActivityInstance migratingActivityInstance) {

    ActivityInstance activityInstance = migratingActivityInstance.getActivityInstance();

    if (!activityInstance.getId().equals(activityInstance.getProcessInstanceId())) {
      final MigratingActivityInstance parentMigratingInstance =
          migratingProcessInstance.getMigratingInstance(
              activityInstance.getParentActivityInstanceId());

      ScopeImpl targetFlowScope = migratingActivityInstance.getTargetScope().getFlowScope();
      ScopeImpl parentActivityInstanceTargetScope = parentMigratingInstance.getTargetScope();

      if (targetFlowScope != parentActivityInstanceTargetScope) {
        // create intermediate scopes

        ExecutionEntity flowScopeExecution = migratingActivityInstance.getFlowScopeExecution();

        // 1. detach activity instance
        migratingActivityInstance.detachState();

        // 2. manipulate execution tree
        ExecutionEntity targetExecution = migratingExecutionBranch.getExecution(targetFlowScope);

        if (targetExecution == null) {
          targetExecution =
              createMissingTargetFlowScopeExecution(
                  flowScopeExecution, (PvmActivity) targetFlowScope);
          migratingExecutionBranch.registerExecution(targetFlowScope, targetExecution);
        } else {
          targetExecution = (ExecutionEntity) targetExecution.createConcurrentExecution();
        }

        // 3. attach to newly created execution
        migratingActivityInstance.attachState(targetExecution);
      }
    }

    // 4. update state (e.g. activity id)
    migratingActivityInstance.migrateState();

    // 5. migrate instance state other than execution-tree structure
    migratingActivityInstance.migrateDependentEntities();

    // Let activity instances on the same level of subprocess share the same execution context
    // of newly created scope executions.
    // This ensures that newly created scope executions
    // * are reused to attach activity instances to when the activity instances share a
    //   common ancestor path to the process instance
    // * are not reused when activity instances are in unrelated branches of the execution tree
    migratingExecutionBranch = migratingExecutionBranch.copy();

    for (ActivityInstance childInstance : activityInstance.getChildActivityInstances()) {
      MigratingActivityInstance migratingChildInstance =
          migratingProcessInstance.getMigratingInstance(childInstance.getId());
      migrateActivityInstance(
          migratingProcessInstance, migratingExecutionBranch, migratingChildInstance);
    }
  }