Starting process instance when a prececessor process instance completes

Hello,

First time poster. Experienced C, C++, .Net, & Java developer. New to Camunda.

The application I am working on processes data “jobs”. A “job” represents a set of process variables that will be passed to a Camunda process instance to drive the workflow. These “jobs” may optionally be “nested”. Each “job” should run in its own process instance. Child “jobs” must wait for the parent “job” to finish before starting processing. We want to queue up all of the process instances and configure each child instance to wait on its parent.

I have two questions:

  • Is this use case supported in Camunda?
  • If it is, how can this be accomplished?

I have looked at messages and message correlation, but being new to the platform I am not sure that this approach will yield the desired results and have been unable to get a functioning prototype with the blocking behavior.

The following is prototype code that I have used to successfully start the jobs without the blocking behavior we are looking for:

    this.ProcessMessage(processingBatch, (processKey, arguments) -> runtimeService.startProcessInstanceByKey(processKey, arguments));

    private void processMessage(final BatchBase<?> processingBatch, MessageDelegate messageDelegate) {
        final Map<String, Object> variables = new HashMap<>();
        final BatchBase<?> next = processingBatch.emitState(variables);

        final ProcessInstance processInstance = messageDelegate.dispatch("My-Import", variables);
        if(next != null) {
            // TODO: The lambda expression needs to be changed to block this processinstance until the previous instance completes
            processMessage(next, (processKey, arguments) -> runtimeService.startProcessInstanceByKey(processKey, arguments));
        }
    }

My attempts to modify the above code and BPMN diagram to use message and correlation have been unsuccessful.

Currently using 7.15. Any guidance, thoughts, or input is greatly appreciated!

This may not be too complicted, depending on where and how you’d like to keep the queue of job.
I would suggest that you have one process model that works as a the queue, accepting the requests and holding the instances until it’s their turn. Then you can have another process that does the actual work.

Sorted it all out. I used an intermediate conditional event to get this working. In the variables, I included a variable named “next” containing the business key of the next process to run. The algorithm is able to string together the processes so that they run in the correct order.

CamundaProcess

With this, new process instance will block until a variable named “run” is set to true. With these pieces in place it was trivial to orchestrate the jobs into the correct order with blocking:

public void run(final ApplicationArguments args) {
        try {
            BatchBase<?> processingBatch = EnrichBatch.deserializeProcessingBatch("{\"filePackageId\":2, \"next\":{\"filePackageId\":3}}");

            processMessage(processingBatch, true, (arguments) -> runtimeService.createProcessInstanceByKey(processingBatch.getProcessKey())
                                                                                    .businessKey(processingBatch.getBusinessKey())
                                                                                    .setVariable("run", true)
                                                                                    .setVariables(arguments));
        } catch(Exception e) {
            LOG.error("An exception occurred", e);
        }
    }

    private void processMessage(final BatchBase<?> processingBatch, boolean top, MessageDelegate messageDelegate) {
        final Map<String, Object> variables = new HashMap<>();
        final BatchBase<?> next = processingBatch.emitState(variables, top);

        final ProcessInstantiationBuilder processInstance = messageDelegate.dispatch(variables);
        if(next != null) {
            processMessage(next, false, (arguments) -> runtimeService.createProcessInstanceByKey(next.getProcessKey())
                                                                          .businessKey(next.getBusinessKey())
                                                                          .setVariables(arguments));
        }
        processInstance.execute();
    }

Note how the run variable is injected into the first process instance only.

Finally, the release of each process occurs when a service task is invoked that is configured with a JavaDelegate that sets the “run” variable on the next process instance in the chain:

@Override
    public void execute(DelegateExecution delegateExecution) throws Exception {
        String next = (String)delegateExecution.getVariable("next");
        Integer filePackageId = (Integer)delegateExecution.getVariable("file-package-id");

        if(next != null) {
            ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(next).singleResult();

            runtimeService.setVariable(p.getId(), "run", true);
        }
    }

With this I have a functioning prototype that I can deploy into our solution.