cancel
Showing results for 
Search instead for 
Did you mean: 

Multiple instances of a ServiceTask class?

jonathan1
Champ in-the-making
Champ in-the-making
My project uses a service task class implementing ActivityBehavior several times in multiple BPMN processes.  It looks to me that Activiti treats the service class as a singleton and only creates one instance of it, so that separate processes end up sharing state.

Is there a configuration option to create a fresh service task instance every time the task is executed, or should I just not store any state in the service task class?
6 REPLIES 6

ronald_van_kuij
Champ on-the-rise
Champ on-the-rise
or should I just not store any state in the service task class?

No, correct. as the documentation states, service tasks should be stateless

jbarrez
Star Contributor
Star Contributor
You can inject an expression. That expression will always be evaluated each time, if you evaluate it yourself in the service task.


To inject values that are dynamically resolved at runtime, expressions can be used. Those expressions can use process variables, or Spring defined beans (if Spring is used). As noted in Service Task Implementation, an instance of the Java class is shared among all process-instances in a service task. To have dynamic injection of values in fields, you can inject value and method expressions in a org.activiti.engine.delegate.Expression which can be evaluated/invoked using the DelegateExecution passed in the execute method.

<serviceTask id="javaService" name="Java service invocation"
  activiti:class="org.activiti.examples.bpmn.servicetask.ReverseStringsFieldInjected">
 
  <extensionElements>
    <activiti:field name="text1">
      <activiti:expression>${genderBean.getGenderString(gender)}</activiti:expression>
    </activiti:field>
    <activiti:field name="text2">
       <activiti:expression>Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}</activiti:expression>
    </activiti:field>
  </ extensionElements>
</ serviceTask>
The example class below uses the injected expressions and resolves them using the current DelegateExecution. Full code and test can be found in org.activiti.examples.bpmn.servicetask.JavaServiceTaskTest.testExpressionFieldInjection


public class ReverseStringsFieldInjected implements JavaDelegate {

  private Expression text1;
  private Expression text2;

  public void execute(DelegateExecution execution) {
    String value1 = (String) text1.getValue(execution);
    execution.setVariable("var1", new StringBuffer(value1).reverse().toString());

    String value2 = (String) text2.getValue(execution);
    execution.setVariable("var2", new StringBuffer(value2).reverse().toString());
  }
}

chetan5p
Champ in-the-making
Champ in-the-making
I have slightly different problem. I will explain using above example…
In my case I have multiple instances of service task which has two fields, say “Text1” and “Text2”, but both are mutually exclusive. In the first instance the value for Text1 will be passed while for the second instance the value for Text2 will be passed. Here the first instance works properly. (Text1= somevalue, Text2=null) but the second instance is causing problem. Value for Text1 is not supplied to the second instance still the expression “text1.getValue(execution);” returns a value which is applicable to first instance.

Is there any way to solve this problem. I mean a way to inject fields each time the instance is called or creating a new instance each time? Or any other way to solve this problem?
Thanks in advance.

jbarrez
Star Contributor
Star Contributor
Hmmm, i see. Could you provide a unit test that demonstrates this problem? Im actually unsure what the correct semantics should be by default.

schaumtier
Champ in-the-making
Champ in-the-making
I created an Jira issue with an attached unit test.

https://jira.codehaus.org/browse/ACT-2165

rohail
Champ in-the-making
Champ in-the-making

Hello I created two processes with two service tasks each as below

<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlnsSmiley Surprisedmgdc="http://www.omg.org/spec/DD/20100524/DC" xmlnsSmiley Surprisedmgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="myProcess" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<serviceTask id="servicetask1" name="Service Task" activiti:expression="${businessRules.checkBalance(execution)}"></serviceTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow>
<serviceTask id="servicetask2" name="Service Task" activiti:expression="${businessRules.addAccount(execution, context)}"></serviceTask>
<sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="servicetask2"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow3" sourceRef="servicetask2" targetRef="endevent1"></sequenceFlow>
</process>
</definitions>

businessRules is a bean I have configured in the activiti.cfg.xml file as below

<bean id="businessRules" class="com.company.BusinessRules" />

Note that there are two service tasks in this process definition using expression to call method on the same bean. I also created a second process called "otherProcess" which is a mirror image of this process. I then ran both processes one after the other using the code below.

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
.addClasspathResource("MyProcess.bpmn20.xml")
.deploy();

repositoryService.createDeployment()
.addClasspathResource("OtherProcess.bpmn20.xml")
.deploy();

Map<string, object=""> variables = new HashMap<string, object="">();
variables.put("context", new WorkflowContext());
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess", variables);

Map<string, object=""> variables2 = new HashMap<string, object="">();
variables2.put("context", new WorkflowContext());
ProcessInstance processInstance2 = runtimeService.startProcessInstanceByKey("otherProcess", variables2);

processInstance2 = runtimeService.createProcessInstanceQuery().processDefinitionKey("otherProcess").singleResult();
System.out.println("Is Ended = " + processInstance.isEnded() + " Current Task: " + processInstance.getActivityId());

What I notice is that an instance of BusinessRules bean is only created once. I put a println message in the constructor of the BusinessRules and I see that it is hit only once across both processes, across the calls to the two service tasks in each process. Is this the expected behavior or should the BusinessRules bean be instantiated once per process, or once per service task? Thanks in advance for the clarification.