Local Variable scope - single task scoped or process transaction scope?

1,587 views
Skip to first unread message

Alfonso Mateos Alarcón

unread,
Sep 30, 2014, 12:06:26 PM9/30/14
to camunda-...@googlegroups.com
Hi guys, I've got a question now...

Let's suppose I've got the following process flow:

  UserTask1 -> ServiceTask2 -> ServiceTask3 -> UserTask4

If I set a local variable to UserTask1, or to ServiceTask2, will that variable be available until the instance variables are persisted back into the engine? That would mean that it would also be available for ServiceTask3, but would be discarded at UserTask4, when the engine is about to persist every single variable and waits for a new "complete task" action.

Or does it long just for the single task I define the local variable for? (i.e. for UserTask1, or for ServiceTask2 if i set that local variable for that serviceTask)
Thanks in advance ;-)

thorben....@camunda.com

unread,
Sep 30, 2014, 12:19:50 PM9/30/14
to camunda-...@googlegroups.com
Hi Alfonso,

it depends on what you refer to by "local variable". I will describe two cases:

1): During UserTask1, a task is created, let us call it taskInstance1. If you set a local variable on taskInstance1, it is removed when the task is removed which is when it gets completed and UserTask1 finishes.

2): During UserTask1, you set a local variable on the execution that executes UserTask1. In a simple sequential process, like you describe, this would be the process instance execution but in more complex scenarios, this could be an execution further down the tree. The variable will get removed, when the execution it belongs to is removed. In the process you describe, this would happen after UserTask4.

To generalize, a "local variable" means that it is attached to a certain scope (e.g. execution or task instance). So the variable is local for the scope it is set on. Therefore, its lifetime is coupled to the lifetime of the scope. This of course varies by the scope type and the process constructs you use. Also, this behavior is independent of the points at which the engine persists the variables. [1] provides a visualization of this idea, although it does not explicitly state the last point regarding the variable's lifetime.

I hope this helps you in your understanding.

Cheers,
Thorben

[1] http://docs.camunda.org/latest/guides/user-guide/#process-engine-process-variables-variable-scopes-and-variable-visibility

Alfonso Mateos Alarcón

unread,
Oct 1, 2014, 4:38:46 AM10/1/14
to camunda-...@googlegroups.com
Hi Thorben, thanks a lot :-)

Could you give me some hint on how to use this method in my process?
taskService.setVariableLocal(taskId, variable, value)

This is the part of my process where I think it could be useful to use that local variable:

What I am doing so far is:
- Passing a complex DTO as a variable in the startProcessInstanceByKey
- Then inside the "Save Recruiting Task" I read that variable, process the DTO inside and remove that variable from my instance.

I tried something like this, but it's obvious that it's usefulness because it reaches the java delegate for save recruiting task before setting my local variable:

String processInstanceId = runtimeService.startProcessInstanceByKey(RECRUITING_PROCESS_KEYrecruitingVariables.getVariablesMap()).getProcessInstanceId();

 Map<String, Object> localVariables = new HashMap<String, Object>();

 localVariables.put(Recruiting.VARIABLE_RECRUITINGPERSONDTO, createRecruitingPersonDtoWithoutIds());        taskService.setVariablesLocal(taskService.createTaskQuery().processInstanceId(processInstanceId).taskDefinitionKey("tskSaveRecruiting").singleResult().getId(), localVariables);

But I cannot figure out how to set that local variable on a serviceTask (not a userTask) whitout starting the process.... but if I start it, then the serviceTask will be reached with no chance to set that local variable...

May be I'm missing some important point here...

Thanks in advance ;-)

thorben....@camunda.com

unread,
Oct 1, 2014, 8:05:08 AM10/1/14
to camunda-...@googlegroups.com
Hi Alfonso,

When you start a process instance and set variables on it, the following happens (in the process snippet from the image that you posted):
1. A new ProcessInstance is created. Internally, a ProcessInstance object is in fact an Execution and Execution is a variable scope, that means it can hold variables. VariableScopes are generally organized in a hierarchical structure and for a process instance, the root of them is the newly created Execution. Let us call this Execution e1. At this point, no other scope exists in the tree.
2. When e1 is created, the variables are set on it. They become also "local" variables of e1. This is reflected in the database where the variables directly reference e1.

Now let's say the process reaches the "Save Recruiting" task. Then e1 is used to execute the task and is also the object that is passed to your java delegate in the execute(..) method. In your delegate, you could now call execution.setVariable("aVar", "aValue") or execution.setVariableLocal("aVar", "aValue"). Both calls would result in a new variable being created, directly referencing execution e1. So no difference between the methods when the process instance is represented by a single execution.

Now the process reaches task "Review Recruiting". A new task instance is created. Here, "task instance" means an instance of org.camunda.bpm.engine.task.Task, not the process model element that is also called "task" or "userTask" or "serviceTask" etc. There is no concept of local variables of a "serviceTask".
This task is added to the hierarchy of variable scopes and in fact as a child of the execution it was created by. Let us call this task instance t1. Then we have a scope hiearchy that looks like the following:

e1 <--- t1

where the arrow reflects a parent-child relationship. The parent-child relationship means that in the task scope (i.e. t1), all local variables of t1 but also all variables from e1 are visible (e.g. if you call taskService.getVariables(..)). The other way around, the local variables of t1 are not visible to e1 (e.g. if you call runtimeService.getVariables(..)).

At this point, you can set a local variable on the task. As soon as the task is removed (i.e. completed by the user), the task is removed from the database and so are all its local variables. So if you need these variables later on, you should not set a local variable. Instead, if you set a non-local variable (i.e. by taskService.setVariable(..)), the variable is set on e1 (or in general always on the highest possible scope) and therefore remains accessible later on in the process.

I hope this makes sense to you, as its quite a complex matter. To wrap this up: I think you do not need local variables here. Just use the *setVariable() methods. The *setVariableLocal() methods are especially of interest when you want to limit the visibility of variables, for example in cases where there is a parallel gateway and you want some variables to be only visible to one branch of execution but not the other.

Regarding your code: I am not quite sure what you want to achieve but if you want to set variables in the service task, you should do that in your delegate.

Feel free to ask for further explanations.

Cheers,
Thorben

Alfonso Mateos Alarcón

unread,
Oct 1, 2014, 9:55:00 AM10/1/14
to camunda-...@googlegroups.com
Hi Thorben,

Your explanation is really clear, got it!

The purpose I want to solve is the following: 


2.- In my process diagram, when I start a new instance, I pass a DTO (Data Transfer Object, it's really a POJO) with several values inside. The goal is that this DTO is processed inside SaveRecruitingTask: it picks up these values, saves them into the database and sets the resulting entities Id's as instance variables, and then the DTO is discarded and removed from the instance variables. 

Since (1) needs an accessor (getter/setter) for this DTO in order to access that instance variable a reference to my DTO's maven module is needed. I thought I could get rid of that reference by avoiding to create an instance variable for this DTO, maybe by using local variables, or task variables, but I see that I cannot use it with serviceTasks, because I just get a new task instance for userTasks, when the engine is waiting for this task to be "completed".

If I can avoid references from my DataAccessor to my DTO's modules then I could put all of my DTO's accessors in a more "commons" module, so that those accessors would be easier to be used from my JavaDelegate tasks, from my Camel processors, and from my liferay JSF frontend...

Maybe I would need a userTask as a first task in my process in order to be able to use my DTO as a local variable inside a listener attached to it? Or maybe this is not a good usage for this situation?

One reason to pass this DTO with several fields of info was that even when my process instances didn't progress to further tasks it would be easy to have some info to be retrieved from my process instance in order to be rendered in a grid showing my in-progress instances with some user-friendly info...

Thanks in advance, I hope that I didn't bother you too much with this explanation about my goal when using local variables.

Best regards :-)

thorben....@camunda.com

unread,
Oct 1, 2014, 12:00:38 PM10/1/14
to camunda-...@googlegroups.com
Hi Alfonso,

I am sorry, but I have some understanding issues with your scenario.

I understand that you want to implement the data accessor pattern which says that you create a strongly-typed getter method for your DTO to access it in the service task. And you want to avoid a dependency from your data accessor module to your DTO module, while it is ok for you that the module that contains JavaDelegates has a dependency on the DTO module.
 
What I do not understand is why this would make it easier to use the DTO module from your Java delegates etc, because in the end, your JavaDelegate also uses the data accessor or am I mistaken? Or what are you using the data accessor for?

Sorry for my confusion and best regards,
Thorben

Alfonso Mateos Alarcón

unread,
Oct 1, 2014, 12:19:21 PM10/1/14
to camunda-...@googlegroups.com
Hi Thorben, it would be easier just because it's using it strongly-typed through the data accessor, that's the only reason... 
The rest of your understading is right... And I wondered if could get rid of that DTO in my instance variables by using local variables...

Alfonso Mateos Alarcón

unread,
Oct 2, 2014, 7:16:35 AM10/2/14
to camunda-...@googlegroups.com
Hi Thorben, do you see the way (or at least the convenience) to use a local variable (maybe with task scope) for that DTO? May be I could:

1.- start
2.- set that DTO as a local variable of userTask1
3.- complete userTask1 + listener (processing that local variable DTO).
4.- let the process go ahead with serviceTask named "Save Recruiting Task", and so on.

That is the only way I am able to think of in order to avoid to have to include my DTO inside my data accessor...

Or is it more convenient to keep the process as it is now, and just pass everything at the start and then have a serviceTask, without any userTask in between?

What do you think?
Thanks in advance!

thorben....@camunda.com

unread,
Oct 6, 2014, 5:37:53 AM10/6/14
to camunda-...@googlegroups.com
Hi Alfonso,

First of all sorry for my late response. On Thursday I was on holiday and Friday was a public holiday in Germany.

If the only purpose of having userTask1 is to set local variables I would not recommend you to do that.
With the userTask + listener option you describe, you would have to cast the variable to the DTO in the listener, correct? What is the difference if you do the same thing in the "Save Recruiting Task Delegate"? Of course, you would not have strongly-typed variable access there. You could also build this with an execution listener on the start event, process the variable and afterwards remove it. Or is there anything wrong with the DTO being a non-local variable? I do not yet see why this would force you to have the class accessible in your data accessor.

Cheers,
Thorben

Alfonso Mateos Alarcón

unread,
Oct 7, 2014, 11:56:13 AM10/7/14
to camunda-...@googlegroups.com
You are right, Thorben... I will have to access that variable somehow... But I thought that maybe could be a good idea to keep a DataAccessor for those variables that are instance-scoped (since they are supposed to be read/written more than once) and keep that DTO as a local variable, at the price of having to read it without a strongly-typed access, and just from one single JavaDelegate.

I understand that If I attach a listener to consume that DTO and remove it (that was done in Save Recruiting Task) then I suppose that i will need to rearrange the process flow, setting the code inside that serviceTask as a Listener for some User Tasks... I don't see any problem to do so, but that "Save Recruiting Task" has a compensation task attached to it now...

Thanks a lot, Thorben, really!
Reply all
Reply to author
Forward
0 new messages