cancel
Showing results for 
Search instead for 
Did you mean: 

How to query all tasks of a given workflow instance? (NPE)

nicolasraoul
Star Contributor
Star Contributor
I want to get the list of tasks of a given workflow instance. So I wrote:

WorkflowTaskQuery query = new WorkflowTaskQuery();
query.setProcessId("jbpm$" + new Long(processInstance.getId()).toString());
query.setTaskState(WorkflowTaskState.COMPLETED); // TODO add those IN_PROGRESS
List<WorkflowTask> tasks = workflowService.queryTasks(query);

But it already gives an exception:

ERROR [org.jbpm.graph.def.GraphElement] action threw exception: null
java.lang.NullPointerException
   at org.alfresco.repo.workflow.jbpm.JBPMEngine.createWorkflowNode(JBPMEngine.java:2699)
   at org.alfresco.repo.workflow.jbpm.JBPMEngine.createWorkflowPath(JBPMEngine.java:2685)
   at org.alfresco.repo.workflow.jbpm.JBPMEngine.createWorkflowTask(JBPMEngine.java:2841)
   at org.alfresco.repo.workflow.jbpm.JBPMEngine.getWorkflowTasks(JBPMEngine.java:1289)
   at org.alfresco.repo.workflow.jbpm.JBPMEngine$24.doInJbpm(JBPMEngine.java:1260)
   at org.alfresco.repo.workflow.jbpm.JBPMEngine$24.doInJbpm(JBPMEngine.java:1249)
   at org.springmodules.workflow.jbpm31.JbpmTemplate$1.doInHibernate(JbpmTemplate.java:87)
   at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
   at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:338)
   at org.springmodules.workflow.jbpm31.JbpmTemplate.execute(JbpmTemplate.java:80)
   at org.alfresco.repo.workflow.jbpm.JBPMEngine.queryTasks(JBPMEngine.java:1247)
   at org.alfresco.repo.workflow.WorkflowServiceImpl.queryTasks(WorkflowServiceImpl.java:522)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
   at org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor.invoke(AlwaysProceedMethodInterceptor.java:40)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
   at org.alfresco.repo.security.permissions.impl.ExceptionTranslatorMethodInterceptor.invoke(ExceptionTranslatorMethodInterceptor.java:49)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
   at org.alfresco.repo.audit.AuditMethodInterceptor.invoke(AuditMethodInterceptor.java:148)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
   at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
   at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
   at $Proxy46.queryTasks(Unknown Source)
   at org.myproject.EndActionHandler.readHistory(EndActionHandler.java:80)

If I comment the setTaskState line, then no exception but empty result. Any idea what is happening?

What would be a valid way to get the list of tasks for a given workflow instance?

Thanks a lot!
Nicolas
3 REPLIES 3

nicolasraoul
Star Contributor
Star Contributor
Context: the last step of a workflow reads the workflow's tasks history.

This exception appears because I am running the query within a jBPM ActionHandler. If I am running the query from non-jBPM-related classes (for instance a custom dialog) then it works. But I need to run the query as the last step of the workflow, so the last action handler is the only place where I can do it.

Querying the workflow from the Java code in the workflow' action handler itself confuses jBPM to the point of generating a NullPointerException.
I guess it is a bug in jBPM.

Here is an unreliable workaround (it works most of the time but race conditions might make it fail, so it is not a reliable solution):

- In MyActionHandler, launch a worker thread that you can call WorkflowQueryWorker.
- In WorkflowQueryWorker, do the query.

It works, probably because the worker thread is not part of any jBPM transaction.
But there is a race between the two threads. If for some reason the main thread goes faster, then the workflow may finish and the workflow's history be removed, which means the query will return no results.

Code in the ActionHandler:
PublishWorker worker = new PublishWorker(processInstance.getId(), nodeRefs, workflowService, nodeService);
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(5);
ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, queue);
threadExecutor.execute(worker);

Code in the worker:
public class PublishWorker implements Runnable {

   private QName PROP_QNAME_HISTORY = QName.createQName("http://www.my.org/", "approvalHistory");
   
   private long processId;
   
   private List<NodeRef> nodeRefs;
   
   private WorkflowService workflowService;
   
   private NodeService nodeService;
   
   public PublishWorker(long processId, List<NodeRef> nodeRefs, WorkflowService workflowService, NodeService nodeService) {
      this.processId = processId;
      this.nodeRefs = nodeRefs;
      this.workflowService = workflowService;
      this.nodeService = nodeService;
   }
   
   @Override
   public void run() {
      System.out.println("PublishWorker.run enter");
      
      // Read history from workflow.
      // Needs to be done fast, because the workflow is finishing in another thread,
      // and history is deleted when the workflow finishes.
      String history = readHistory();
      System.out.println("history:" + history);

      // Wait for the nodes to be written in the main thread, otherwise we get a InvalidNodeRefException
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {}
      
      final String finalHistory = history;
      for(NodeRef nodeRef : nodeRefs) {
         // Write workflow history to the published document, as admin.
         final NodeRef finalNodeRef = nodeRef;
         RunAsWork<Object> work = new RunAsWork<Object>() {
              public Object doWork() throws Exception {
                 // Set the history property.
                 nodeService.setProperty(finalNodeRef, PROP_QNAME_HISTORY, finalHistory);
                 return new Object(); // Have to return something.
              }
         };
         AuthenticationUtil.runAs(work, "admin");
      }
      System.out.println("PublishWorker.run exit");
   }
   
   /**
    * Read history of a workflow instance.
    * @return history as a human-readable String.
    */
   private String readHistory() {
      
      // Build processId string.
        String processIdString = "jbpm$" + new Long(processId).toString();

      // Get completed tasks for current workflow instance.
      WorkflowTaskQuery query = new WorkflowTaskQuery();
        //query.setActive(workflowInstance.active);
        query.setProcessId(processIdString);
        query.setTaskState(WorkflowTaskState.COMPLETED);
        query.setOrderBy(new WorkflowTaskQuery.OrderBy[] {
                 WorkflowTaskQuery.OrderBy.TaskCreated_Desc,
                 WorkflowTaskQuery.OrderBy.TaskActor_Asc });
        List<WorkflowTask> completedTasks = workflowService.queryTasks(query);

      // Get in-progress tasks for current workflow instance.
        query.setTaskState(WorkflowTaskState.IN_PROGRESS);
        List<WorkflowTask> inProgressTasks = workflowService.queryTasks(query);
       
        // Join tasks.
        List<WorkflowTask> tasks = new ArrayList<WorkflowTask>();
        tasks.addAll(completedTasks);
        tasks.addAll(inProgressTasks);
       
        // Print the tasks to a String.
        StringBuffer historyBuffer = new StringBuffer();
        for (WorkflowTask task : tasks)
        {
           // Get task characteristics.
           Long id = (Long)task.properties.get(WorkflowModel.PROP_TASK_ID);
           String desc = (String)task.properties.get(WorkflowModel.PROP_DESCRIPTION);
           Date createdDate = (Date)task.properties.get(ContentModel.PROP_CREATED);
           String owner = (String)task.properties.get(ContentModel.PROP_OWNER);
           String comment = (String)task.properties.get(WorkflowModel.PROP_COMMENT);
           Date completedDate = (Date)task.properties.get(WorkflowModel.PROP_COMPLETION_DATE);
           String transition = (String)task.properties.get(WorkflowModel.PROP_OUTCOME);
           String outcome = "";
           if (transition != null)
           {
              WorkflowTransition[] transitions = task.definition.node.transitions;
              for (WorkflowTransition trans : transitions)
              {
                 if (trans.id.equals(transition))
                 {
                    outcome = trans.title;
                    break;
                 }
              }
           }
           // Write task characteristics.
           historyBuffer.append(id.toString());
           historyBuffer.append(" ; ");
           historyBuffer.append(desc == null ? "" : Utils.encode(desc));
           historyBuffer.append(" ; ");
           historyBuffer.append(Utils.encode(task.title));
           historyBuffer.append(" ; ");
           historyBuffer.append(createdDate);
           historyBuffer.append(" ; ");
           historyBuffer.append(owner == null ? "" : owner);
           historyBuffer.append(" ; ");
           historyBuffer.append(comment == null ? "" : Utils.encode(comment));
           historyBuffer.append(" ; ");
           historyBuffer.append(completedDate);
           historyBuffer.append(" ; ");
           historyBuffer.append(outcome);
           historyBuffer.append("\n");
        }
        return historyBuffer.toString();
   }   
}

Can you suggest any improvement?
In particular, how to make it more reliable and remove the race condition?
Or a better workaround for this bug?

Thanks a lot,
Nicolas Raoul

talija83
Champ in-the-making
Champ in-the-making
Has anybody found a solution for this problem?  :cry:

nicolasraoul
Star Contributor
Star Contributor
I asked my Alfresco Enterprise support, and they created a JIRA issue:
https://issues.alfresco.com/jira/browse/ALF-2337
But no progress since.
So the system I created will be deployed in production next week, with the silly workaround I described above  :shock: