AnsweredAssumed Answered

Problems with pluggable Persistence layer

Question asked by byr on Sep 26, 2016

With Activiti 6.0 beta4 out I started to play again how I could switch the mybatis persistence layer with a custom one. Now the problems I had before seems to be resolved since the 6.0 beta2, but now I'm facing a new problem.

Given this simple workflow:

<process id="my-process" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="start" name="StartEvent"/>
    <userTask activiti:assignee="admin" activiti:exclusive="true" id="task1" name="First Task"/>
    <userTask activiti:assignee="admin" activiti:candidateUsers="admin" activiti:exclusive="true" id="task2" name="Second Task"/>
    <endEvent id="end" name="EndEvent"/>
    <sequenceFlow id="_6" sourceRef="start" targetRef="task1"/>
    <sequenceFlow id="_7" sourceRef="task1" targetRef="task2"/>
    <sequenceFlow id="_8" sourceRef="task2" targetRef="end"/>

and given this test case:

        ProcessEngine activitiRule = (ProcessEngine) context.getBean("mybatisProcessEngine");

        Deployment deployment = activitiRule.getRepositoryService().createDeployment()
                .name("Dynamic process deployment")
                .addInputStream("test.bpmn", this.getClass().getResourceAsStream("/test.bpmn"))

        ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");

        activitiRule.getRuntimeService().setVariable(processInstance.getId(), "variable1", "value1");
        activitiRule.getRuntimeService().setVariable(processInstance.getId(), "variable2", "value2");
        activitiRule.getRuntimeService().setVariable(processInstance.getId(), "variable3", "value3");

        List<Task> tasks = activitiRule.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).list();

        Assert.assertEquals(1, tasks.size());
        Assert.assertEquals("First Task", tasks.get(0).getName());
        Assert.assertEquals("admin", tasks.get(0).getAssignee());

        long count = activitiRule.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).count();

        Assert.assertEquals(1, count);

        activitiRule.getTaskService().setVariableLocal(tasks.get(0).getId(), "taskVar1", 5d);

        tasks = activitiRule.getTaskService().createTaskQuery().includeProcessVariables().includeTaskLocalVariables().list();

        Assert.assertEquals(1, tasks.size());
        Assert.assertEquals(3, tasks.get(0).getProcessVariables().size());
        Assert.assertEquals(1, tasks.get(0).getTaskLocalVariables().size());

        activitiRule.getFormService().submitTaskFormData(tasks.get(0).getId(), new HashMap<String, String>());

        tasks = activitiRule.getTaskService().createTaskQuery().includeProcessVariables().includeTaskLocalVariables().list();
        Assert.assertEquals(1, tasks.size());
        Assert.assertEquals("Second Task", tasks.get(0).getName());

Where the process engine is created with spring, and it uses a StandaloneProcessEngineConfiguration.
With MyBatis (default) persistence layer everything is fine.

However if I switch to my process engine configuration, where I swap the persistence layer to my custom one, not everything works.
Following line fails with an exception:

activitiRule.getFormService().submitTaskFormData(tasks.get(0).getId(), new HashMap<String, String>());

The exception says:
Programmatic error: no current flow element found or invalid type: null. Halting.

I debugged what happens, and I discovered, that using the mybatis persistence layer, the entities are not inserted into the database when they are passed to the DataManager, but stored in some cache, and are flushed later to the database, which results, that modifications on the entity after the insert has been called are persisted to the database.
Since I do not use any caching when the Insert on the DataManager is called, I insert the record to the database, and any modification to the entity afterwards are not persisted, since the UPDATE is never called on the DataManager.
This causes, that the "ActivityId" property of the ExecutionEntity(Impl) entity is always NULL in my case, since when the insert is called, this property is not yet set. But after setting it, the update never gets called, since the flushing happens later.
Different problem is, that the property "ActivityId" does not have a setter method, so if I manage to get the ActivityId into the database, I'm not able to read it back, since I cannot set this property on the ExecutionEntity.
So the root cause of the exception is, that the activityId is not set in the database, and whenever I read the executionEntity back using the findById method, I return NULL ActivityId, so the flow cannot be continued.

So questions:

1.) is this caching/entity saving behavior on purpose?

2.) if 1.) is yes, how can I switch the caching layer, or at least how can I get notified about flushing the cache, since then all inserted entities should be kept in memory before flushing, since modifications can occur on the inserted entity.
if 1.) is no, can we expect a correction in this behavior, that whenever an entity is passed to insert, it is safe to write to the database?

3.) would be nice when all entity property would have a setter-getter to be able to map between the database and the entity (eg. the activityId cannot be set on the ExecutionEntity, just with dirty reflection hacks)

Thank you very much!