-
Bug
-
Resolution: Won't Do
-
Major
-
None
-
5.5.0.Final, 6.0.0.Beta2
-
None
A NullPointerException is thrown when updating a fact of a FromNode.
Here is the corresponding stack trace:
Exception executing consequence for rule "update_TestObjects_from_TestObjectContainer" in de.avo: java.lang.NullPointerException at org.drools.core.runtime.rule.impl.DefaultConsequenceExceptionHandler.handleException(DefaultConsequenceExceptionHandler.java:39) at org.drools.core.common.DefaultAgenda.fireActivation(DefaultAgenda.java:1424) at org.drools.core.common.DefaultAgenda.fireNextItem(DefaultAgenda.java:1343) at org.drools.core.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1561) at org.drools.core.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:637) at org.drools.core.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:601) at de.avo.Builder.fireAllRules(Builder.java:47) at de.avo.NPETest.testWrongFiringOrder(NPETest.java:37) 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.internal.runners.TestMethod.invoke(TestMethod.java:59) at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79) at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87) at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77) at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42) at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88) at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51) at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44) at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27) at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37) at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 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.NullPointerException at org.drools.core.base.DefaultKnowledgeHelper.update(DefaultKnowledgeHelper.java:374) at org.drools.core.base.DefaultKnowledgeHelper.update(DefaultKnowledgeHelper.java:439) at org.drools.core.base.ModifyInterceptor.doAfter(ModifyInterceptor.java:79) at org.mvel2.ast.InterceptorWrapper.getReducedValueAccelerated(InterceptorWrapper.java:40) at org.mvel2.MVELRuntime.execute(MVELRuntime.java:85) at org.mvel2.compiler.CompiledExpression.getDirectValue(CompiledExpression.java:123) at org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:119) at org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:113) at org.mvel2.MVEL.executeExpression(MVEL.java:930) at org.drools.core.base.mvel.MVELConsequence.evaluate(MVELConsequence.java:100) at org.drools.core.common.DefaultAgenda.fireActivation(DefaultAgenda.java:1414) ... 28 more
By debugging the Drools code (version 6.0.0.Beta2) I've found out that in the class FromNode FactHandles are created; e.g. in the method createRightTuple:
... if( _handle != null ) { // create a handle with the given id handle = workingMemory.getFactHandleFactory().newFactHandle( _handle.getId(), object, _handle.getRecency(), null, // set this to null, otherwise it uses the driver fact's entrypoint workingMemory, null ); } else { handle = workingMemory.getFactHandleFactory().newFactHandle( object, null, // set this to null, otherwise it uses the driver fact's entrypoint workingMemory, null ); } ...
As one can see in the code snippet above, the EntryPoint of the FactHandle is set to null. The newly created FactHandles are stored in the field identityMap of a knowledge helper (its type is DefaultKnowledgeHelper).
When the fact that corresponds to the newly created FactHandle has to be updated, the method update(FactHandle handle, long mask, Class<?> modifiedClass) is called. In this method the EntryPoint of the FactHandle is used to issue the update. Since this EntryPoint has been set to null by the class FromNode, a NPE is thrown. Here is the code snippet where the NPE is thrown:
public void update(final FactHandle handle, long mask, Class<?> modifiedClass) { InternalFactHandle h = (InternalFactHandle) handle; ((InternalWorkingMemoryEntryPoint) h.getEntryPoint()).update( h, ((InternalFactHandle)handle).getObject(), mask, modifiedClass, this.activation ); ...