Question

in order of developing a custom BPM Application there is one feature we used with another BPM engine provider and like to use it with camunda too. The targeted functionality is about setting/reset running process instances to a specified task other than the current active one. From our perspective necessary when e.g.:

  • authoring process-instances due to process-version migration
  • resolving incidents
  • resolving accidentially wrong usage by an user

Finally I didn't really found a simple function to do this but worked out some custom code which worked with some limitations. There are some weaknesses and uncertainities within this code so that I have the following question:

Did I miss an alternative way to achieve this or is the following approach correct or is it even fully unsupported at the moment ?

The current weaknesses imho:

  • first and most important: no historic task instance is stored. This causes that it's not traceable who or even when the task was triggered/activate/started. I found the following post on camunda google group (post) which says that it's correct at this point because it’s a task out of the process definition scope but by using a task definition from the underlying process definition I should be "in scope" ?!
  • the code is based on internal implementation and not on official interface
  • at this point a lot of "bootstrap"/initialization have to be done manually but as user (not developer of camunda) I am not fully aware of what is required and what is optional
  • some parts like parsing expressions from task definition failed (see code commented out) but that may be caused by wrong usage

Here's the code (experimental snippet of our camunda service facade) :

@Inject
protected HistoryService histService;
@Inject
protected TaskService taskService;
@Inject
protected ManagementService managementService;
@Inject
protected RuntimeService runtimeService;
@Inject
protected IdentityService identityService;
@Inject
protected RepositoryService repositoryService;
@Inject
protected FormService formService;
@Inject
protected ProcessEngine processEngine;


public void startTask(String processInstanceId, String taskKey) {
    Collection<TaskDefinition> taskDefs = getAvailableTasks(
            processInstanceId);
    TaskEntity newTask = null;
    TaskDefinition taskDef = null;
    for (TaskDefinition taskDefinition : taskDefs) {
        if (taskDefinition.getKey().equals(taskKey)) {
            taskDef = taskDefinition;
            break;
        }
    }
    boolean taskDefExists = taskDef != null;
    List<Task> runningTasksByKey = getTasksByKey(taskKey, processInstanceId);
    boolean taskIsAlreadyRunning = runningTasksByKey != null
            && runningTasksByKey.size() > 0;
    if (taskDefExists && !taskIsAlreadyRunning) {
        newTask = (TaskEntity) taskService.newTask();
        ProcessInstance procInst = getProcessInstance(processInstanceId);
        ExecutionEntity procInstEntity = (ExecutionEntity) procInst;
        String taskName = (String) taskDef.getNameExpression().
                getExpressionText();
//            String taskAssigne = (String) taskDef.getAssigneeExpression().
//                    getValue(
//                            procInstEntity);
//            newTask.setAssignee(taskAssigne);
            newTask.setTaskDefinitionKey(taskDef.getKey());
            newTask.setProcessInstance(procInstEntity);
            newTask.setTaskDefinition(taskDef);
            newTask.setName(taskName);
            newTask.setProcessInstanceId(processInstanceId);
            newTask.setProcessDefinitionId(procInstEntity.
                    getProcessDefinitionId());
            taskService.saveTask(newTask);

        TaskServiceImpl taskServiceImpl = (TaskServiceImpl) BpmPlatform.
                getProcessEngineService().getDefaultProcessEngine().
                getTaskService();
        CommandExecutor commandExecutor = taskServiceImpl.
                getCommandExecutor();
        ExecutionEntity executionEntity = commandExecutor.execute(
                new SaveTaskActivityInstanceCmd(newTask,
                        procInstEntity));
//            commandExecutor.execute(new `SaveTaskHistoricActivityInstanceCmd(executionEntity, newTask));`
    }
}

public Collection<TaskDefinition> getAvailableTasks(String processInstanceId) {
            Map<String, TaskDefinition> taskDefs = null;
            Collection<TaskDefinition> taskDefObjects = null;
            if (processInstanceId != null) {
                ProcessInstanceQuery procInstQuery = runtimeService.
                        createProcessInstanceQuery().processInstanceId(
                                processInstanceId);
                ProcessDefinitionEntity procDefEntity = getProcessDefinitionEager(
                        processInstanceId);
                taskDefs = procDefEntity.getTaskDefinitions();
            }
            taskDefObjects = (Collection<TaskDefinition>) (taskDefs != null ? taskDefs.
                    values() : new ArrayList<TaskDefinition>());
            return taskDefObjects;
}

public ProcessDefinitionEntity getProcessDefinitionEager(
        String processInstanceId) {
    ProcessInstanceQuery procInstQuery = runtimeService.
            createProcessInstanceQuery().processInstanceId(
                    processInstanceId);
    ProcessInstance procInst = procInstQuery.singleResult();
    String procDefId = procInst.getProcessDefinitionId();
    return (ProcessDefinitionEntity) repositoryService.getProcessDefinition(
            procDefId);
}

public List<Task> getTasksByKey(String taskKey, String processInstanceId) {
    List<Task> tasks = taskService.createTaskQuery().processInstanceId(
            processInstanceId).taskDefinitionKey(taskKey).list();
    return tasks;
}


public class SaveTaskActivityInstanceCmd implements Command<ExecutionEntity>,  
      Serializable {

    private TaskEntity newTask;
    private ExecutionEntity procInstEntity;

    public SaveTaskActivityInstanceCmd(TaskEntity newTaskInit,
            ExecutionEntity procInstEntityInit) {
        this.newTask = newTaskInit;
        this.procInstEntity = procInstEntityInit;
    }


    public ExecutionEntity execute(CommandContext commandContext) {
          ActivityImpl actImpl = new ActivityImpl(newTask.
                getTaskDefinitionKey(),
                procInstEntity.getProcessDefinition());
          actImpl.setActivityBehavior(new UserTaskActivityBehavior(
                new CdiExpressionManager(), newTask.getTaskDefinition()));
          ExecutionEntity execEntity = new ExecutionEntity();
          execEntity.setActivity(actImpl);
          execEntity.setActivityInstanceId(newTask.getTaskDefinitionKey()
                + ":" + newTask.getId());
          execEntity.setEventName(newTask.getEventName());
          execEntity.setProcessDefinitionId(newTask.getProcessDefinitionId());
          execEntity.setActive(true);
          execEntity.setProcessInstance(procInstEntity);
          commandContext.getExecutionManager().insert(execEntity);
          return execEntity;
     }
}

I appreciate any hint or advice :-)

Was it helpful?

Solution

I wouldn't mess with the process instance on that level, as you already noticed, you are bypassing camundas services. When faced with a similar problem, we went with the following:

  1. cancel the process instance of the old process version
  2. start a new instance of the extended process and forward it programmatically to the desired state ...

Another option: model an entry point (message start event) inside the new process version. Then, instead of programmatically forwarding the instance to the desired state, just start the new instance via the event and pass all process variables of the old instance ...

OTHER TIPS

Beginning with Camunda 7.3, you can use process instance modification to start any activity in a process and cancel any active activity instance.

Example:

runtimeService.createProcessInstanceModification(processInstanceId)
  .startBeforeActivity("someActivityId")
  .cancelActivityInstance("someActivityInstanceId")
  .execute();

See http://docs.camunda.org/7.3/guides/user-guide/#process-engine-process-instance-modification for documentation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top