[
https://issues.jboss.org/browse/DROOLS-163?page=com.atlassian.jira.plugin...
]
Davide Sottara commented on DROOLS-163:
---------------------------------------
The keyword "from" should be used to match patterns against objects which are
not (necessarily) in the working memory.
That is, objects that are so "local" to some rule that there is no point in
inserting them in the WM. From, in fact,
does not check whether the object is actually in the WM (that would impact performance
heavily), beacuse the most likely
answer would be NO.
So, it does not make sense to modify an object returned "from" an expression. We
are discussing whether a compilation
error/warning should be generated.
In general, there are several options to write your rule in a more correct form, you
should pick the one that makes
most sense for your use case. For example:
1) Modify the parent container rather than the object itself
when
$x : Parent( $z : collection ) $y : Child() from $z
then
$y.set( .. )
modify ( $x ) { .. }
end
2) Use memberOf, which requires the fact to be in the WM:
when
$x : Parent( $z : collection )
$y : Child( this memberOf $z )
then
modify ( $y ) { .. }
end
3) Use ==, which again requires the fact to be in the WM (a bit redundant though)
when
$x : Parent( $z : collection )
$w : Child( .. ) from $z
$y : Child( this == $w )
then
modify ( $y ) { .. }
end
NPE when updating a fact of a FromNode
--------------------------------------
Key: DROOLS-163
URL:
https://issues.jboss.org/browse/DROOLS-163
Project: Drools
Issue Type: Bug
Security Level: Public(Everyone can see)
Affects Versions: 5.5.0.Final, 6.0.0.Beta2
Environment: Windows7, JDK 1.6.26
Reporter: Andreas Volz
Assignee: Mark Proctor
Labels: EntryPoint, FactHandle, NPE, update
Attachments: npe.zip
A NullPointerException is thrown when updating a fact of a FromNode.
Here is the corresponding stack trace:
{code}
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
{code}
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:
{code:title=FromNode#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 );
}
...
{code}
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:
{code:title=DefaultKnowledgeHelper}
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 );
...
{code}
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see:
http://www.atlassian.com/software/jira