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
-
t1
hands over the variables of p1
to the case execution ce1
- therefore it fetches the varialbe
filteredContent
of the case instance, so that t1
has an “own copy” of that variable
-
t1
changes the value of filteredContent
by setting the value of the variable anonymizedContent
of p1
-
t2
hands over the variables of p2
to the case execution ce2
- therefore it fetches the varialbe
filteredContent
of the case instance, so that t2
has an “own copy” of that variable
-
t2
changes the value of filteredContent
by setting the value of the variable anonymizedContent
of p2
-
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
-
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
- now
t1
is finished and starts to flush its changes to the database
-
t1
commits its changes
-
t2
is finished and starts to flush its changes to the database
-
t2
detects the concurrency conflict and an OptimisticLockingExcpetion
is thrown
For your example I would recommend you to
- 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.
- 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