[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