Chapter 11. Context

Context is about process variables. Process variables are key-value pairs that maintain information related to the process instance. Since the context must be storable in a database, some minor limitations apply.

11.1. Accessing variables

org.jbpm.context.exe.ContextInstance serves as the central interface to work with process variables. You can obtain the ContextInstance from a ProcessInstance like this :

ProcessInstance processInstance = ...;
ContextInstance contextInstance = (ContextInstance) processInstance.getInstance(ContextInstance.class);

The most basic operations are

void ContextInstance.setVariable(String variableName, Object value);
void ContextInstance.setVariable(String variableName, Object value, Token token);
Object ContextInstance.getVariable(String variableName);
Object ContextInstance.getVariable(String variableName, Token token);

The variable names are java.lang.String. By default, jBPM supports the following value types:

  • java.lang.String
  • java.lang.Boolean
  • java.lang.Character
  • java.lang.Float
  • java.lang.Double
  • java.lang.Long
  • java.lang.Byte
  • java.lang.Short
  • java.lang.Integer
  • java.util.Date
  • byte[]
  • java.io.Serializable
  • classes that are persistable with hibernate

Also an untyped null value can be stored persistently.

All other types can be stored in the process variables without any problem. But it will cause an exception when you try to save the process instance.

To configure jBPM for storing hibernate persistent objects in the variables, see Storing hibernate persistent objects.

11.2. Variable lifetime

Variables do not have to be declared in the process archive. At runtime, you can just put any java object in the variables. If that variable was not present, it will be created. Just the same as with a plain java.util.Map.

Variables can be deleted with

ContextInstance.deleteVariable(String variableName);
ContextInstance.deleteVariable(String variableName, Token token);

Automatic changing of types is now supported. This means that it is allowed to overwrite a variable with a value of a different type. Of course, you should try to limit the number of type changes since this creates a more db communication then a plain update of a column.

11.3. Variable persistence

The variables are a part of the process instance. Saving the process instance in the database, brings the database in sync with the process instance. The variables are created, updated and deleted from the database as a result of saving (=updating) the process instance in the database. For more information, see Chapter 7, Persistence.

11.4. Variables scopes

Each path of execution (read: token) has its own set of process variables. Requesting a variable is always done on a token. Process instances have a tree of tokens (see graph oriented programming). When requesting a variable without specifying a token, the default token is the root token.

The variable lookup is done recursively over the parents of the given token. The behaviour is similar to the scoping of variables in programming languages.

When a non-existing variable is set on a token, the variable is created on the root-token. This means that each variable has by default process scope. To make a variable token-local, you have to create it explicitely with:

ContextInstance.createVariable(String name, Object value, Token token);

11.4.1. Variables overloading

Variable overloading means that each path of execution can have its own copy of a variable with the same name. They are treated as independant and hence can be of different types. Variable overloading can be interesting if you launch multiple concurrent paths of execution over the same transition. Then the only thing that distinguishes those paths of executions are their respective set of variables.

11.4.2. Variables overriding

Variable overriding means that variables of nested paths of execution override variables in more global paths of execution. Generally, nested paths of execution relate to concurrency : the paths of execution between a fork and a join are children (nested) of the path of execution that arrived in the fork. For example, if you have a variable 'contact' in the process instance scope, you can override this variable in the nested paths of execution 'shipping' and 'billing'.

11.4.3. Task instance variable scope

For more info on task instance variables, see Section 12.4, “Task instance variables”.

11.5. Transient variables

When a process instance is persisted in the database, normal variables are also persisted as part of the process instance. In some situations you might want to use a variable in a delegation class, but you don't want to store it in the database. An example could be a database connection that you want to pass from outside of jBPM to a delegation class. This can be done with transient variables.

The lifetime of transient variables is the same as the ProcessInstance java object.

Because of their nature, transient variables are not related to a token. So there is only one map of transient variables for a process instance object.

The transient variables are accessable with their own set of methods in the context instance, and don't need to be declared in the processdefinition.xml

Object ContextInstance.getTransientVariable(String name);
void ContextInstance.setTransientVariable(String name, Object value);

11.6. Customizing variable persistence

Variables are stored in the database in a 2-step approach :

user-java-object <---> converter <---> variable instance

Variables are stored in VariableInstances. The members of VariableInstances are mapped to fields in the database with hibernate. In the default configuration of jBPM, 6 types of VariableInstances are used:

  • DateInstance (with one java.lang.Date field that is mapped to a Types.TIMESTAMP in the database)

  • DoubleInstance (with one java.lang.Double field that is mapped to a Types.DOUBLE in the database)

  • StringInstance (with one java.lang.String field that is mapped to a Types.VARCHAR in the database)

  • LongInstance (with one java.lang.Long field that is mapped to a Types.BIGINT in the database)

  • HibernateLongInstance (this is used for hibernatable types with a long id field. One java.lang.Object field is mapped as a reference to a hibernate entity in the database)

  • HibernateStringInstance (this is used for hibernatable types with a string id field. One java.lang.Object field is mapped as a reference to a hibernate entity in the database)

Converters convert between java-user-objects and the java objects that can be stored by the VariableInstances. So when a process variable is set with e.g. ContextInstance.setVariable(String variableName, Object value), the value will optionally be converted with a converter. Then the converted object will be stored in a VariableInstance. Converters are implementations of the following interface:

public interface Converter extends Serializable {
  boolean supports(Object value);
  Object convert(Object o);
  Object revert(Object o);
}

Converters are optional. Converters must be available to the jBPM class loader

The way that user-java-objects are converted and stored in variable instances is configured in the file org/jbpm/context/exe/jbpm.varmapping.properties. To customize this property file, put a modified version in the root of the classpath, as explained in Section 6.3, “Other configuration files” Each line of the properties file specifies 2 or 3 class-names separated by spaces : the classname of the user-java-object, optionally the classname of the converter and the classname of the variable instance. When you refer your custom converters, make sure they are in the jBPM class path. When you refer to your custom variable instances, they also have to be in the the jBPM class path and the hibernate mapping file for org/jbpm/context/exe/VariableInstance.hbm.xml has to be updated to include the custom subclass of VariableInstance.

For example, take a look at the following xml snippet in the file org/jbpm/context/exe/jbpm.varmapping.xml.

    <jbpm-type>
      <matcher>
        <bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
          <field name="className"><string value="java.lang.Boolean" /></field>
        </bean>
      </matcher>
      <converter class="org.jbpm.context.exe.converter.BooleanToStringConverter" />
      <variable-instance class="org.jbpm.context.exe.variableinstance.StringInstance" />
    </jbpm-type>

This snippet specifies that all objects of type java.lang.Boolean have to be converted with the converter BooleanToStringConverter and that the resulting object (a String) will be stored in a variable instance object of type StringInstance.

If no converter is specified as in

    <jbpm-type>
      <matcher>
        <bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
          <field name="className"><string value="java.lang.Long" /></field>
        </bean>
      </matcher>
      <variable-instance class="org.jbpm.context.exe.variableinstance.LongInstance" />
    </jbpm-type>

that means that the Long objects that are put in the variables are just stored in a variable instance of type LongInstance without being converted.