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
[cid:image001.png@01CACF25.77ED4B20]
Carlos Henrique P. da Silva
Arquiteto
55-48-3953-5088
55-48-8822-2897
carlos.dasilva(a)todobpo.com.br
www.todobpo.com.br<http://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.