cancel
Showing results for 
Search instead for 
Did you mean: 

Difference between activiti:delegateExpression and activiti:class. Bug?

richardmward
Champ in-the-making
Champ in-the-making
Hi,

I have come across what I think may be a bug with the use of
activiti:delegateExpression
as an alternative to
activiti:class
on both
serviceTask
's and
taskListener
's.

When using
activiti:class
, one instance of a
JavaDelegate
  is created for each
serviceTask
in a
ProcessDefinition
. When using
activiti:delegateExpression
, one instance of a
JavaDelegate
, peroid. This is as expected.

The issue comes when you have two
serviceTask
's which use different
Expression
's. For example:

In the
JavaDelegate
:

@Component("processDefinitionBean")
public class ProcessDefinitionBean {
    private Expression requiredVariable;
    private Expression optionalVariable;
   …
}


In
bpmn1.bpmn20.xml



<serviceTask id="serviceTask" name="Service task" activiti:delegateExpression="${processDefinitionBean}">
  <extensionElements>
    <activiti:field name="requiredVariable">
      <activiti:expression>${requiredVariableValue}</activiti:expression>
    </activiti:field>
    <activiti:field name="optionalVariable">
      <activiti:expression>${optionalVariableValue}</activiti:expression>
    </activiti:field>
</serviceTask>



In
bpmn2.bpmn20.xml



<serviceTask id="serviceTask" name="Service task" activiti:delegateExpression="${processDefinitionBean}">
  <extensionElements>
    <activiti:field name="requiredVariable">
      <activiti:expression>${requiredVariableValue}</activiti:expression>
    </activiti:field>
</serviceTask>



Now, if an instance of
bpmn1
is started, when it reaches the
serviceTask
it will initialise all three of the
Expression
's in the
JavaDelegate
. If an instance of
bpmn2
is then started, when it reaches the
serviceTask
, the
requiredVariable
Expression
is being set to the value in
bpmn2
, but the
optionalVariable
Expression
is still set to the value from
bpmn1
. This is a pretty significant issue as it means that the
processDefinitionBean
is not in the state it expects to be in, and will either end up with a value it is not meant to have in it (should
bpmn2
happen to set
${optionalVariableValue}
somewhere along the way, or it will fall over because
optionalVariableValue
does not exist.

This isn't a problem when using
activiti:class
becuase there is one instance for each of the two
serviceTask
's, so there is no possibility that different combinations of
activiti:field
's can be set on the same isntance of a
JavaDelegate
.

For the time being, we are nullifying each of the
Expression
's at the end of the 
exectute
method in each of our
JavaDelegate
's - but I wonder if an official fix should be investigated (should this be considered a bug).
2 REPLIES 2

richardmward
Champ in-the-making
Champ in-the-making
I seem to be unable to edit the above post in which I have spotted a few errors - they are as follows:

  • "When using activiti:delegateExpression, one instance of a JavaDelegate, peroid." should read: "When using activiti:delegateExpression, one instance of a JavaDelegate <b>is created</b>, peroid."

  • The <code>JavaDelegate</code> code snippet should have <code>implements JavaDelegate</code>

  • "Now, if an instance of bpmn1 is started, when it reaches the serviceTask it will initialise all three of the Expression's in the JavaDelegate." should read: "Now, if an instance of bpmn1 is started, when it reaches the serviceTask it will initialise <b>both</b> of the Expression's in the JavaDelegate."


  • "For the time being, we are nullifying each of the Expression's at the end of the exectute method in each of our JavaDelegate's" - I am aware this may cause concurrency issues, and will be testing to prove that shortly. If anyone can suggest an alternative approach, it would be much appreciated!

richardmward
Champ in-the-making
Champ in-the-making
<b>Current resolution</b>

We have removed out nullifiying code in order to not cause concurrency issues and instead have used <code>@Scope(BeanDefinition.SCOPE_PROTOTYPE)</code> on the beans used as <code>JavaDelegate</code>'s. This allows a new bean to be created each time it is accessed - i.e. a new bean will be created for each and every instance of a <code>serviceTask</code>. Whilst this is slightly inefficient, we don't anticipate issues as looking through the Activiti code we see:

in <code>ServiceTaskDelegateExpressionActivityBehavior.execute(ActivitiExecution)</code>
<java>
// Note: we can't cache the result of the expression, because the
// execution can change: eg.
// delegateExpression='${mySpringBeanFactory.randomSpringBean()}'
Object delegate = expression.getValue(execution);
</java>

Following the references to <code>delegate</code> from this point onwards, shows it being used in the following line (when the <code>delegate</code> is an instance of <code>JavaDelegate</code>):
<java>
Context.getProcessEngineConfiguration().getDelegateInterceptor().handleInvocation(new JavaDelegateInvocation((JavaDelegate) delegate, execution));
</java>

And checking <code>handleInvocation</code> I can see that the invocations are not held onto, so Activiti will release the delegates after they have executed, and then we can leave it to the garbage collector to clear the objects up.