10 Replies Latest reply on Oct 7, 2011 2:34 AM by mgeldenhuys

    How to persist a timer

    krausest

      I'm failing miserably at persisting a process with a timer.

      I'm disposing the session before the timer expires. When I call JPAKnowledgeService.loadStatefulKnowledgeSession with the old session id I'm getting an exception. I've attached a test case with a simple maven project. Just use mvn test to see the issue.

       

      Any help would be appreciated.

       

       

      {code}

      java.lang.IllegalStateException: java.lang.reflect.InvocationTargetException

                at org.drools.persistence.jpa.KnowledgeStoreServiceImpl.buildCommanService(KnowledgeStoreServiceImpl.java:101)

                at org.drools.persistence.jpa.KnowledgeStoreServiceImpl.loadStatefulKnowledgeSession(KnowledgeStoreServiceImpl.java:69)

                at org.drools.persistence.jpa.JPAKnowledgeService.loadStatefulKnowledgeSession(JPAKnowledgeService.java:131)

                at net.stefankrause.jbpm.ProcessRunner.setupSession(ProcessRunner.java:94)

                at net.stefankrause.jbpm.ProcessRunner.<init>(ProcessRunner.java:49)

                at net.stefankrause.jbpm.tests.TimerPersistence.testTimerPersistence(TimerPersistence.java:34)

                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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)

                at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)

                at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)

                at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)

                at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)

                at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)

                at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)

                at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)

                at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)

                at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)

                at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)

                at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)

                at org.junit.runners.ParentRunner.run(ParentRunner.java:236)

                at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)

                at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)

                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)

                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)

                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)

                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

      Caused by: java.lang.reflect.InvocationTargetException

                at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

                at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)

                at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)

                at java.lang.reflect.Constructor.newInstance(Constructor.java:513)

                at org.drools.persistence.jpa.KnowledgeStoreServiceImpl.buildCommanService(KnowledgeStoreServiceImpl.java:86)

                ... 28 more

      Caused by: java.lang.RuntimeException: Unable to load session snapshot

                at org.drools.persistence.SessionMarshallingHelper.loadSnapshot(SessionMarshallingHelper.java:96)

                at org.drools.persistence.SingleSessionCommandService.initKsession(SingleSessionCommandService.java:229)

                at org.drools.persistence.SingleSessionCommandService.<init>(SingleSessionCommandService.java:177)

                ... 33 more

      Caused by: java.lang.NullPointerException

                at org.drools.common.ConcurrentNodeMemories.getNodeMemory(ConcurrentNodeMemories.java:65)

                at org.drools.common.AbstractWorkingMemory.getNodeMemory(AbstractWorkingMemory.java:1040)

                at org.drools.marshalling.impl.InputMarshaller.readSession(InputMarshaller.java:231)

                at org.drools.marshalling.impl.InputMarshaller.readSession(InputMarshaller.java:203)

                at org.drools.marshalling.impl.DefaultMarshaller.unmarshall(DefaultMarshaller.java:92)

                at org.drools.persistence.SessionMarshallingHelper.loadSnapshot(SessionMarshallingHelper.java:91)

                ... 35 more

       

       

      {code}

       

        • 1. Re: How to persist a timer
          winton

          I have the same issue.  it seems that there are problem on loading  StatefulKnowledgeSession. 
          this exception happen frequently,not only persist a timer.

          • 2. Re: How to persist a timer
            krausest

            I just noticed that the problem persists (pun intended) when I remove the timer event from the diagram. Even if the process has only two script services loadStatefulKnowledgeSession fails with the exception above. Looking at the rare examples that I find I can't see any significant differences. The problem occurs only when an instance was or is active, otherwise restoring the knowlede session works (but is pointless of course).

             

            Has anyone a clue how I can make it work?

            • 3. Re: How to persist a timer
              krausest

              Thanks for your help so far! I found that the problem occurs when I recreate the KnowledgeBase. If I keep the KnowledgeBase (e.g. in a member variable) I can recreate the session with JPAKnowledgeService.loadStatefulKnowledgeSession and everything is fine.

               

              Here's how I create the KnowledgeBase:

              {code}

              KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

              kbuilder.add(ResourceFactory.newClassPathResource("TimerPersistence.bpmn2"), ResourceType.BPMN2);

              KnowledgeBase knowledgeBase = kbuilder.newKnowledgeBase();

              {code}

               

              How can I handle recreation of the KnowledgeBase e.g. in a web app when I run in a new java process?

              • 4. Re: How to persist a timer
                rrpeterson

                I've had similar issues.  The way I worked around mine was to not call the .dispose() method on the ksession until all processes have finished executing.  For some reason calling dispose during processInstance execution put things into a weird state.  Maybe that's how it's supposed to work, its unclear to me if .dispose() should be called every every unit of work (check on the status of a running processInstance for example), or only at the completion of all work.

                 

                Using the previous approach I also found if the processInstance is persisted while waiting in a timer state, and is retrieved after the timer has expired, the process instance will not continue firing.  Basically the session has to be re-loaded from the DB before timer expiration.

                 

                I haven't played with the KnowledgeBase, I'll give that a shot & see if there's a better solution that way.

                • 5. Re: How to persist a timer
                  mnorsic

                  Hi Stefan,

                   

                  I had similar problems, and I resolved it by using the same Environment object. I see that you create a new Environment every time when creating ProcessRunner, but essentially, you are using the same knowledge session in a same thread.

                  Try to create Environment once, and pass it to ProcessRunner, and see if it works for you.

                   

                  Miljenko

                  • 6. Re: How to persist a timer
                    krausest

                    Thanks for your reply. Keeping the Environment object didn't help. Anyway I want to simulate an appserver restart, so I have to recreate those objects. I'll create a jira issue.

                    • 7. Re: How to persist a timer
                      mgeldenhuys

                      Hi Stefan,

                       

                      I was quite interested in your question, because we have to have long running processes as well. So I spent some time to try and see how this can be solved. I used your test case you uploaded to the JIRA to test and this is what I did:

                       

                      When loading the KnowledgeSession you can pass in a reference to a KnowledgeBase.

                       

                      StatefulKnowledgeSession ksession = JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, knowledgeBase, null, env);
                      
                      

                       

                      That solves the problem of loading a knowledge session with an already loaded knowledge base.

                       

                      You can write a knowledge base itself to external storage, the KnowledgeBaseImpl class is Externalizable, and then read it again from external storage to load up you session.

                       

                      This obviously does not solve the problem where the knowledgebase needs to be rebuilt because of changes to the process definitions, but at least you can load sessions now with existing process definitions.

                       

                      You can also look at using a knowledge agent to load knowledge bases from Guvnor which should manage changes to process definitions.

                       

                      Mare

                      • 8. Re: How to persist a timer
                        marco.rietveld

                        Mare,

                         

                        Thanks -- that's a good tip.

                         

                        Stefan,

                         

                        Thanks for submitting the Jira (and the test case) -- I've been looking at the problem this week and trying to find a solution for it. I've put most of the info on Jira.

                         

                        Marco

                        1 of 1 people found this helpful
                        • 9. Re: How to persist a timer
                          krausest

                          Thanks Mare and Marco. Actually my code looked like that before, but when I tried this and that as a workaround I finally left (to my shame) the code passing null for the knowledgeBase.
                          Marco commented on https://issues.jboss.org/browse/JBPM-3383 and found the real problem I ran into originally, which is also a NullPointerException but caused by ConcurrentNodeMemories.getNodeMemory(NodeMemory) line: 65.

                           

                          Thank you both and sorry for causing additional trouble

                          • 10. Re: How to persist a timer
                            mgeldenhuys

                            No worries Stefan,

                             

                            You asked an important question and raised an issue that needs to be solved (albeit with the common programmer mistake of passing a null, we all do that by the way). The solution I supplied is merely a workaround and not the optimal solution anyway. Marco is busy with that (the optimal solution, that is).