CMMN process task out variable mapping - local?

Is there a way to map out variable from a called process to a local variable of the calling task?

Using the out variable mapping results in the variable being stored in the calling case instance. However I want to store it as a local variable of the calling case execution (the process task). Although modeler has checkbox “local” it doesn’t seem to have any effect and in documentation “local” is only mentioned with “in” mapping.

Hi @jaroslav.muller,

No, it is not possible to map out variables from a called process to a local variable of the calling task.

Whenever a case execution completes (or terminates) this case execution will be deleted. As a consequence, local variables of the completing case execution will also be deleted.
It does not make sense to set the out variables locally on the case execution representing the process task, because when the process completes the case execution will be completed too, so that the local variables would be deleted right away.

I am going to create an issue that the properties panel does not offer the “local” flag for out variables.

What is your use case? Why do you need the variables locally?

Cheers,
Roman

I actually wanted to use an execution listener on the process task itself that would decide what to do with the output of the process.

The reason I wanted to do this is that the process task is repeatable (and auto started) and I wanted to collect the results of executed processes into a list. So if I let it to save the process output into a global variable (say OUT) and then tried to append it into the list (say LIST) via execution listener EL, I couldn’t guarantee that another instance of the same process task wouldn’t finish and overwrote OUT before EL would put OUT into LIST.
What I have here is a race condition problem. (Is it understandable?)

So, while I believe there is some deterministic order listeners (and other things) happen in, I think the engine doesn’t run in one thread only, or does it?

Having had local out mapping I would simply store the output into a local variable of the process task and using execution listener appended it into the global list variable. No race conditions involved.

Hi @jaroslav.muller

in version 7.6 you will be able to define target scope in delegate, would that be enough for you? https://app.camunda.com/jira/browse/CAM-3685

Cheers,
Askar

Hi @jaroslav.muller,

Thanks for your further information.

How does you process look like? Does the process contains for examples a user task or asyncBefore or asyncAfter flag? What kind of repetition rule are you using? Has the process task any entry criteria? Could you share your process and case xml?

Are you aware of the concept of OptimisticLockingException?

Cheers,
Roman

Hi Roman,
I’m attaching an example case&process. Don’t take it too literary, it’s very artificial.
We are currently in a proof of concept stage, so we are experimenting with such artificial cases.

The point of this example is that for any incoming mail (that we identify as related to this case instance) our code manually starts “Handle incoming email” task. The repetition rule is simply ${true} on manualStart event, as we need to always have one enabled instance.
The task will execute handle_email process which should result into either discarding the mail or providing an anonymized version of the mail.
After completing the process the anonymized mail would be put in a queue for later processing (via execution listener on the process task).

The handle_mail process is intentionally put into a separate bpmn process because

  • it seems to be more suitable tool
  • it can be replaced by a different (more sofisticated or customer tailored) process for filtering mails

My plan was to use a spring bean as a service for all the service tasks, listeners etc.
No, I’m not aware of OptimisticLockingException. No sure why you ask, but I planned to solve all synchronization issues in java code inside the spring service beans.
And no, I’m not using any async flags.

(Sorry, the forum will allow me only one attachment in post. I am attaching the bpmn file in the next post)
diagram_1.cmmn (2.5 KB)

BR
Jaroslav

Hi Roman,
here goes the bpmn:
handle_email.bpmn (5.9 KB)

Jaroslav

Hi Jaroslav,

Thanks for sharing your example.

The engine uses the Optimistic Locking for concurrency control. If a concurrency conflict is detected an OptmisticLockingException is thrown and the transaction is rolled back. Conflicts are detected when UPDATE or DELETE statements are executed. For further details see 1
If we have in your scenario two threads t1 and t2, whereby t1 completes the called process instance p1 and t2 completes the called process instance p2. The completition happens in parallel. Then

  1. t1 hands over the variables of p1 to the case execution ce1
  2. therefore it fetches the varialbe filteredContent of the case instance, so that t1 has an “own copy” of that variable
  3. t1 changes the value of filteredContent by setting the value of the variable anonymizedContent of p1
  4. t2 hands over the variables of p2 to the case execution ce2
  5. therefore it fetches the varialbe filteredContent of the case instance, so that t2 has an “own copy” of that variable
  6. t2 changes the value of filteredContent by setting the value of the variable anonymizedContent of p2
  7. t1 executes the (complete) case execution listener of ce1 in order to add the filteredContent to the case instance variable list, therefore t1 has also its own copy of the variable
  8. t2 executes the (complete) case execution listener of ce2 in order to add the filteredContent to the case instance variable list, therefore t2 has also its own copy of the variable
  9. now t1 is finished and starts to flush its changes to the database
  10. t1 commits its changes
  11. t2 is finished and starts to flush its changes to the database
  12. t2 detects the concurrency conflict and an OptimisticLockingExcpetion is thrown

For your example I would recommend you to

  1. set the variable filteredContent locally inside a case execution listener on event create. So, when the called process instance completes the value of anonymizedContent is set locally on the corresponding caseExecution and as a global case instance variable.
  2. set the ServiceTask Anonymize to be async (for example to asyncAfter). That means when the engine reaches this point, the changes will be flushed and committed to the database and a job to continue the process is created. Then the JobExecutor executes the created job. If an OptimisticLockingException occurs, this exception is handled automatically by using retries.

Does it help you?

Cheers,
Roman

Roman,
I have just tested it - I didn’t know that if a local variable exists on the process task, the output mapping will set the value into the local variable (instead of the global one).
That is exactly what I wanted to achieve.

Great! Thanks a lot
Jaroslav