3 Replies Latest reply on Nov 4, 2013 7:06 AM by herwix

    Single TaskService and multiple RuntimeManager non CDI

    herwix

      Hey guys,

       

      I have a situation where I would like to use multiple runtimeManager in my app, but provide a unified TaskList to my users.

      As I understand it now, this is done in the CDI based runtimeManager through the injection of a single taskService to all runtimeManagers, but I might be wrong?!

      I have tried to emulate that behavior in my code, e.g. I prepare a single taskService (normal HumanTaskServiceFactory.newTaskServiceConfigurator configured with JbpmJTATransactionManager, my entityManagerFactory and customUsergroupCallback)  and inject the same object into all my runtimeManagers (or engines) - similar in spirit to the CDITaskServiceFactory.

       

      This seems to work for some part:

      I can create multiple runtimeManagers (or engines) and start and complete humanTasks from the single taskService object and the Events get picked up by the runtimeEngines (e.g. the process moves forward on completion of tasks).

       

      However, when I run a Test based on the HumanTask Process from the jbpm6 runtime manager examples (https://github.com/mswiderski/jbpm-examples/blob/master/jbpm6/jbpm-sample/src/main/resources/humanTask.bpmn) I get a NPE - only if I have my taskservice injected into multiple runtimeEngines (e.g. I start the process twice in a perProcessInstance runtimeManager) - after I complete the last Task with the following trace:

       

      2013-11-03 13:06:17,533 [main] DEBUG wih.ExternalTaskEventListener  - >> I've recieved an event for a known session (3)
      ### REPORT - 
      ###
      ### approval_document: null
      ### approval_translatedDocument: null
      ### approval_reviewComment: null
      2013-11-03 13:06:17,548 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA TransactionManager found at fallback JNDI location [java:comp/TransactionManager]
      2013-11-03 13:06:17,549 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA 1.1 [javax.transaction.TransactionSynchronizationRegistry] API not available
      2013-11-03 13:06:17,549 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA TransactionManager found at fallback JNDI location [java:comp/TransactionManager]
      2013-11-03 13:06:17,549 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA 1.1 [javax.transaction.TransactionSynchronizationRegistry] API not available
      2013-11-03 13:06:17,560 [main] DEBUG persistence.SingleSessionCommandService  - Instantiating  JtaTransactionManager
      2013-11-03 13:06:17,561 [main] DEBUG jta.JtaTransactionManager  - JTA TransactionManager found at fallback JNDI location [java:comp/TransactionManager]
      2013-11-03 13:06:17,561 [main] DEBUG jta.JtaTransactionManager  - JTA 1.1 [javax.transaction.TransactionSynchronizationRegistry] API not available
      2013-11-03 13:06:17,572 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA TransactionManager found at fallback JNDI location [java:comp/TransactionManager]
      2013-11-03 13:06:17,572 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA 1.1 [javax.transaction.TransactionSynchronizationRegistry] API not available
      2013-11-03 13:06:17,572 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA TransactionManager found at fallback JNDI location [java:comp/TransactionManager]
      2013-11-03 13:06:17,572 [main] DEBUG tx.ExtendedJTATransactionManager  - JTA 1.1 [javax.transaction.TransactionSynchronizationRegistry] API not available
      2013-11-03 13:06:17,573 [main] DEBUG wih.ExternalTaskEventListener  - >> I've recieved an event for a known session (3)
      2013-11-03 13:06:17,600 [main] WARN  persistence.SingleSessionCommandService  - Could not commit session
      Message: null
         Line | Method
      ->> 106 | <init>                  in java.io.ByteArrayInputStream
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
      |   105 | getWorkItem             in org.drools.persistence.info.WorkItemInfo
      |   203 | internalGetWorkItem . . in org.drools.persistence.jpa.processinstance.JPAWorkItemManager
      |   130 | completeWorkItem        in     ''
      |    74 | execute . . . . . . . . in org.drools.core.command.runtime.process.CompleteWorkItemCommand
      |    35 | execute                 in     ''
      |    36 | execute . . . . . . . . in org.drools.core.command.impl.DefaultCommandService
      |    41 | executeNext             in org.drools.core.command.impl.AbstractInterceptor
      |   564 | execute . . . . . . . . in org.drools.persistence.SingleSessionCommandService$TransactionInterceptor
      |    41 | executeNext             in org.drools.core.command.impl.AbstractInterceptor
      |    71 | execute . . . . . . . . in org.drools.persistence.jpa.OptimisticLockRetryInterceptor
      |   388 | execute                 in org.drools.persistence.SingleSessionCommandService
      |   145 | completeWorkItem . . .  in org.drools.core.command.impl.CommandBasedStatefulKnowledgeSession$1
      |   102 | processTaskState        in org.jbpm.services.task.wih.ExternalTaskEventListener
      |   143 | afterTaskCompletedEvent in     ''
      |    74 | fire                    in org.jbpm.shared.services.impl.events.JbpmServicesEventImpl
      |   397 | taskOperation . . . . . in org.jbpm.services.task.internals.lifecycle.MVELLifeCycleManager
      |    47 | taskOperation           in org.jbpm.services.task.identity.UserGroupLifeCycleManagerDecorator
      |   150 | complete . . . . . . .  in org.jbpm.services.task.impl.TaskInstanceServiceImpl
      |    97 | complete                in org.jbpm.services.task.identity.UserGroupTaskInstanceServiceDecorator
      |   102 | complete . . . . . . .  in org.jbpm.services.task.subtask.SubTaskDecorator
      |   113 | complete                in org.jbpm.services.task.deadlines.DeadlinesDecorator
      |   253 | complete . . . . . . .  in org.jbpm.services.task.impl.TaskServiceEntryPointImpl
      

       

      It seems that closing/completing the processInstance causes this NPE as the script node after the last HumanTask node gets executed fine... (see output from line 1 to 6 in the pasted log)

       

      I can get this to work if I use a new TaskService for every runtimeEngine, but then I also need that specific taskService instance to move the process forward, as a signal from another taskService doesn't reach the correct runtimeEngine. And this doesn't really allow me to have a single unified tasklist for my users.

       

      So what would be the preferred or best approach in this situation? Is this just a bug or did I miss something important?

       

      Cheers, Alex

       

      P.S. I use jbpm 6.CR4 at the moment

        • 1. Re: Single TaskService and multiple RuntimeManager non CDI
          herwix

          So, I digged a little deeper here and it seems like I got it to work, however, I would love some feedback if this is indeed the correct approach or if there might be some side effects that I don't see in my simple testcase yet.

           

          The problem was that the  DefaultRegisterbaleItemsFactory set the runtimeManager into a new instance of externalTaskEventListener, which should (for my usecase) be an injected singleton (as it's done in the InjectableRegisterableItemsFactory) to go with the singleton taskservice. So I basically copied the getHTWorkItemHandler method from InjectableRegisterableItemsFactory, setup the singletons for taskservice and externalTaskEventListener and my test works now for multiple engines!

           

          However, I'm slightly confused about the TaskNotificationEventListeners. I think it's needed for the deadline notifications, etc. but I couldn't find a piece of code where it's set and I'm not really sure what it's about... So I just removed the onDispose Listener altogether, since a clear of EventListeners for a shared TaskService doesn't make sense anyway, but maybe I have to change code somewhere else as well to accommodate the sideeffects?

           

          I would be glad about any feedback! Cheers, Alex

           

          For Reference:

          DefaultRegisterbaleItemsFactory

          protected WorkItemHandler getHTWorkItemHandler(RuntimeEngine runtime) {
                 
                  ExternalTaskEventListener listener = new ExternalTaskEventListener();
                  listener.setRuntimeManager(((RuntimeEngineImpl)runtime).getManager());
                 
                  LocalHTWorkItemHandler humanTaskHandler = new LocalHTWorkItemHandler();
                  humanTaskHandler.setRuntimeManager(((RuntimeEngineImpl)runtime).getManager());
                  if (runtime.getTaskService() instanceof EventService) {
                      ((EventService)runtime.getTaskService()).registerTaskLifecycleEventListener(listener);
                  }
                 
                  if (runtime instanceof Disposable) {
                      ((Disposable)runtime).addDisposeListener(new DisposeListener() {
                         
                          @Override
                          public void onDispose(RuntimeEngine runtime) {
                              if (runtime.getTaskService() instanceof EventService) {
                                  ((EventService)runtime.getTaskService()).clearTaskLifecycleEventListeners();
                                  ((EventService)runtime.getTaskService()).clearTasknotificationEventListeners();
                              }
                          }
                      });
                  }
                  return humanTaskHandler;
              }
          
          

           

          InjectableRegisterableItemsFactory (and basically identical to my code now)

           

              protected WorkItemHandler getHTWorkItemHandler(RuntimeEngine runtime) {
                 
                  RuntimeManager manager = ((RuntimeEngineImpl)runtime).getManager();
                  taskListener.addMappedManger(manager.getIdentifier(), manager);
                 
                  LocalHTWorkItemHandler humanTaskHandler = new LocalHTWorkItemHandler();
                  humanTaskHandler.setRuntimeManager(manager);
          
          
                  return humanTaskHandler;
              } 
          
          
          
          
          1 of 1 people found this helpful
          • 2. Re: Single TaskService and multiple RuntimeManager non CDI
            swiderski.maciej

            in general, that is pretty much why we have the RegisterableItemsFactory to allow custom changes to accommodate the actual needs, so what you've done is completely valid.

             

            Although not sure why is it so important to have single instance of TaskService? If they all are configured with same data source then any instance of it would have access to all the data.

             

            When it comes to dispose/clean up on task service it might be needed when you remove given RuntimeManager and it won't be needed any more so you won't end up with sort of broken links to managers that don't exist. Maybe not important in your case.

             

            HTH

            • 3. Re: Single TaskService and multiple RuntimeManager non CDI
              herwix

              yeah, I guess you are right (as always ), I don't really need a single taskservice, but the important part is the single ExternalTaskEventListener that can be shared by the taskservices.

              Anyhow good to know that my approach seems to be valid


              Thanks for the feedback!