cancel
Showing results for 
Search instead for 
Did you mean: 

HistoricVariableUpdate.getTaskId() always returns null

naag
Champ in-the-making
Champ in-the-making
Dear all!

I'm working on a Activiti 5.9 based platform (JBoss AS 7, Java EE 6, JSF 2, CDI, …) and would like to archive a process instance when it ends. This archive should include all process variables with their history information and also the tasks that were executed (history=full is set in activiti.cfg.xml). What I've done so far:

  • Setup a ExecutionListener named ProcessEndArchiveListener

  • Attached this listener to my test process

  • Call my class ProcessArchiver from the listener

  • Created a @BusinessProcessScoped bean which is referenced in JSF forms related to the test process

  • The process is started / completed using BusinessProcessBean.startTask() / .completeTask() with a conversation
When the process ends, the ProcessEndArchiveListener is called successfully, and in ProcessArchiver I use the HistoryService to retrieve a list of HistoricVariableUpdate, but the method HistoricVariableUpdate.getTaskId() always returns null. Also in the database table ACT_HI_DETAIL, I can see that TASK_ID_ is always null. Other information like the variable content and revision is available, also the processInstanceId and the executionId.

This is the process:


<?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: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="Test processes">
  <process id="test-process" name="Test process">
    <extensionElements>
      <activiti:executionListener event="end" class="foo.bar.ProcessEndArchiveListener"></activiti:executionListener>
    </extensionElements>
    <startEvent id="startevent1" name="Start" activiti:initiator="initiator"></startEvent>
    <userTask id="usertask1" name="Enter information" activiti:assignee="${initiator}" activiti:formKey="enter.jsf"></userTask>
    <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="Update information" activiti:assignee="${initiator}" activiti:formKey="update.jsf"></userTask>
    <sequenceFlow id="flow2" name="" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow3" name="" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
  </process>
</definitions>

The ExecutionListener:

public class ProcessEndArchiveListener implements ExecutionListener {

   public void notify(DelegateExecution execution) throws Exception {
      DestructibleBeanInstance<ProcessArchiver> processArchiverBean = null;
      try {
         processArchiverBean = BeanLookup.getBean(ProcessArchiver.class);
         ProcessArchiver processArchiver = processArchiverBean.getInstance();         
         processArchiver.archive(execution);
      } finally {
         if (processArchiverBean != null) {
            processArchiverBean.destroy();
         }
      }
   }   
}

The class used to access the HistoryService and do the archiving (this is where the taskId is null):


@ApplicationScoped
public class ProcessArchiver {

   @Inject
   protected ProcessEngine processEngine;

   public void archive(DelegateExecution execution) {
      HistoryService historyService = processEngine.getHistoryService();

      // …

      List<HistoricDetail> historicDetails = historyService.createHistoricDetailQuery()
            .processInstanceId(execution.getProcessInstanceId()).variableUpdates().list();
      List<HistoricVariableUpdate> historicVariableUpdates = new ArrayList<HistoricVariableUpdate>();
      for (HistoricDetail historicDetail : historicDetails) {
         if (historicDetail instanceof HistoricVariableUpdate) {
            // historicDetail.getTaskId() is always null here
            historicVariableUpdates.add((HistoricVariableUpdate) historicDetail);
         }
      }
      
      // …
   }
}

And also the @BusinessProcessScoped bean used in the process:


@Named
@BusinessProcessScoped
public class TestProcessBean implements Serializable {
   private static final long serialVersionUID = 1L;

   protected String title;
   protected String comment;

   // Getters & setters
}

Last but not least the activiti.cfg.xml:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!– lookup the JTA-Transaction manager –>
  <bean id="transactionManager" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:jboss/TransactionManager"></property>
    <property name="resourceRef" value="true" />
  </bean>

  <!– process engine configuration –>
  <bean id="processEngineConfiguration" class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
    <property name="dataSourceJndiName" value="java:/DB/Activiti" />
    <property name="databaseType" value="mysql" />
    <property name="transactionManager" ref="transactionManager" />
    <property name="transactionsExternallyManaged" value="true" />
    <property name="databaseSchemaUpdate" value="true" />
    <property name="history" value="full" />

    <property name="customPostBPMNParseListeners">
      <list>
        <bean class="org.activiti.cdi.impl.event.CdiEventSupportBpmnParseListener" />
      </list>
    </property>
  </bean>
</beans>

I've debugged this situation a bit, but was lost in the Activiti Engine code after a while. What I've found out is that there's a call to VariableScopeImpl.initializeVariableInstanceBackPointer() when completing a task, and since VariableScopeImpl is abstract, this will actually be a call to ExecutionEntity or TaskEntity. In this method, the executionId and the processInstanceId will be set, and also the taskId, but only in TaskEntity implementation.

But my VariableScopeImpl is always an instance of ExecutionEntity, never TaskEntity, and that's why (I think) the taskId is never set. Am I making a mistake while using @BusinessProcessScoped? Do I need to take any special measures to ensure that the taskId is maintained?

If there's no direct answer, I would also be happy about some pointers to debug this further 🙂

Many thanks for all your thoughts!

Regards,
Peter
7 REPLIES 7

trademak
Star Contributor
Star Contributor
Hi,

I don't understand your question. Why do you want a taskId in a HistoricVariableUpdate instance?
Why do you need this information? If you want an overview of user tasks then you should use the createHistoricTaskInstanceQuery method.

Best regards,

naag
Champ in-the-making
Champ in-the-making
Hi Tijs,

thanks for your answer and sorry for my late reply (I was on vacation  Smiley Happy).

For archiving and auditing purposes, I need to know the task that triggered the variable update. I need the taskId to see during which task a variable update actually happened. In my example above I have two user tasks "Enter information" and "Update information".  Imagine a situation during audit where the auditor needs to know who entered a particular piece of information in a process variable. If we could see the task during which a variable update took place, then we could also retrieve the information about people who were assigned to those tasks.

Is it clear?

Regards,
Peter

trademak
Star Contributor
Star Contributor
Hi,

Yeah that certainly makes sense. I don't think we store that info currently.
What you could do is check the update time with the start and end time of the tasks.
But this is certainly a valid improvement request. Could you raise a JIRA?

Best regards,

naag
Champ in-the-making
Champ in-the-making
Hi Tijs,

alright, I will raise the JIRA and also investigate if start/end time provide a viable solution.

Thanks for your input!

Regards,
Peter

naag
Champ in-the-making
Champ in-the-making
ACT-1248 has been created. Hopefully something can be worked out  Smiley Happy

frederikherema1
Star Contributor
Star Contributor
For the record: the TASK_ID_ field is only used when the HistoricDetail is task-local variable update. So for process-variables, this does NOT indicate which task was active at the time the process-variable was set. This field is the single indicator for task-local variables and cannot be reused for the proposed functionality.

However, the ACT_INST_ID_ can be inspected to see what activity was active at that time. Based on the ACT_ID_ you can at least know the ID of the task in your BPMN. Using this, you can query for the actual HistoricTaskInstance (or multiple, if task was repeated) associated to activity.

naag
Champ in-the-making
Champ in-the-making
The issue has been solved by following Frederik's advice. Now I have a custom SQL query via JDBC to the Activiti database, which joins ACT_HI_DETAIL with ACT_HI_ACTINST. From there I can read everything I need so far. Thanks 🙂