12 Replies Latest reply on Jun 6, 2012 12:06 PM by jett

    How to Send Signal Out from a WorkItemHandler?

    brucecui

      I have been trying to implement multi-threading processes with jBPM, as well as time consuming batch tasks. I found the console was executing the processes in one same main thread, at least so appeared to me, so I tried to put the task logic in a Thread instance and started it in the WorkItemHandler. For single thread but time consuming tasks, which I need to wait for the return, I also passed the ProcessInstance as a parameter, so that when the thread finished its job, it could send out a signal togher with a return Object to notify the process to move forward. And while it was running, it would't occupy the main thread for long time.

       

      Local testing seems fine, only the job wouldn't exit after all things done. Then I put all stuf on web, but this time I met several exceptions, saying like: "java.lang.IllegalArgumentException: IOException while storing workItem 1: org.jbpm.workflow.instance.impl.WorkflowProcessInstanceImpl$1"

       

      and "Could not commit session or rollback"

       

      So I guess I should not pass the instance reference out, otherwise session may not be able to close or something. But is there a way to signal out from the WorkItemHandler or the Thread it starts, so that I can notify the process instance?

       

      I see there are external signals, but it requires reference to ksession. But I didn't find a clue from either the docs or the API about acquire the ksession reference properly. Could you be so kind to help me out here? Or else I'll have to give up, since time consuming tasks running in the same main thread together will surely be a nightmare.

       

       

      Thanks,

       

      Bruce

        • 1. Re: How to Send Signal Out from a WorkItemHandler?
          brucecui

          I'm thinking of using the same pattern like the human task. But not sure whether it's feasible?

          • 2. Re: How to Send Signal Out from a WorkItemHandler?
            krisverlaenen

            You can just pass the reference to the session when creating your handler and registering it.  Something like:

             

            StatefulKnowledgeSession ksession = ...

            MyHandler handler = new MyHandler(ksession, ...);

            ksession.getWorkItemManager().registerWorkItemHandler("myWorkItem", handler);

             

            Inside your handler, you can then simply invoke methods on the ksession, like signalEvent(..).

             

            Kris

            • 3. Re: How to Send Signal Out from a WorkItemHandler?
              brucecui

              Hi Kris,

               

              Thanks for the advice. But I'm trying to use the web-console to drive my process. Not sure how to make the console pass the session to a self-defined service task without changing the code?

               

               

              Bruce

               

               

               

               

              Added:

               

               

              I tried to pass the KnowledgeRuntime got from kcontext.getKnowledgeRuntime() from the process to the handler as a parameter instead. Similar when I tried to pass the processInstance, the web console threw some exceptions out related to JPA persistenceas below:

               

              2011-06-29 11:31:41,851 INFO  [STDOUT] (http-localhost%2F127.0.0.1-8080-17) 11:31:41,836 ERROR [SingleSessionCommandService] Could not commit session
              java.lang.IllegalArgumentException: IOException while storing workItem 4: org.drools.impl.StatefulKnowledgeSessionImpl
                        at org.drools.persistence.info.WorkItemInfo.update(WorkItemInfo.java:123)
                        at org.drools.persistence.jpa.processinstance.JPAWorkItemManager.internalExecuteWorkItem(JPAWorkItemManager.java:44)
                        at org.jbpm.workflow.instance.node.WorkItemNodeInstance.internalTrigger(WorkItemNodeInstance.java:105)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.trigger(NodeInstanceImpl.java:122)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerConnection(NodeInstanceImpl.java:185)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerCompleted(NodeInstanceImpl.java:150)
                        at org.jbpm.workflow.instance.node.ActionNodeInstance.triggerCompleted(ActionNodeInstance.java:55)
                        at org.jbpm.workflow.instance.node.ActionNodeInstance.internalTrigger(ActionNodeInstance.java:51)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.trigger(NodeInstanceImpl.java:122)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerConnection(NodeInstanceImpl.java:185)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerCompleted(NodeInstanceImpl.java:150)
                        at org.jbpm.workflow.instance.node.StartNodeInstance.triggerCompleted(StartNodeInstance.java:49)
                        at org.jbpm.workflow.instance.node.StartNodeInstance.internalTrigger(StartNodeInstance.java:41)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.trigger(NodeInstanceImpl.java:122)
                        at org.jbpm.ruleflow.instance.RuleFlowProcessInstance.internalStart(RuleFlowProcessInstance.java:35)
                        at org.jbpm.process.instance.impl.ProcessInstanceImpl.start(ProcessInstanceImpl.java:188)
                        at org.jbpm.workflow.instance.impl.WorkflowProcessInstanceImpl.start(WorkflowProcessInstanceImpl.java:302)
                        at org.jbpm.process.instance.ProcessRuntimeImpl.startProcessInstance(ProcessRuntimeImpl.java:154)
                        at org.jbpm.process.instance.ProcessRuntimeImpl.startProcess(ProcessRuntimeImpl.java:124)
                        at org.drools.common.AbstractWorkingMemory.startProcess(AbstractWorkingMemory.java:1095)
                        at org.drools.impl.StatefulKnowledgeSessionImpl.startProcess(StatefulKnowledgeSessionImpl.java:306)
                        at org.drools.command.runtime.process.StartProcessCommand.execute(StartProcessCommand.java:119)
                        at org.drools.command.runtime.process.StartProcessCommand.execute(StartProcessCommand.java:38)
                        at org.drools.persistence.SingleSessionCommandService.execute(SingleSessionCommandService.java:292)
                        at org.drools.command.impl.CommandBasedStatefulKnowledgeSession.startProcess(CommandBasedStatefulKnowledgeSession.java:222)
                        at org.jbpm.integration.console.CommandDelegate.startProcess(CommandDelegate.java:245)
                        at org.jbpm.integration.console.ProcessManagement.newInstance(ProcessManagement.java:80)
                        at org.jboss.bpm.console.server.FormProcessingFacade.startProcessWithUI(FormProcessingFacade.java:195)
                        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.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:117)
                        at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:260)
                        at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:232)
                        at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:166)
                        at org.jboss.resteasy.core.DispatcherUtilities.getJaxrsResponse(DispatcherUtilities.java:142)
                        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356)
                        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:173)
                        at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:93)
                        at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:68)
                        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
                        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                        at org.jboss.bpm.console.server.util.GWTJsonFilter.doFilter(GWTJsonFilter.java:59)
                        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                        at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
                        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                        at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
                        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:525)
                        at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
                        at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
                        at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
                        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                        at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
                        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
                        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
                        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
                        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                        at java.lang.Thread.run(Thread.java:662)
              2011-06-29 11:31:41,882 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[localhost].[/gwt-console-server].[Resteasy]] (http-localhost%2F127.0.0.1-8080-17) Servlet.service() for servlet Resteasy threw exception
              org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalArgumentException: IOException while storing workItem 4: org.drools.impl.StatefulKnowledgeSessionImpl
                        at org.jboss.resteasy.core.SynchronousDispatcher.handleApplicationException(SynchronousDispatcher.java:319)
                        at org.jboss.resteasy.core.SynchronousDispatcher.handleException(SynchronousDispatcher.java:230)
                        at org.jboss.resteasy.core.SynchronousDispatcher.handleInvokerException(SynchronousDispatcher.java:206)
                        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:360)
                        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:173)
                        at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:93)
                        at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:68)
                        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
                        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                        at org.jboss.bpm.console.server.util.GWTJsonFilter.doFilter(GWTJsonFilter.java:59)
                        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                        at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
                        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                        at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
                        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:525)
                        at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
                        at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
                        at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
                        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                        at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
                        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
                        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
                        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
                        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                        at java.lang.Thread.run(Thread.java:662)
              Caused by: java.lang.IllegalArgumentException: IOException while storing workItem 4: org.drools.impl.StatefulKnowledgeSessionImpl
                        at org.drools.persistence.info.WorkItemInfo.update(WorkItemInfo.java:123)
                        at org.drools.persistence.jpa.processinstance.JPAWorkItemManager.internalExecuteWorkItem(JPAWorkItemManager.java:44)
                        at org.jbpm.workflow.instance.node.WorkItemNodeInstance.internalTrigger(WorkItemNodeInstance.java:105)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.trigger(NodeInstanceImpl.java:122)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerConnection(NodeInstanceImpl.java:185)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerCompleted(NodeInstanceImpl.java:150)
                        at org.jbpm.workflow.instance.node.ActionNodeInstance.triggerCompleted(ActionNodeInstance.java:55)
                        at org.jbpm.workflow.instance.node.ActionNodeInstance.internalTrigger(ActionNodeInstance.java:51)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.trigger(NodeInstanceImpl.java:122)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerConnection(NodeInstanceImpl.java:185)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.triggerCompleted(NodeInstanceImpl.java:150)
                        at org.jbpm.workflow.instance.node.StartNodeInstance.triggerCompleted(StartNodeInstance.java:49)
                        at org.jbpm.workflow.instance.node.StartNodeInstance.internalTrigger(StartNodeInstance.java:41)
                        at org.jbpm.workflow.instance.impl.NodeInstanceImpl.trigger(NodeInstanceImpl.java:122)
                        at org.jbpm.ruleflow.instance.RuleFlowProcessInstance.internalStart(RuleFlowProcessInstance.java:35)
                        at org.jbpm.process.instance.impl.ProcessInstanceImpl.start(ProcessInstanceImpl.java:188)
                        at org.jbpm.workflow.instance.impl.WorkflowProcessInstanceImpl.start(WorkflowProcessInstanceImpl.java:302)
                        at org.jbpm.process.instance.ProcessRuntimeImpl.startProcessInstance(ProcessRuntimeImpl.java:154)
                        at org.jbpm.process.instance.ProcessRuntimeImpl.startProcess(ProcessRuntimeImpl.java:124)
                        at org.drools.common.AbstractWorkingMemory.startProcess(AbstractWorkingMemory.java:1095)
                        at org.drools.impl.StatefulKnowledgeSessionImpl.startProcess(StatefulKnowledgeSessionImpl.java:306)
                        at org.drools.command.runtime.process.StartProcessCommand.execute(StartProcessCommand.java:119)
                        at org.drools.command.runtime.process.StartProcessCommand.execute(StartProcessCommand.java:38)
                        at org.drools.persistence.SingleSessionCommandService.execute(SingleSessionCommandService.java:292)
                        at org.drools.command.impl.CommandBasedStatefulKnowledgeSession.startProcess(CommandBasedStatefulKnowledgeSession.java:222)
                        at org.jbpm.integration.console.CommandDelegate.startProcess(CommandDelegate.java:245)
                        at org.jbpm.integration.console.ProcessManagement.newInstance(ProcessManagement.java:80)
                        at org.jboss.bpm.console.server.FormProcessingFacade.startProcessWithUI(FormProcessingFacade.java:195)
                        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.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:117)
                        at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:260)
                        at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:232)
                        at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:166)
                        at org.jboss.resteasy.core.DispatcherUtilities.getJaxrsResponse(DispatcherUtilities.java:142)
                        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356)
                        ... 28 more
              
              
              • 4. Re: How to Send Signal Out from a WorkItemHandler?
                krisverlaenen

                Yes, trying to set the session itself as parameter is probably not a good idea, as we try to store this data in the database and that will fail.

                 

                It's true that it might be a challenge to get access to the ksession when instantiating your handler.  The console allows you to register handlers in a configuration script using MVEL, but there's no out-of-the-box solution that would easily give you access to the session like this.


                I would recommend one of two approaches:

                * instead of trying to signal the session from within the handler, simply set the signal you want to set as a result parameter when you complete your work item, and then add a script action (for example a on-exit script action) that would send the signal using kcontext.getKnowledgeRuntime()

                * try to improve the code so that you can for example get access to the ksession, for example by making ksession a predefined variable that you could use in your work item handler configuration script that would automatically resolve to the current session

                 

                Kris

                1 of 1 people found this helpful
                • 5. Re: How to Send Signal Out from a WorkItemHandler?
                  brucecui

                  Really appreciate the advice, Kris. It help me clear the direction. I think I'm going to look into the code changes.

                   

                  I'm afraid the first solution may not fit the expectation, becasue if I put the completion info in the result, it means that the handler will have to wait for the logic ends. This will block other process from running, which is the very thing I'm trying to avoid.

                   

                  For code changing, I'm looking into two directions.

                   

                  One is the instantiation of the handler. I found the point where MVEL process the config file. But it instantiate them before session is available. So I'm thinking of setting the ksession when actually registering the handler into the handler manager. I haven't found the clasee yet.

                   

                  The other is changing the marshal strategy. If I can make the code ingnore the ksession parameter, it may also avoid the problem. I think I've found the pain point as I mentioned in another thead.

                   

                  In general, if I have to change some code, I think the first one is more elegant. Could you advise on some details for the first one about where should I look into, and for the second one, whether I've been looking at the right direction? Thanks in advance.

                   

                   

                  -----------------------------------------------------------------

                  Regarding the first solution, here are some details I found:

                   

                  The handlers are loaded in SessionConfiguration. But at that point, the session is not ready. So I move to the AbstractWorkingMemory class. In its getWorkItemManager method, it loads the preloaded Handlers from SessionConfiguration into the WorkItemManager. I think I can set the kruntime there to needed handlers there. To recognize needed handlers, I can define an interface such like KnowledgeSessionAware, and check if handler is an instance of this interface. If so, call its setKsession method to pass the ksession to it.

                   

                  This idea has not been tested yet. Could you kindly advise if I'm changing the correct classes?

                  • 6. Re: How to Send Signal Out from a WorkItemHandler?
                    krisverlaenen

                    The approach you mention with KnowledgeSessionAware interface that would set the ksession was exactly what I was thinking about.  I think you would only need to extend the registerWorkItemHandler(..) method in DefaultWorkItemManager (if you're not using persistence) and JPAWorkItemManager (if you're using persistence) to check if the handler implements this interface and if so, set the ksession (note that this code is part of the drools-core or drools-persistence-jpa module respectively).

                     

                    If you could open a JIRA for this and maybe attach the code I need to add to set the session there, I'll make sure it gets included in the codebase.

                     

                    Kris

                    • 7. Re: How to Send Signal Out from a WorkItemHandler?
                      brucecui

                      Hi Kris, really appreciate your advice. After I posted upper messge last Friday, I checked out jbpm and drools codes from git, and changed exactly what you mentioned, the DefaultWorkItemManager, JPAWorkItemManager, and also a Register Command class I don't remember the exact name, which seems used in a command pattern, but I'm not sure whether it's used in practice. I also created a KnowledgeRuntimeAware interface to make the WorkItemManagers capable of recognizing those handlers that need to inject session in.

                       

                      This change worked exactly as we expected. But now I'm facing new problems. When one process contains sub process, once a async task call the ksession's signalEvent out, there is always a NullPointerException at the point which seems to be where the engine tries to add the sub process into persistance.

                       

                      I've opened another thread for this, with detail log in it. I think for the topic in this thread, you have given me absolute help. I do very much expect your kindly help on my new headache as well. I understand jBPM may not be developed for the very purpose as the one I currently have. But I don't want to simply give up especially after having gone this far.

                      • 8. Re: How to Send Signal Out from a WorkItemHandler?
                        amin-mc

                        Hi Kris

                         

                        Is the StatefulKnowledgeSessionAware something that has been implemented in Drools?  I'm very interested in using this and have implemented something but it means having a copy of JPAWorkItemManager in my codebase as I'm not sure how to replace my version with the one that is used by Drools.  If this has been implemented when would it be officially released?

                         

                        Cheers

                        • 9. Re: How to Send Signal Out from a WorkItemHandler?
                          eaa

                          AFAIK, this is not yet implemented in jBPM. So don't expect to find this feature in 5.3.0. If you are not using jbpm-gwt-console, then you can pass the ksession to your WorkItemHandler before register it.

                          If you are using jbpm-gwt-console, then probably you should implement what Bruce's solution.

                          • 10. Re: How to Send Signal Out from a WorkItemHandler?
                            amin-mc

                            Ok.  I am testing this with StatefulSessionAware interface.  As I mentioned I have a copy of JPAWorkItemManager which overrides registerWorkItemHandler(..).  Is there a way for me to extend (CustomJpaWorkItemManager extends JPAWorkItemManager) JPAWorkItemManager and then get jBPM to use my custom JPAWorkItemManager? 

                            • 11. Re: How to Send Signal Out from a WorkItemHandler?
                              mbraud

                              Hi,

                               

                              I'm looking for a similar solution. Has the code implemented by Bruce been posted somewhere?

                               

                              Regards,

                              • 12. Re: How to Send Signal Out from a WorkItemHandler?
                                jett

                                I am in a similar situation right now. Has a built-in solution been implemented for 5.3 ?

                                 

                                Thanks