Hi Kristian,
Part [1] of the docs tries to introduce the concept of scopes and where variables are placed in the hierarchy of scopes.
I'll try to give a concrete example: Imagine you have the scope structure as in [2]. Every blue box is a scope, that means it can hold variables during its lifetime. Scopes are hierarchical, meaning that every scope has a parent and possibly many children. There are different kinds of scopes: Executions and tasks.
Tasks are units of work that have to be executed by humans. Their lifetime is restricted to the duration of the BPMN user task instance. In [2], two human tasks are active which represent one scope each. This means, they can hold variables as long as they exist (i.e. until completion).
Executions are like tokens, they move through the process graph. As a rule of thumb, you have one execution for every active, concurrent path of activities in a process. Furthermore, you have parent executions that group executions together that originate from one path. In the process of [2], when the process is started, there is only one execution for the start event. When it reaches the parallel fork, it creates two child executions, Execution 2 and Execution 3, one for each outgoing branch of the parallel fork. The process instance is an execution itself and always the root execution in the hierarchy. Like tasks, executions are variable scopes, so they can hold variables.
Now you can use the process engine API to set variables.
Some examples:
- taskService.setVariablesLocal(taskId, variables): "Local" means the variable is always set on the task that is provided by "taskId". Not in any of the higher scopes. You can only use this method for tasks, not for executions.
- taskService.setVariables(taskId, variables): Each variable contained in "variables" is set in the highest possible scope. Highest possible means that for each variable, the scopes are traversed from the task scope upwards and either a variable of the same name is updated or a new variable instance is created in the topmost scope, which is the process instance. The latter is the regular case.
- runtimeService.setVariablesLocal(executionId, variables): The same as taskService.setVariableLocal but for executions instead of tasks. That is also why you get an exception when you supply a task id as argument.
- runtimeService.setVariables(executionId, variables): again the parallel construct to taskService.setVariables
- taskService.complete(taskId, variables): The same as runtimeService.setVariables for the execution that created the task. In addition, it tells the process engine to complete the
task and continue process execution. Variables you provide here do not get
lost as they are stored in an execution.
Now the getter methods:
- runtimeService.getVariables(executionId): Returns all variables that are visible from the execution provided. A variable is "visible", if it is defined in the same scope or on a higher scope. If you pass the process instance id as an argument, all variables are returned that are set on the process instance scope.
- taskService.getVariables(taskId): Similar to the one mentioned before, however starting at the task's scope. All variables are returned that are defined on the task or in any of its parent scopes.
So what is the hierarchal concept good for?
Consider [2]. You can separate variables between different paths of process execution. For example, you can define a variable with the same name but different value for Execution 2 and Execution 3. Still, you can share variables between both by defining them on Execution 1.
I hope this makes sense and clarifies the concept a little.
Cheers,
Thorben
[1]
http://docs.camunda.org/latest/guides/user-guide/#process-engine-process-variables-variable-scopes-and-variable-visibility[2]
http://docs.camunda.org/latest/guides/user-guide/assets/img/variables-4.png