Hi all,

 

We're evaluating the use of Drools Fusion as rule engine for one of our projects, but after updating from version 5.0.1 to 5.1.0 to fix one persistence issue we faced new errors and some unexpected behavior changes in already tested parts of our code, and we aren't unsure if it's our fault due to incorrect assumptions or if it's resulting from a new bug introduced in last release. Here follows what we are doing:

 

 

1) we declared this in our Test_Rules.drl:

 

    declare UserEvent         

        @role( event )

        @expires( 30s )

    end

 

    query "get user by login" (String login)

    user : UserEvent(user.login == login)

    end

 

 

2) this is the code we use to retrieve the Drools session (implemented in a singleton):

 

    public StatefulKnowledgeSession getKnowledgeSessionInstance() {

        if (session == null) {

            kbase = readKnowledgeBase();

            KnowledgeSessionConfiguration conf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();

            session = kbase.newStatefulKnowledgeSession(conf, null);           

        }

       

        return session;

    }

 

 

 3) that's how we load the rules (some non-pertinent code was omitted):

 

     private KnowledgeBase readKnowledgeBase() {

       KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

       KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();

       config.setOption(EventProcessingOption.STREAM);

       kbuilder.add(ResourceFactory.newClassPathResource("Test_Rules.drl"), ResourceType.DRL);

       KnowledgeBuilderErrors errors = kbuilder.getErrors();

       if (errors.size() > 0) {

            // (...)

            throw new IllegalArgumentException("Could not parse knowledge.");

       }

       KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(config);

       kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());

       return kbase;

    }

   

(NOTE: Up to this point the code behaves exactly the same way in both versions and everything works as expected.)

 

 

4) in this method we check our event expiration, and that's where things starts to behave different. In version 5.0.1, the code always worked in the same way, returning a null reference to userValidation if the expiration time is reached. Also, handleQuery is always an instance of EventFactHandle. Based in userValidation reference being null or not, we either retrieve it or update it. Again, non-related code was ommited:

 

    private UserTrackForValidation assertDesktopEvent(DesktopDeviceEvent event) {

        UserEvent userValidation = null;

        StatefulKnowledgeSession session = sessionFactory.getKnowledgeSessionInstance(createEnviroment());

        QueryResults results = session.getQueryResults("get user by login", new String[] {event.getLogin()});

                  

        if (results.size() > 0) {

            QueryResultsRow row = results.iterator().next();

            FactHandle handleQuery = row.getFactHandle("user");

            userValidation = (UserTrackForValidation) row.get("user");

        }

 

        if (userValidation == null) {

            try {

                userValidation = entityDAO.getUserTrackByLogin(event.getLogin());

            } catch (Exception e) {

                e.printStackTrace();

            }

 

            // (...)

        }

        else {

            // (...)           

            session.update(handleQuery, userValidation);

            session.fireAllRules();

        }

    }

  

    return userValidation;

}

 

 

However, starting with version 5.1.0, we observed some odd behaviors:

    a) after time expiration, we may or may not receive a null reference to userValidation, which renders the test line "if (userValidation == null)" erratic;

    b) even when we receive a (as far as we can tell) valid reference to userValidation, sometimes a NullPointerException is raised, as can be seen in the following stacktrace:

 

java.lang.NullPointerException

                at org.drools.common.AbstractWorkingMemory.update(AbstractWorkingMemory.java:1329)

                at org.drools.common.AbstractWorkingMemory.update(AbstractWorkingMemory.java:1288)

                at org.drools.impl.StatefulKnowledgeSessionImpl.update(StatefulKnowledgeSessionImpl.java:248)

                at br.com.todobpo.lam.ejb.session.ContactCenterRuleServiceImpl.assertSimpleEvent(ContactCenterRuleServiceImpl.java:191)

                at br.com.todobpo.lam.ejb.session.ContactCenterRuleServiceImpl.insertEvent(ContactCenterRuleServiceImpl.java:89)

                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.aop.joinpoint.MethodInvocation.invokeTarget(MethodInvocation.java:122)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:111)

                at org.jboss.ejb3.EJBContainerInvocationWrapper.invokeNext(EJBContainerInvocationWrapper.java:69)

                at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.invoke(InterceptorSequencer.java:73)

                at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.aroundInvoke(InterceptorSequencer.java:59)

                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.aop.advice.PerJoinpointAdvice.invoke(PerJoinpointAdvice.java:174)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.fillMethod(InvocationContextInterceptor.java:72)

                at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_fillMethod_1000702.invoke(InvocationContextInterceptor_z_fillMethod_1000702.java)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.setup(InvocationContextInterceptor.java:88)

                at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_setup_1000702.invoke(InvocationContextInterceptor_z_setup_1000702.java)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:62)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor.invoke(TransactionScopedEntityManagerInterceptor.java:56)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:47)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.tx.StatelessBMTInterceptor.handleInvocation(StatelessBMTInterceptor.java:106)

                at org.jboss.ejb3.tx.BMTInterceptor.invoke(BMTInterceptor.java:55)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:68)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:186)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:421)

                at org.jboss.ejb3.session.InvokableContextClassProxyHack._dynamicInvoke(InvokableContextClassProxyHack.java:53)

                at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:91)

                at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82)

                at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:891)

                at org.jboss.remoting.transport.socket.ServerThread.completeInvocation(ServerThread.java:744)

                at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:697)

                at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:524)

                at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:232)

                at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:211)

                at org.jboss.remoting.Client.invoke(Client.java:1724)

                at org.jboss.remoting.Client.invoke(Client.java:629)

                at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:60)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:61)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.security.client.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:65)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.aspects.remoting.PojiProxy.invoke(PojiProxy.java:62)

                at $Proxy7.invoke(Unknown Source)

                at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:207)

                at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:164)

                at $Proxy6.insertEvent(Unknown Source)

                at br.com.todobpo.lam.ejb.test.Authenticate.authenticationOk(Authenticate.java:70)

                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.runChild(BlockJUnit4ClassRunner.java:76)

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

                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:46)

                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)

                at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:72)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:61)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.security.client.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:65)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)

                at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

                at org.jboss.aspects.remoting.PojiProxy.invoke(PojiProxy.java:62)

                at $Proxy7.invoke(Unknown Source)

                at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:207)

                at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:164)

                at $Proxy6.insertEvent(Unknown Source)

                at br.com.todobpo.lam.ejb.test.Authenticate.authenticationOk(Authenticate.java:70)

                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.runChild(BlockJUnit4ClassRunner.java:76)

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

                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:46)

                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)

 

    c) handleQuery can either receive an EventFactHandle or a DisconnectedFactHandle, without no apparent pattern. For the later case (DisconnectedFactHandle), the returned class lacks a useful isValid() method (the current implementation only throws an exception), thus we are unable to check the returned object before using it;

 

    d) finally, we noticed that all mentioned errors happens only when we set the real time clock in the KnowledgeSessionConfiguration object. Using the pseudo clock, there are no runtime errors and the object expiration behavior appears to be correct, like in 5.0.1.

   

So, after all this information, we have a couple questions:

 

    1) Is really correct to assume that after an event expiration the object reference will always be null (as observed up to version 5.0.1) ? If not, how can we determine the event associated object sanity ?

    2) Why starting from version 5.1.0 we started to observe the occurrence of DisconnectedFactHandle ? Can this detail be safely ignored, or is this a sign that something went wrong when trying to retrieve the event ?

    3) Is there something flawed in our presented approach ? Or did we bump into a new bug ?

   

Any help is apreciated.   

 

Carlos

 

logo_.png

 

Carlos Henrique P. da Silva

 

Arquiteto

 

55-48-3953-5088

 

55-48-8822-2897

 

carlos.dasilva@todobpo.com.br

 

www.todobpo.com.br

 

 

 

 

 

 

 

 

 

 



Esta mensagem pode conter informação confidencial e/ou privilegiada, sendo seu sigilo protegido por lei. Se você não for o destinatário ou a pessoa autorizada a receber esta mensagem, não pode usar, copiar ou divulgar as informações nela contidas ou tomar qualquer ação baseada nessas informações. Se você recebeu esta mensagem por engano, por favor avise imediatamente ao remetente, respondendo o e-mail e em seguida apague-o. Agradecemos sua cooperação.

This message may contain confidential and/or privileged information, being the sigil protected by law. If you are not the addressee or authorized to receive this for the addressee, you must not use, copy, disclose or take any action based on this message or any information herein. If you have received this message in error, please advise the sender immediately by reply e-mail and delete this message. Thank you for your cooperation.