Best way to store variables in multiple subprocesses

Hi everyone,

I’m using Camunda 7.5.0. In my BPMN, I have a multi-instance subprocess. Inside, there are user forms that are required to fill in.
For example a shipping taks with form values like ‘shipment date’, ‘shipment ref’ and ‘shipment comment’.

Sometimes though, it can be decided, to ship all material in the same box. These form values must then manually be entered in every single shipping task of the multi-instance subprocess. In rare cases, the number of tasks can go up to about 20 or 30 so the packager manager asked to place a button in the forms that, if pressed, copies all these variable values to the other subprocess instances. (via rest-request with values as JSON payload in the body)

I’ve mashed together some lines of code how I would deal with this with my limited knowledge of Camunda. My question now; is that this is the best way to proceed, or are there other, more robust solutions?

public void copyShipmentVariables(String processInstanceId, Date date, String comment, String reference) {
        // Get subProcess execution Ids
        List<String> subProcessExecutionIds = engineServices.getHistoryService().createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .activityType("subProcess")
                .list()
                .stream()
                .map(HistoricActivityInstance::getExecutionId)
                .filter(execId -> !processInstanceId.equals(execId))
                .collect(Collectors.toList());

        // Set variable values
        for (String subProcessExecutionId : subProcessExecutionIds) {
            engineServices.getRuntimeService().setVariables(subProcessExecutionId, Variables.createVariables()
                    .putValue(VariableConstants.SHIPMENT_DATE, date)
                    .putValue(VariableConstants.SHIPMENT_COMMENT, comment)
                    .putValue(VariableConstants.SHIPMENT_REFERENCE, reference));
        }
    }

I’m not sure I understand the question. What values are you trying to store? Are there “common” values between multiple sub-processes (e.g. SHIPMENT_REFERENCE) such that in a “shared” process variable environment, one sub-process might overwrite that value making it incorrect for another sub-process?

Are these true sub-processes or are they “child” processes invoked through a call activity?

All that said, one way to keep “track” of variables in multiple, possibly similar sub-processes would be to create a JSON object in the “parent” process. This object would have objects representing each sub-process and within that object would be the variables particular to that sub-process. Something like this:

{
	"subprocess_1": {
		"SHIPMENT_DATE": "2017-09-26 08:00:00",
		"SHIPMENT_COMMENT" : "First part of the order",
		"SHIPMENT_REFERENCE" : "1234546ABC"
	},
	"subprocess_2": {
		"SHIPMENT_DATE": "2017-09-27 14:00:00",
		"SHIPMENT_COMMENT" : "Second part of the order",
		"SHIPMENT_REFERENCE" : "456789XYZ"
	},
	"subprocess_3": {
		"SHIPMENT_DATE": "2017-09-28 13:15:00",
		"SHIPMENT_COMMENT" : "Final part of the order",
		"SHIPMENT_REFERENCE" : "098765FGH"
	}
}

You can then use Spin (JSONPath) to extract the appropriate values as needed. The key “subprocess_X” could be substituted with a process instance ID or other value that can be programmatically derived.

Yes so I have one of these things that loop over a collection and they run with a different executionId.

The scope of these shipment variables is different, so for every subProcess task, I can set a different variable value. It’s not a global process variable. Given this, storing it all in one object is not required for me.

@nvanbelle see the following example:

It has some examples of having a multi-instance that stores the consolidated data in a json array.

The thread got a little hijacked due to some misunderstandings, but my initial question is still stands. Anyone?

Hey Nico,

some comments on your code:

  1. The execution id selection only works if this is the only subprocess in your model. You can improve that by filtering by activity id.
  2. The code setting the variables should use #setVariablesLocal or else every #setVariable invocation overwrites the previously set variables.

If you want to avoid using history tables, an alternative approach is:

  1. Instantiate the variables in the subprocess scope via an input mapping (e.g. set them all to null)
  2. Use a VariableInstanceQuery to find all those variables by name
  3. For each returned variable instance, obtain the execution id and set the variable via runtime service

Cheers,
Thorben

Hi @thorben,

Thank you for your reply. Yes, I am instantiating all these variables in a start listener.

VariableMap initLocalVariablesMap = Variables.createVariables()
                .putValue(VariableConstants.PRODUCT_SERIAL_NUMBER, null)
                .putValue(VariableConstants.WO_PRODUCT_DEFINITION, null)
                .putValue(VariableConstants.INSTALLATION_DATE, null)
                .putValue(VariableConstants.SHIPMENT_DATE, null)
                .putValue(VariableConstants.RECEIVE_DATE, null)
                .putValue(VariableConstants.NO_INTERVENTION_DATE, null)
                .putValue(VariableConstants.PACKAGER_TEST_COMMENT, null)
                .putValue(VariableConstants.SHIPMENT_REFERENCE, null)
                .putValue(VariableConstants.SHIPMENT_COMMENT, null)
                .putValue(VariableConstants.ADDITIONAL_INSTALLATION_COST, null);

execution.setVariablesLocal(initLocalVariablesMap);

I changed my query to find all executionIds not from the historic tables like you proposed.

Set<String> subProcessExecutionIds = engineServices.getRuntimeService().createVariableInstanceQuery()
                .processInstanceIdIn(processInstanceId)
                .variableName(VariableConstants.SHIPMENT_REFERENCE)
                .list()
                .stream()
                .map(VariableInstance::getExecutionId)
                .collect(Collectors.toSet());

    // Set variable values
    for (String subProcessExecutionId : subProcessExecutionIds) {
        engineServices.getRuntimeService().setVariables(subProcessExecutionId, Variables.createVariables()
                .putValue(VariableConstants.SHIPMENT_DATE, date)
                .putValue(VariableConstants.SHIPMENT_COMMENT, comment)
                .putValue(VariableConstants.SHIPMENT_REFERENCE, reference));
    }

I’ll go ahead and implement something like this. Thank you all for your responses!