Aggregating back data from multi-instance call activity

Hi,

What is the recommended way of aggregating back data from multi-instance call activity?

In our particular case, objects are passed to the call activity and some operations may or may not be performed based on that object.
We need to be able to later know and act based on what actions were performed to each separate object.
I thought about Input, Output parameters, but the “target” field accepts a fixed value which means that each finishing instance is going to override the value. Also i was using the Flowable Eclipse Designer which had Target Expression field, which seems to not be supported by the engine.

Best regards,
Petko Boyadzhiev

Hi Petko,

When multi-instance task is driven by collection, I would use elements of this collection to store output values too. You can access collection element through elementVariable.

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="true"
     flowable:collection="${myService.resolveUsersForTask()}" flowable:elementVariable="assignee" >
  </multiInstanceLoopCharacteristics>
</userTask>

Regards
Martin

Hi,

Sorry for not being clear enough in my question. We need to access that information from outside the multi instance call activity, after it ends.
Does your answer mean that the collection variables’s changes are propagated to the paren that started the activity?

Best regards,
Petko Boyadzhiev

Hi,

Yes. :slight_smile:
One possibility of doing that is:

    @Test
@Deployment(resources = "org/flowable/engine/test/bpmn/multiinstance/MultiInstanceTest.simpleMultiInstanceWithCollectionVariableChange.bpmn20.xml")
public void testChangeCollectionElement() {
    Map<String, Object> vars = new HashMap<>();
    ArrayList<Integer> elements = new ArrayList<Integer>();
    elements.add(4);
    elements.add(5);
    elements.add(6);
    vars.put("elements", elements);

    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("simple_multi", vars);

    assertThat(runtimeService.getVariable(processInstance.getId(), "elements"),
       is(Arrays.asList(1, 2, 3))) ;
}

process model:

<definitions>
    <process id="simple_multi" name="Test Multi" isExecutable="true">
        <startEvent id="start" name="Start"/>
        <endEvent id="end" name="End"/>
        <scriptTask id="multi" name="Multi" scriptFormat="groovy">
            <multiInstanceLoopCharacteristics
                    isSequential="false"
                    flowable:collection="elements"
                    flowable:elementVariable="element"/>
            <script>
                tempElements = execution.getVariable("elements");
                tempElements.set(loopCounter, loopCounter+1);
                execution.setVariable("elements", tempElements);
            </script>
        </scriptTask>
        <receiveTask id="wait"/>
        <sequenceFlow id="flow1" sourceRef="start" targetRef="multi"/>
        <sequenceFlow id="flow2" sourceRef="multi" targetRef="wait"/>
        <sequenceFlow id="flow3" sourceRef="wait" targetRef="end"/>
    </process>
</definitions>

You have to add call activity behaviour there.

Regards
Martin

Hi,
I was not able to reproduce your results with our call activity approach. Also i needed to include inheritVariables or to specify the collection and the loopVariable as “in” parameters.
Is your example working only with script tasks?

In our case, this is how the main process calls the multi-instance call activity.

<startEvent id="startEvent1"></startEvent>
<serviceTask id="task1" name="task 1" flowable:async="true" flowable:class="parallel.flowable.test.SetMessageTask"></serviceTask>
<endEvent id="sid-A3712D0A-C601-48B0-B7A8-98E76F0F6BDE"></endEvent>
<callActivity id="task2" name="sub" flowable:async="true" calledElement="subProcess" flowable:calledElementType="key" flowable:completeAsync="true" flowable:inheritVariables="true">
  <multiInstanceLoopCharacteristics isSequential="false" flowable:collection="messages" flowable:elementVariable="message">
  </multiInstanceLoopCharacteristics>
</callActivity>
<sequenceFlow id="sid-EF20AD70-6619-44C1-94F3-97C23A25B6FD" sourceRef="startEvent1" targetRef="task1"></sequenceFlow>
<sequenceFlow id="sid-132893E5-DA76-4224-ACFC-3E49D552B729" sourceRef="task1" targetRef="task2"></sequenceFlow>
<serviceTask id="showMessage" name="show" flowable:async="true" flowable:class="parallel.flowable.test.ShowMessageTask"></serviceTask>
<sequenceFlow id="sid-C3A52B8D-C69B-4A46-947D-496DD222EEC7" sourceRef="task2" targetRef="showMessage"></sequenceFlow>
<sequenceFlow id="sid-62AAC348-6D32-4530-97EA-A27752D5F36A" sourceRef="showMessage" targetRef="sid-A3712D0A-C601-48B0-B7A8-98E76F0F6BDE"></sequenceFlow>
</process>

And this is the call activity.

<process id="subProcess" name="subProcess" isExecutable="true">
<startEvent id="startEvent1"></startEvent>
<endEvent id="sid-A3989074-F58B-4088-BE1A-49869B031D69"></endEvent>
<serviceTask id="stepUpdate" name="stepUpdate" flowable:async="true" flowable:class="parallel.flowable.test.DisplayMessageAndUpdate"></serviceTask>
<sequenceFlow id="sid-F2780EF8-BF63-43E6-875F-FFD8F7884B66" sourceRef="startEvent1" targetRef="stepUpdate"></sequenceFlow>
<sequenceFlow id="sid-15B07519-8103-4A87-B888-06166E82D371" sourceRef="stepUpdate" targetRef="sid-A3989074-F58B-4088-BE1A-49869B031D69"></sequenceFlow>
Small app showing our case can be found here: [Link to repo](https://github.com/petko-boyadzhiev/flowable-multi-instance-activity) To start it, just run the unit test.

Best regards,
Petko Boyadzhiev

1 Like

Hi Petko,

the jUnit test is fine. The only problem there is that it does not pass.

Setting messages [0, 1, 2]
Updating messages: [0Changed, 1, 2]
Updating messages: [0, 1, 2Changed]
Updating messages: [0, 1Changed, 2]
NEW messages: [0, 1, 2]

The messages are not updated after each iteration. The problem is that messages are set on the subProcess execution and these changes are not propagated to the main process.
You can change the logic in DisplayMessageAndUpdate.java and address main process execution and variables, but it will make code little bit complicated.
I would propose to

  1. make a call to subprocess with in/out variable messages
  2. apply multiinstance behaviour in the subprocess service task

One more tricky thing there is optimistic locking exception. When 2 threads in parallel try to update one variable (in our case messages) OptimistickLockingException is thrown. You can create new output variable with new name for each iteration, or make updates sequentially.
See the changes in the commit:

Output:

Setting messages [0, 1, 2]
Updating messages: [0Changed, 1, 2]
Updating messages: [0Changed, 1Changed, 2]
Updating messages: [0Changed, 1Changed, 2Changed]
NEW messages: [0Changed, 1Changed, 2Changed]

Regards
Martin

Hi, Petko,
we have similar problem, did you manage to find a solution?