Uploaded image for project: 'Drools'
  1. Drools
  2. DROOLS-438

Pattern for "reuse java method" bug

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • Major
    • 6.1.0.Beta1
    • 6.0.1.Final
    • None
    • None

    Description

      Ciao, in the documentation about "reuse Java methods" Ref. (1) a java.lang static method is used as a pattern.

      • A-/ I hereby attach proof Java code to replicate the issue that doing so, rules will functionally work OK, actually, but in the background will increase memory consumption leading to Exception java.lang.OutOfMemoryError: Java heap space
      • B-/ I also attach proof of Java code to replicate that by taking the "Java methods" from the pattern and when each one is extracted to their own "conditional element eval", then, it will work OK functionally and memory wise.

      Therefore, I believe either of the two:

      • The reference (1) is wrong, example should be corrected, and I suggest also should be put a warning that each "reuse Java methods" should get their own "conditional element eval"
      • If reference (1) is intended to be working fine, then I suspect there is a bug in the background working of the ReteOO/Phreak algorithm, so I hope this helps identify the bug.
      References

      (1) http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102 example Person( Math.round( weight / ( height * height ) ) < 25.0 )

      Java code to replicate

      In the attached Maven project, in the src/main/resources there are two .drl files:

      • package_usingReuseJavaMethodsExample.drl this will demonstrate point A above
      • package_usingCEeval.drl this will demonstrate point B above

      Leave only one of the two files with the .drl extension, so to decide which point to prove.

      I encourage to run the AppRealtime.class with the following command line parameters: -XX:+HeapDumpOnOutOfMemoryError -Xms128m -Xmx128m -ea. The other two App* are left as I originally suspected this bug was due to the pseudo session clock, but it's not, so you can ignore them.

      If running to demonstrate point A, it will after 3 minutes or so Exception java.lang.OutOfMemoryError: Java heap space as per attached screenshots.
      If running to demonstrate point B, it will run forever, you can attach Java VisualVM as per attached screenshot.

      Business Requirements - explanation of the Rule

      Sensor data is received as OpDatum.java, about temperature and heater on/off from xml and instantiated in Java object, put into a list AcmeModelListDTO.java to be inserted into the Working Memory.

      • If temperature is < 9, then the heater must be running, otherwise an Alert must be raised.
      • If temperature is >= 9, or the heater is running, and there was previously raised an Alert, then the Alert shall be retracted

      The attached code is a minimalistic extraction of a bigger broader application, so there are pieces of Java code which could have been likely optimized, in this version.

      Style A

      This implementation follows example at http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102 example Person( Math.round( weight / ( height * height ) ) < 25.0 )
      Rules will functionally work OK, actually.
      You will see that Java asserts in the AppRealtime.java are always met, and indeed the Working Memory fact count does not explode, the number of facts in the Working Memory is always limited.
      But, in the background will increase memory consumption leading to Exception java.lang.OutOfMemoryError: Java heap space

      package com.acme.drools6test.sessionclock;
      
      /*
      "reuse Java methods" Ref. [1]
      Using analogous example from example "reuse Java methods" in the doc at [1].
      
      This implementation, despite follow example documentation, does NOT work good.
      Functionally, actually works OK. The rules behaves as expected indeed.
      Problem is however memory consumption is always icreasing, leading to Out of Memory error
       
      [1] http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102
       */
      
      import org.acme.model.*
      
      declare OpDatum
        @role(event)
        @timestamp(ts)
      end
      
      declare Alert
        @role(fact)
        condition : String
      end
      
      rule "AB1 Detect"
      no-loop
      dialect "mvel" 
      when
      	$oNumeric : OpDatum( id == "temperature", Double.parseDouble(val) < 9 )
      	$oBoolean : OpDatum( id == "heater_powered", val == "False" ) 
      	not( Alert( condition == "temperature freezing" ) )
      then
      	Alert a = new Alert();
      	a.condition = "temperature freezing";
      	insert(a);
      	System.out.println( "AB1 Detect Alert : " + a);
      end
      
      rule "AB2 Clear"
      no-loop
      dialect "mvel" 
      when
      	$a : Alert( condition == "temperature freezing" )
      	OpDatum( id == "heater_powered", val == "True" ) or OpDatum( id == "temperature", Double.parseDouble(val) >= 9 ) 
      then
      	retract($a);
      	System.out.println("Alert "+$a+" retracted");
      end
      
      rule "OpDatum Housekeeping"
      salience 1000
      no-loop
      dialect "mvel" 
      when
          $oldOpDatum : OpDatum( $id : id  )
          $newOpDatum : OpDatum( this after $oldOpDatum, id == $id  )
      then
          retract($oldOpDatum);
      end
      
      Style B

      This implementation instead take the "Java methods" from the pattern,
      and when each one is extracted to their own "conditional element eval", then, it will work OK functionally and memory wise.

      package com.acme.drools6test.sessionclock;
      
      /*
      "reuse Java methods" Ref. [1]
      Using "import function" but then extracted each one is extracted to their own "conditional element eval" Ref. [2],
      which is /different/ from example "reuse Java methods" in the doc at [1].
      
      This works OK, functionally and memory wise.
       
      [1] http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e7102
      [2] http://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e8745
       */
      
      import function java.lang.Double.parseDouble
      
      import org.acme.model.*
      
      declare OpDatum
        @role(event)
        @timestamp(ts)
      end
      
      declare Alert
        @role(fact)
        condition : String
      end
      
      rule "AB1 Detect"
      no-loop
      dialect "mvel" 
      when
      	$oNumeric : OpDatum( id == "temperature", $val : val )
      	eval( parseDouble($val) < 9 )
      	$oBoolean : OpDatum( id == "heater_powered", val == "False" ) 
      	not( Alert( condition == "temperature freezing" ) )
      then
      	Alert a = new Alert();
      	a.condition = "temperature freezing";
      	insert(a);
      	System.out.println( "AB1 Detect Alert : " + a);
      end
      
      rule "AB2 Clear"
      no-loop
      dialect "mvel" 
      when
      	$a : Alert( condition == "temperature freezing" )
      	OpDatum( id == "heater_powered", val == "True" )
      		 or ( OpDatum( id == "temperature", $val : val ) and eval ( parseDouble($val) >= 9 ) ) 
      then
      	retract($a);
      	System.out.println("Alert "+$a+" retracted");
      end
      
      rule "OpDatum Housekeeping"
      salience 1000
      no-loop
      dialect "mvel" 
      when
          $oldOpDatum : OpDatum( $id : id  )
          $newOpDatum : OpDatum( this after $oldOpDatum, id == $id  )
      then
          retract($oldOpDatum);
      end
      
      Conclusion

      Kindly advise, I hope provided detailed explanation, and useful Java code to replicate.
      Thank you.
      Ciao, Matteo

      Attachments

        Activity

          People

            mfusco@redhat.com Mario Fusco
            mmortari@redhat.com Matteo Mortari
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: