Passing variables to Activities using Parse Handlers

In an Activiti project I’m working on, we need to pass some variables in/out through the call activities. But there was a problem. We have too many processes and call activities in them. So managing those variables in all of them was a tedious task. Also missing one in or out tag will cause error in our execution.

The solution to this is adding in and out parameters on deploy. We’ve created some parse handlers extending from org.activiti.engine.parse.BpmnParseHandler and register it to the org.activiti.engine.ProcessEngineConfiguration. We are using org.activiti.spring.SpringProcessEngineConfiguration so I will go on with that but stand alone configuration will be similar.

You can learn how to define SpringProcessEngineConfiguration bean in your application using that guide.

First you need to define process configuration and process engine beans like this:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
</bean>
  
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
	<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

The dots will be filled soon.
If you are able to deploy and run your processes then we are ready to go. We need to implement our parser classes that will help us to register in and out parameters. Here I’ll implement a custom call activity parse handler for this, namely ParameterRegisteringCallActivityParseHandler.

The mechanism is simple:

package org.activiti.engine.impl.bpmn.parser.handler;
public class CallActivityParseHandler extends AbstractActivityBpmnParseHandler<CallActivity> {
  public Class< ? extends BaseElement> getHandledType() {
    return CallActivity.class;
  }
  
  protected void executeParse(BpmnParse bpmnParse, CallActivity callActivity) {    
    ActivityImpl activity = createActivityOnCurrentScope(bpmnParse, callActivity, BpmnXMLConstants.ELEMENT_CALL_ACTIVITY);
    activity.setScope(true);
    activity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createCallActivityBehavior(callActivity));
  }

}

Our code will be like this:

public class ParameterRegisteringCallActivityParseHandler extends CallActivityParseHandler {
	@Override
	protected void executeParse(BpmnParse bpmnParse, CallActivity callActivity) {
		//super.executeParse(bpmnParse, callActivity);
		ActivityImpl activity = createActivityOnCurrentScope(bpmnParse, callActivity, BpmnXMLConstants.ELEMENT_CALL_ACTIVITY);
		activity.setScope(true);
		ProcessDefinitionEntity currentProcessDefinition = bpmnParse.getCurrentProcessDefinition();
		initDefaultInOutParameters(currentProcessDefinition, callActivity);
		activity.setActivityBehavior(bpmnParse.getActivityBehaviorFactory().createCallActivityBehavior(callActivity));
	}
}

I will come to initDefaultInOutParameters later but I want you to notice commenting out super call. The trick here, Activiti does not let us to add in or out parameter passing definitions after calling super. You can check out the conversation between me and Joram Barrez here. We’ve just stepped in while the call activity is being constructed.

So what is about initDefaultInOutParameters? Here is the details:

private void initDefaultInOutParameters(ProcessDefinition processDefinition, CallActivity callActivity) {
	final Set<IOParameter> defaultInParameters = getDefaultInParameters();
	final List<IOParameter> activityInParameters = callActivity.getInParameters();
	activityInParameters.addAll(defaultInParameters);

	final Set<IOParameter> defaultOutParameters = getDefaultOutParameters();
	final List<IOParameter> activityOutParameters = callActivity.getOutParameters();
	activityOutParameters.addAll(defaultOutParameters);
}

private Set<IOParameter> getDefaultInParameters() {
	final Set<IOParameter> ioParameters = new HashSet<IOParameter>();

	final IOParameter currentCallActivityIdParameter = getCurrentCallActivityIdParameter();
	ioParameters.add(currentCallActivityIdParameter);

	final IOParameter someCustomParameter = getCustomParameter();
	ioParameters.add(someCustomParameter);

	return ioParameters;
}

private Set<IOParameter> getDefaultOutParameters() {
	final Set<IOParameter> ioParameters = new HashSet<IOParameter>();

	final IOParameter someCustomParameter = getCustomParameter();
	ioParameters.add(someCustomParameter);

	return ioParameters;
}

As you can see I’ve defined 2 default in parameters and 1 out. One of the in parameters is same with the out one. That means that parameter will be passed in and out for each call activity in the process definition. If you have nested call activities, then it will be passed to and from them too. You can update the value of it and then set to the execution, this parameter will passed out up and up until root execution and you will have the updated value on your root execution.

Implementation of those methods, getCurrentCallActivityIdParameter, getCustomParameter are like this:

private IOParameter getCurrentCallActivityIdParameter() {
	final String sourceExpression = "${execution.currentActivityId}";
	final String target = "CALL_ACTIVITY_ID_VARIABLE_NAME";
	return getIoParameterForSourceExpression(sourceExpression, target);
}

private IOParameter getCustomParameter() {
	final String sourceExpression = "${SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED}";
	final String target = "SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED";
	return getIoParameterForSourceExpression(sourceExpression, target);
}

protected IOParameter getIoParameterForSourceExpression(final String sourceExpression, final String target) {
	final IOParameter ioParameter = new IOParameter();
	ioParameter.setSourceExpression(sourceExpression);
	ioParameter.setTarget(target);
	return ioParameter;
}

First method, getCurrentCallActivityIdParameter, adds to your call activity definition this:

<activiti:in sourceExpression="${execution.currentActivityId}" target="CALL_ACTIVITY_ID_VARIABLE_NAME"/>

So when you deploy your process definition, it is added to the call activity definition. When the execution reaches to your call activity, this call activity’s id is got and written to the call activity’s execution context. That can be useful for logging.

Second method, notice it is called for in and out, adds to your call activity definition this:

<activiti:in sourceExpression="${SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED}" target="SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED"/>
<activiti:out sourceExpression="${SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED}" target="SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED"/>

When the execution hits to your call activity, a new execution context is created. There are 2 execution context at this time, one parent and one child(call activity). Activiti will try to find the variable named SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED and get its value. Beware, you should have a variable named SOME_VARIABLE_NAME_WHOSE_VALUE_WILL_BE_UPDATED even if its value is null, otherwise you will get NullPointerException. After getting its value, Activiti writes the value to child execution context with same name. After completing call activity, same is done but in reverse direction. You can update it in call activity or just leave as it is.

The final touch, org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl, which org.activiti.spring.SpringProcessEngineConfiguration is extending from has a property definition that lets us to register our parsers: customDefaultBpmnParseHandlers

So bean definition becomes this:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
	<property name="customDefaultBpmnParseHandlers">
		<list>
			<ref bean="parameterRegisteringCallActivityParseHandler"/>
		</list>
	</property>
</bean>
  
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
	<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

<bean id="parameterRegisteringCallActivityParseHandler" class="my.pkg.ParameterRegisteringCallActivityParseHandler"/>

Hope this helps if you face with something similar.