AnsweredAssumed Answered

NullPointerException on TimerBoundaryEvent trigger

Question asked by sckempgowda on Aug 14, 2018
Latest reply on Sep 6, 2018 by bassam.al-sarori

Hi, 

 

We recently upgraded from Activiti 5.18 to 6.0.0.

 

We had a requirement to capture Activiti history only for certain workflow types. As per the suggestion from Activiti team, we had overridden the org.activiti.engine.impl.history.DefaultHistoryManager to provide overridden implementation only for the method isHistoryLevelAtLeast(HistoryLevel level)

 

The suggested code changes were done as follows

 

import org.activiti.engine.ProcessEngine;

import org.activiti.spring.SpringProcessEngineConfiguration;

import com.abc.dms.activiti.persistence.DMSHistoryManagerSessionFactory;

   public class CustomSpringProcessEngineConfiguration extends SpringProcessEngineConfiguration {

      @Override public ProcessEngine buildProcessEngine() {

         ProcessEngine processEngine = super.buildProcessEngine();

         addSessionFactory(new CustomHistoryManagerSessionFactory());

         return processEngine;

   }

}

 

import org.activiti.engine.impl.interceptor.Session;
import org.activiti.engine.impl.persistence.DefaultHistoryManagerSessionFactory;

import com.abc.dms.activiti.history.DMSCustomHistoryManager;

public class CustomHistoryManagerSessionFactory extends DefaultHistoryManagerSessionFactory {
   @Override
   public Session openSession() {
      return new CustomHistoryManager();
   }

}

 

import org.activiti.engine.impl.history.DefaultHistoryManager;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.abc.dms.activiti.cache.ActivitiThreadCache;
import com.abc.dms.activiti.constant.ActivitiConstantManager;

public class CustomHistoryManager extends DefaultHistoryManager implements ActivitiConstantManager {

   @Override
   public boolean isHistoryLevelAtLeast(HistoryLevel level) {
      // TODO Auto-generated method stub
      boolean atLeast = true;
      String hlevel = ActivitiThreadCache.get(HISTORY_LEVEL);

      HistoryLevel historyLevel = null;
      if(hlevel != null)
         historyLevel = HistoryLevel.getHistoryLevelForKey(hlevel.toLowerCase());

         if(null != historyLevel){
              atLeast = historyLevel.isAtLeast(level);
         }
         else {
            atLeast = super.isHistoryLevelAtLeast(level);
         }
      return atLeast;
   }

 

   @Override
   public void recordProcessInstanceStart(ExecutionEntity processInstance) {
      super.recordProcessInstanceStart(processInstance);
   }

}

 

 

After upgrading the Activiti to 6.0.0

We did the following changes,

 

import org.activiti.engine.ProcessEngine;
import org.activiti.spring.SpringProcessEngineConfiguration;

import com.abc.dms.activiti.history.DMSCustomHistoryManager;

public class CustomSpringProcessEngineConfiguration extends SpringProcessEngineConfiguration {

   @Override
   public ProcessEngine buildProcessEngine() {
      ProcessEngine processEngine = super.buildProcessEngine();
      return processEngine;
   }

   @Override
   public void initHistoryManager() {
      if (historyManager == null) {
         historyManager = new CustomHistoryManager(this);
      }
   }
}

 

import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.history.DefaultHistoryManager;
import org.activiti.engine.impl.history.HistoryLevel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

 

public class DMSCustomHistoryManager extends DefaultHistoryManager implements ActivitiConstantManager {

   public DMSCustomHistoryManager(ProcessEngineConfigurationImpl processEngineConfiguration) {
      super(processEngineConfiguration, HistoryLevel.getHistoryLevelForKey(processEngineConfiguration.getHistory()));
   }

   @Override
   public boolean isHistoryLevelAtLeast(HistoryLevel level) {
      boolean atLeast = true;
      String hlevel = ActivitiThreadCache.get(HISTORY_LEVEL);

      HistoryLevel historyLevel = null;
      if(hlevel != null)
         historyLevel = HistoryLevel.getHistoryLevelForKey(hlevel.toLowerCase());

         if(null != historyLevel){
            atLeast = historyLevel.isAtLeast(level);
         }
         else {
               atLeast = super.isHistoryLevelAtLeast(level);
         }
      return atLeast;
   }

}

 

With these changes, in Activiti 6.0.0 we are able to achieve to capture History only for intended workflow types, where as workflows for which Boundary timer events are attached, the trigger on timeout fails with below exception

java.lang.NullPointerException
at org.activiti.engine.impl.db.DbSqlSession.delete(DbSqlSession.java:226)
at org.activiti.engine.impl.persistence.entity.data.AbstractDataManager.delete(AbstractDataManager.java:87)
at org.activiti.engine.impl.persistence.entity.AbstractEntityManager.delete(AbstractEntityManager.java:92)
at org.activiti.engine.impl.persistence.entity.HistoricProcessInstanceEntityManagerImpl.delete(HistoricProcessInstanceEntityManagerImpl.java:71)
at org.activiti.engine.impl.persistence.entity.ExecutionEntityManagerImpl.deleteProcessInstanceCascade(ExecutionEntityManagerImpl.java:412)
at org.activiti.engine.impl.persistence.entity.ExecutionEntityManagerImpl.deleteProcessInstanceCascade(ExecutionEntityManagerImpl.java:384)
at org.activiti.engine.impl.persistence.entity.ExecutionEntityManagerImpl.deleteProcessInstanceExecutionEntity(ExecutionEntityManagerImpl.java:443)
at org.activiti.engine.impl.bpmn.behavior.BoundaryEventActivityBehavior.deleteChildExecutions(BoundaryEventActivityBehavior.java:152)
at org.activiti.engine.impl.bpmn.behavior.BoundaryEventActivityBehavior.deleteChildExecutions(BoundaryEventActivityBehavior.java:143)
at org.activiti.engine.impl.bpmn.behavior.BoundaryEventActivityBehavior.executeInterruptingBehavior(BoundaryEventActivityBehavior.java:88)
at org.activiti.engine.impl.bpmn.behavior.BoundaryEventActivityBehavior.trigger(BoundaryEventActivityBehavior.java:56)
at org.activiti.engine.impl.agenda.TriggerExecutionOperation.run(TriggerExecutionOperation.java:38)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:73)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:57)
at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:42)
at org.activiti.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:48)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:63)
at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:47)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:45)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:29)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:44)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:39)
at org.activiti.engine.impl.asyncexecutor.ExecuteAsyncRunnable.executeJob(ExecuteAsyncRunnable.java:97)
at org.activiti.engine.impl.asyncexecutor.ExecuteAsyncRunnable.run(ExecuteAsyncRunnable.java:75)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

 

On further investigating, we found out for workflows for which we are not capturing history this error occurs as the global configured history level is audit, HistoricProcessInstanceEntityManagerImpl passes entity as null and hence NullPointerException.

 

Any suggestion or workaround to solve this issue?

Outcomes