[jboss-jira] [JBoss JIRA] (JBRULES-3466) accumulate with an "and" condition results in NPE on reverse
Chris Dolan (JIRA)
jira-events at lists.jboss.org
Wed Apr 25 16:51:18 EDT 2012
[ https://issues.jboss.org/browse/JBRULES-3466?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Chris Dolan updated JBRULES-3466:
---------------------------------
Summary: accumulate with an "and" condition results in NPE on reverse (was: sum with no matching values results in NPE on reverse)
[Changed title]
I finally reproduced this in a unit test! It turns out that there something about an "and" in the predicate of the accumulate function that's screwing it up. I came to that realization due to a completely different bug, that "and" and "or" clauses can trigger an off-by-one in the variable binding. (I'm working on a test case for that, but I suspect it may be the same root cause!)
Here's the rule. The min() part is just legacy of a previous attempt. The "and" in the sum accumulate is what triggers it.
{noformat}
package org.drools.test;
import org.drools.Cheese;
import org.drools.Cheesery;
import org.drools.Person;
global java.util.List results;
declare Total
person : Person
value : Number
end
rule "Total prices of cheeses liked by other people" salience 2
when
$person : Person( $likes : likes )
$total : Number() from accumulate( $p : Person(likes != $likes, $l : likes) and $c : Cheese( type == $l ),
sum($c.getPrice()) )
then
insertLogical(new Total($person, $total));
end
rule "Minimum Total" salience 1
when
$min : Number() from accumulate( $p : Person(name != "Doug") and $t : Total(person == $p),
min($t.getValue()) )
then
results.add( $min );
end
{noformat}
The failing test I added to AccumulateTest. I crashes with an NPE on the retract() line.
{noformat}
@Test
public void testAccumulateReverseModifyInsertLogical2() throws Exception {
// read in the source
final Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_AccumulateReverseModifyInsertLogical2.drl" ) );
final RuleBase ruleBase = loadRuleBase( reader );
final WorkingMemory wm = ruleBase.newStatefulSession();
final List results = new ArrayList();
wm.setGlobal( "results",
results );
final Cheese[] cheese = new Cheese[]{
new Cheese( "stilton", 10 ),
new Cheese( "stilton", 2 ),
new Cheese( "stilton", 5 ),
new Cheese( "brie", 15 ),
new Cheese( "brie", 16 ),
new Cheese( "provolone", 8 )
};
final Person alice = new Person( "Alice", "brie" );
final Person bob = new Person( "Bob", "stilton" );
final Person carol = new Person( "Carol", "cheddar" );
final Person doug = new Person( "Doug", "stilton" );
final FactHandle[] cheeseHandles = new FactHandle[cheese.length];
for ( int i = 0; i < cheese.length; i++ ) {
cheeseHandles[i] = wm.insert( cheese[i] );
}
final FactHandle aliceHandle = wm.insert( alice );
final FactHandle bobHandle = wm.insert( bob );
// add Carol later
final FactHandle dougHandle = wm.insert( doug ); // should be ignored
// alice = 31, bob = 17, carol = 0, doug = 17
// !alice = 34, !bob = 31, !carol = 65, !doug = 31
wm.fireAllRules();
System.out.println("results: " + results);
assertEquals( 31, ((Number) results.get( results.size() - 1 )).intValue() );
// retract stilton=2 ==> bob = 15, doug = 15, !alice = 30, !carol = 61
wm.retract(cheeseHandles[1]);
wm.fireAllRules();
System.out.println("results: " + results);
assertEquals( 31, ((Number) results.get( results.size() - 1 )).intValue() );
}
{noformat}
Here's the NPE:
{noformat}
org.drools.RuntimeDroolsException: java.lang.NullPointerException
at org.drools.rule.Accumulate.reverse(Accumulate.java:217)
at org.drools.reteoo.AccumulateNode.removeMatch(AccumulateNode.java:902)
at org.drools.reteoo.AccumulateNode.removePreviousMatchesForRightTuple(AccumulateNode.java:972)
at org.drools.reteoo.AccumulateNode.retractRightTuple(AccumulateNode.java:320)
at org.drools.reteoo.RightInputAdapterNode.retractLeftTuple(RightInputAdapterNode.java:190)
at org.drools.reteoo.SingleLeftTupleSinkAdapter.doPropagateRetractLeftTuple(SingleLeftTupleSinkAdapter.java:225)
at org.drools.reteoo.SingleLeftTupleSinkAdapter.propagateRetractRightTuple(SingleLeftTupleSinkAdapter.java:132)
at org.drools.reteoo.JoinNode.retractRightTuple(JoinNode.java:172)
at org.drools.reteoo.ObjectTypeNode.retractObject(ObjectTypeNode.java:283)
at org.drools.reteoo.EntryPointNode.retractObject(EntryPointNode.java:354)
at org.drools.common.NamedEntryPoint.retract(NamedEntryPoint.java:555)
at org.drools.common.AbstractWorkingMemory.retract(AbstractWorkingMemory.java:920)
at org.drools.common.AbstractWorkingMemory.retract(AbstractWorkingMemory.java:908)
at org.drools.integrationtests.AccumulateTest.testAccumulateReverseModifyInsertLogical2(AccumulateTest.java:606)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
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.base.accumulators.SumAccumulateFunction.reverse(SumAccumulateFunction.java:83)
at org.drools.base.accumulators.JavaAccumulatorFunctionExecutor.reverse(JavaAccumulatorFunctionExecutor.java:130)
at org.drools.rule.Accumulate.reverse(Accumulate.java:208)
... 36 more
{noformat}
> accumulate with an "and" condition results in NPE on reverse
> ------------------------------------------------------------
>
> Key: JBRULES-3466
> URL: https://issues.jboss.org/browse/JBRULES-3466
> Project: Drools
> Issue Type: Bug
> Security Level: Public(Everyone can see)
> Components: drools-core (expert)
> Affects Versions: 5.4.0.CR1
> Environment: drools-planner 5.4.0-SNAPSHOT, snapshot downloaded on Apr 9, 2012
> Reporter: Chris Dolan
> Assignee: Edson Tirelli
>
> I have a rule with this element in the when clause:
> {noformat}
> $beforePrediction : Number() from accumulate(
> JobSlot(job != NULL_JOB, $j : job, $service == service, $ordinal > ordinal) and $p : Prediction(job == $j, service == $service),
> sum($p.getMillisMid())
> )
> {noformat}
> When I execute this I have cases where there are no matching Prediction instances, so the sum is zero. Later on retract, I get this:
> {noformat}
> Caused by: java.lang.NullPointerException: null
> at org.drools.base.accumulators.SumAccumulateFunction.reverse(SumAccumulateFunction.java:83)
> at org.drools.base.accumulators.JavaAccumulatorFunctionExecutor.reverse(JavaAccumulatorFunctionExecutor.java:130)
> at org.drools.rule.Accumulate.reverse(Accumulate.java:208)
> {noformat}
> The relevant code is this:
> {noformat}
> public void accumulate(Serializable context,
> Object value) {
> SumData data = (SumData) context;
> data.total += ((Number) value).doubleValue();
> }
> {noformat}
> and this:
> {noformat}
> public void reverse(Object workingMemoryContext,
> Object context,
> Tuple leftTuple,
> InternalFactHandle handle,
> Declaration[] declarations,
> Declaration[] innerDeclarations,
> WorkingMemory workingMemory) throws Exception {
> final Object value = ((JavaAccumulatorFunctionContext) context).reverseSupport.remove( Integer.valueOf( handle.getId() ) );
> this.function.reverse( ((JavaAccumulatorFunctionContext) context).context,
> value );
> }
> {noformat}
> A breakpoint reveals that handle is a Prediction instance. Somehow that handle was not added to reverseSupport, apparently.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.jboss.org/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira
More information about the jboss-jira
mailing list