AnsweredAssumed Answered

Historic Process instance variable is out of date for serialized Java object

Question asked by pstapl on Dec 14, 2015
Latest reply on Dec 21, 2015 by pstapl
I am using Activit's forms to change the field values in a Serializable Java object as part of a process.
I have noticed that the historic process instance variables are often out of sync with the runtime version of the variables when the process has finished (and sometimes even during the process execution)

This causes a problem for me when I complete and submit the last form/user task in the process. The process ends and the only way to obtain the process variable is through the HistoryService API. This then returns an old version of the java object without the updates in the final form submission.

<!–break–>

Here is a simple testcase to demonstrate my problem. This demonstrates the problem in Activiti 5.19.0.

BPMN XML:


<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="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>
    <endEvent id="endevent1" name="End"></endEvent>
    <userTask id="usertask1" name="User Task">
      <extensionElements>
        <activiti:formProperty id="field1" name="Field1" type="string" expression="${myobject.field1}" required="true"></activiti:formProperty>
        <activiti:formProperty id="field2" name="Field2" type="string" expression="${myobject.field2}" required="true"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow>
    <serviceTask id="servicetask1" name="Service Task" activiti:class="com.example.mytest.MyServiceTask"></serviceTask>
    <sequenceFlow id="flow4" sourceRef="servicetask1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="User Task">
      <extensionElements>
        <activiti:formProperty id="field1" name="Field1" type="string" expression="${myobject.field1}" required="true"></activiti:formProperty>
        <activiti:formProperty id="field2" name="Field2" type="string" expression="${myobject.field2}" required="true"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow5" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <sequenceFlow id="flow6" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
  </process>
</definitions>


Referenced Service Task used to create an instance of MyBean and attach as a process instance variable:

package com.example.mytest;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;

public class MyServiceTask implements JavaDelegate {

   public void execute(DelegateExecution execution) throws Exception {
      execution.setVariable("myobject", new MyBean());
   }

}


The Java Bean being used.


package com.example.mytest;

import java.io.Serializable;

public class MyBean implements Serializable {

   /**
    *
    */
   private static final long serialVersionUID = 1L;

   private String field1;
   private String field2;

   public String getField1() {
      return field1;
   }

   public void setField1(String field1) {
      this.field1 = field1;
   }

   public String getField2() {
      return field2;
   }

   public void setField2(String field2) {
      this.field2 = field2;
   }

}


Finally the testcase to demonstrate the problem

package com.example.mytest;

import java.util.HashMap;
import java.util.Map;

import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.engine.test.Deployment;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;

public class ProcessTest {
   
   @Rule
   public ActivitiRule activitiRule = new ActivitiRule();
   
   @Deployment(resources = {"MyProcess.bpmn"})
   @Test
   public void test() {
      
      ProcessInstance pi = activitiRule.getRuntimeService().startProcessInstanceByKey("myProcess");
      
      Map<String, String> properties;
      Task task;
      MyBean myobject;
      
      properties = new HashMap<String, String>();
      properties.put("field1", "value1");
      properties.put("field2", "value2");
      
      task = activitiRule.getTaskService().createTaskQuery().processInstanceId(pi.getId()).singleResult();
      activitiRule.getFormService().submitTaskFormData(task.getId(), properties);
            
      properties = new HashMap<String, String>();
      properties.put("field1", "value3");
      properties.put("field2", "value4");
      
      task = activitiRule.getTaskService().createTaskQuery().processInstanceId(pi.getId()).singleResult();
      activitiRule.getFormService().submitTaskFormData(task.getId(), properties);
            
      Assert.assertEquals(0, activitiRule.getRuntimeService().createProcessInstanceQuery().count());
      myobject = (MyBean) activitiRule.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(pi.getId()).variableName("myobject").singleResult().getValue();
      Assert.assertEquals("value4", myobject.getField2());
   }

}


The result I get when running is:


org.junit.ComparisonFailure: expected:<value[4]> but was:<value[2]>
   at org.junit.Assert.assertEquals(Assert.java:115)
   at org.junit.Assert.assertEquals(Assert.java:144)
   at com.example.mytest.ProcessTest.test(ProcessTest.java:45)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
   at java.lang.reflect.Method.invoke(Method.java:497)
   at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
   at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
   at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
   at org.activiti.engine.test.ActivitiRule$1.evaluate(ActivitiRule.java:126)
   at org.junit.rules.RunRules.evaluate(RunRules.java:20)
   at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
   at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
   at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
   at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
   at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
   at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
   at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)



I would have expected the value of field2 to have been set to 'value4' in the historic process instance variable, after I had submitted the second form. Instead it appears not to have been updated from 'value2'. As the process is finished I cannot check in the runtime process instance to see if that was updated or not.

I'm not sure if I am doing something wrong or how I can get access to a correctly up-to-date version of the historic process instance variable.

Outcomes