cancel
Showing results for 
Search instead for 
Did you mean: 

Process end time not available in process end listener

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

In my Activiti 5.9 project (with CDI, history = full), I've setup a global execution listener for the process end event via a custom BPMN parse listener. Inside the event handler, I try to load the current process instance via the HistoryService, but I see that historicProcessInstance.getEndTime() is always null. That's although Activiti ProcessInstanceEndHandler calls historicProcessInstance.markEnded() before.

I believe the reason is that ProcessInstanceEndHandler does not flush it's changes to the DB, and HistoryService only works against the DB. Is that correct? Is there any official API to get the transient entities instead of what's stored in the database? As a workaround, I use the HistoricProcessInstanceManager retrieved via the CommandContext (I was inspired by ProcessInstanceEndHandler here), but I'm not sure if it's reliable / stable because of bypassing the API.

The same problem happens with the last executed user task (it also has no end time available when retrieved via HistoryService). I suppose this just occurs if it's the last task before the end event?

My CustomBpmnParseListener:


public class CustomBpmnParseListener implements BpmnParseListener {

   protected static ExecutionListener PROCESS_END_ARCHIVE_LISTENER = new ProcessEndArchiveListener();

   @Override
   public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) {
      processDefinition.addExecutionListener(ExecutionListener.EVENTNAME_END, PROCESS_END_ARCHIVE_LISTENER);
   }

   // …
}

Part of my original event handler ProcessEndArchiveListener:


HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
      .processInstanceId(execution.getProcessInstanceId()).singleResult();

List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery()
      .processInstanceId(execution.getProcessInstanceId()).list();

Part of my event handler ProcessEndArchiveListener with the workaround in place:


// Inspired by
// org.activiti.engine.impl.history.handler.ProcessInstanceEndHandler
HistoricProcessInstance processInstance = Context
      .getCommandContext()
      .getHistoricProcessInstanceManager()
      .findHistoricProcessInstance(execution.getProcessInstanceId());

HistoricTaskInstanceQueryImpl historicTaskInstanceQuery = new HistoricTaskInstanceQueryImpl();
historicTaskInstanceQuery.processInstanceId(execution.getProcessInstanceId());
List<HistoricTaskInstance> taskInstances = Context.getCommandContext().getHistoricTaskInstanceManager()
      .findHistoricTaskInstancesByQueryCriteria(historicTaskInstanceQuery, null);

Any advice on how to make this more solid would be much appreciated 🙂

Regards,
Peter
5 REPLIES 5

frederikherema1
Star Contributor
Star Contributor
Indeed, when the process-end listener is called the process flow itself is ended, but not yet marked as ended in the database. All changes in a single API-call (eg. completeTask() which causes the process to end) are flushed at the end of the call.

Using the HistoricProcessInstance manager is the way activiti handles entities internally. It's not part of the API and *could* be changed without backward-compatibility guarantees. However, this approach is stable and won't be changed any time soon. There have been talks/thoughts in the past to expose an API inside delegate/listener calls, but nothing concrete is planned, so this is the only way to get those values.

naag
Champ in-the-making
Champ in-the-making
Wow Frederik, you're really helpful here 🙂

I just noticed that the same problem happens with the HistoricVariableUpdates. I've looked for something comparable to the HistoricProcessInstanceManager and found the HistoricDetailManager, but it does not provide the queries that I require (I need to join ACT_HI_ACTINST). Of course I could loop over the result set, and execute multiple queries, but I'm afraid of bad performance.

Do you know of any safe way to flush the transient entities to the DB without affecting the rest of the system? E.g. call Context.getCommandContext().getDbSqlSession().flush() at the beginning of my end event handler? This way I could just use the HistoryService and custom JDBC queries to improve performance. I had some issues with that in the past, but maybe there's a trick to it?

Regards,
Peter

frederikherema1
Star Contributor
Star Contributor
Flushing changes before ending the command-context can be tricky and maybe break something else along the way, I don't recommend it. Can't you use the DelegateTask's variables itself in the end-listener (then they're not yet historic Smiley Wink)

Or, just a thought: Can't you add the logic in the layer on top of activity, that actually causes the process to complete?

naag
Champ in-the-making
Champ in-the-making
Oh that's a brilliant idea, I mean just doing the logic AFTER the process is completed… I will try it out ASAP. Guess I looked at it for too long and got stuck with Activiti, never thinking out of the box 😉 I suppose with @Specializes and BusinessProcess bean, it should be a piece of cake 🙂

Thanks again for the idea, I will post my solution once it's done!

Peter

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

I've now implemented it according to your idea and it works 🙂 What I did was to create a new class extending BusinessProcess (the CDI bean) and mark it with @Specializes so it's automatically hooked up. Then I override BusinessProcess.completeTask(), checking if the current process has ended, and if that's true I call to my logic.

Just for someone who might get into the same situation: finding out if a process has ended is not intuitive, since processInstance.isEnded() doesn't work (always returns false, I believe there's a ticket somewhere for that…). Instead what you can do is query the RuntimeService with the processInstanceId and check if you get back a ProcessInstance. If so, it didn't end yet.

One more thought though: I think for such cases it would be appropriate to have a event fired by Activiti after the process has really ended, not just while it's ending. I guess I will open a new ticket for that as a possible improvement.

Regards,
Peter