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

NPE while retracting expired event facts after update version of rules in runtime

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Major Major
    • None
    • 7.74.1.Final
    • core engine
    • None
    • 2024 Week 07-09 (from Feb 12)
    • Hide

      Test Code

      @Data
      @Role(Role.Type.EVENT)
      @Timestamp("timestamp")
      @Expires("30s")
      public class DispatchEvent {
          @Key
          private String id;
          private Long timestamp;
      
          public DispatchEvent(String id) {
              this.id = id;
              this.timestamp = Instant.now().toEpochMilli();
          }
      }
      
      public class RetractExpiredEventNPEMain {
      
          public static final String DRL1 = "package org.example.drools;\n" +
                  "\n" +
                  "import org.example.drools.DispatchEvent;\n" +
                  "\n" +
                  "rule \"Old Rule\"\n" +
                  "when\n" +
                  "    $e : DispatchEvent($id : id) from entry-point \"events\"\n" +
                  "then\n" +
                  "    System.out.println(\"received event in old rule: \" + $id);\n" +
                  "end";
      
          public static final String DRL2 = "package org.example.drools;\n" +
                  "\n" +
                  "import org.example.drools.DispatchEvent;\n" +
                  "\n" +
                  "rule \"New Rule\"\n" +
                  "when\n" +
                  "    $e : DispatchEvent($id : id) from entry-point \"events\"\n" +
                  "then\n" +
                  "    System.out.println(\"received event in new rule=== \" + $id);\n" +
                  "end";
      
          public static void main(String[] args) {
      
              KieServices ks = KieServices.Factory.get();
              KieFileSystem kfs = ks.newKieFileSystem();
      
              KieModuleModel kmodel = ks.newKieModuleModel();
              kmodel.newKieBaseModel( "KBase")
                      .setDefault(true)
                      .addPackage("org.example.drools")
                      .setEventProcessingMode(EventProcessingOption.STREAM)
                      .newKieSessionModel("session1")
                      .setDefault(true);
      
              kfs.writeKModuleXML(kmodel.toXML());
      
              ReleaseId releaseId = ks.newReleaseId( "org.example.drools", "rules", "1.0.0-SNAPSHOT");
              kfs.generateAndWritePomXML(releaseId);
              String rulePath = "src/main/resources/org/example/drools/rules.drl";
              kfs.write(rulePath, ks.getResources().newByteArrayResource(DRL1.getBytes()).setResourceType(ResourceType.DRL));
              KieBuilder kieBuilder = ks.newKieBuilder(kfs, RetractExpiredEventNPEMain.class.getClassLoader());
              kieBuilder.buildAll();
      
              KieContainer kcontainer = ks.newKieContainer(releaseId);
              KieSession session = kcontainer.newKieSession("session1");
              EntryPoint events = session.getEntryPoint("events");
      
              EventFactHandle aaa = (EventFactHandle) events.insert(new DispatchEvent("aaa"));
      
              System.out.println("fire until halt...");
              Executors.newSingleThreadExecutor()
                      .submit(() -> {
                          try {
                              session.fireUntilHalt();
                          } catch (Throwable e) {
                              e.printStackTrace(System.err); // Print NPE
                          }
                      });
      
              try {
                  Thread.sleep(3000);
              } catch (InterruptedException e) {
                  return;
              }
      
              AbstractTerminalNode sink = aaa.getLinkedTuples().getFirstLeftTuple(0).getTupleSink();
              System.out.println("Tuple source not null: " + (sink.getLeftTupleSource() != null));
      
              kfs.generateAndWritePomXML(releaseId);
              kfs.write(rulePath, ks.getResources().newByteArrayResource(DRL2.getBytes()).setResourceType(ResourceType.DRL));
              kieBuilder = ks.newKieBuilder(kfs, RetractExpiredEventNPEMain.class.getClassLoader());
      
              System.out.println("build new rules...");
              kieBuilder.buildAll();
              System.out.println("update rule version...");
              kcontainer.updateToVersion(releaseId);
      
              System.out.println("Tuple source not null: " + (sink.getLeftTupleSource() != null));
      
              System.out.println("insert event bbb");
              events.insert(new DispatchEvent("bbb"));
      
              System.out.println("insert event ccc");
              events.insert(new DispatchEvent("ccc"));
      
              try {
                  System.out.println("wait 60s ...");
                  Thread.sleep(60000);
              } catch (InterruptedException e) {
                  return;
              }
      
              System.out.println("trigger retract expired facts...");
              events.insert(new DispatchEvent("check expire"));
      
              try {
                  Thread.sleep(3000);
              } catch (InterruptedException e) {
                  return;
              }
      
              System.out.println("FactHandles size=" + events.getFactCount());
              session.halt();
              kcontainer.dispose();
              System.exit(0);
          }
      

      Output:

      fire until halt...
      received event in old rule: aaa
      Tuple source not null: true
      build new rules...
      update rule version...
      Tuple source not null: false
      insert event bbb
      insert event ccc
      wait 60s ...
      received event in new rule=== bbb
      received event in new rule=== ccc
      java.lang.NullPointerException
      at org.drools.core.reteoo.ObjectTypeNode.lambda$retractLeftTuples$0(ObjectTypeNode.java:356)
      at org.drools.core.common.DefaultFactHandle$SingleLinkedTuples.forEachLeftTuple(DefaultFactHandle.java:739)
      at org.drools.core.common.DefaultFactHandle.forEachLeftTuple(DefaultFactHandle.java:935)
      at org.drools.core.reteoo.ObjectTypeNode.retractLeftTuples(ObjectTypeNode.java:354)
      at org.drools.core.common.DefaultAgenda.doRetract(DefaultAgenda.java:1466)
      at org.drools.core.common.DefaultAgenda.flushExpirations(DefaultAgenda.java:1458)

      Show
      Test Code @Data @Role(Role.Type.EVENT) @Timestamp( "timestamp" ) @Expires( "30s" ) public class DispatchEvent { @Key private String id; private Long timestamp; public DispatchEvent( String id) { this .id = id; this .timestamp = Instant.now().toEpochMilli(); } } public class RetractExpiredEventNPEMain { public static final String DRL1 = " package org.example.drools;\n" + "\n" + " import org.example.drools.DispatchEvent;\n" + "\n" + "rule \" Old Rule\ "\n" + "when\n" + " $e : DispatchEvent($id : id) from entry-point \" events\ "\n" + "then\n" + " System .out.println(\" received event in old rule: \ " + $id);\n" + "end" ; public static final String DRL2 = " package org.example.drools;\n" + "\n" + " import org.example.drools.DispatchEvent;\n" + "\n" + "rule \" New Rule\ "\n" + "when\n" + " $e : DispatchEvent($id : id) from entry-point \" events\ "\n" + "then\n" + " System .out.println(\" received event in new rule=== \ " + $id);\n" + "end" ; public static void main( String [] args) { KieServices ks = KieServices.Factory.get(); KieFileSystem kfs = ks.newKieFileSystem(); KieModuleModel kmodel = ks.newKieModuleModel(); kmodel.newKieBaseModel( "KBase" ) .setDefault( true ) .addPackage( "org.example.drools" ) .setEventProcessingMode(EventProcessingOption.STREAM) .newKieSessionModel( "session1" ) .setDefault( true ); kfs.writeKModuleXML(kmodel.toXML()); ReleaseId releaseId = ks.newReleaseId( "org.example.drools" , "rules" , "1.0.0-SNAPSHOT" ); kfs.generateAndWritePomXML(releaseId); String rulePath = "src/main/resources/org/example/drools/rules.drl" ; kfs.write(rulePath, ks.getResources().newByteArrayResource(DRL1.getBytes()).setResourceType(ResourceType.DRL)); KieBuilder kieBuilder = ks.newKieBuilder(kfs, RetractExpiredEventNPEMain. class. getClassLoader()); kieBuilder.buildAll(); KieContainer kcontainer = ks.newKieContainer(releaseId); KieSession session = kcontainer.newKieSession( "session1" ); EntryPoint events = session.getEntryPoint( "events" ); EventFactHandle aaa = (EventFactHandle) events.insert( new DispatchEvent( "aaa" )); System .out.println( "fire until halt..." ); Executors.newSingleThreadExecutor() .submit(() -> { try { session.fireUntilHalt(); } catch (Throwable e) { e.printStackTrace( System .err); // Print NPE } }); try { Thread .sleep(3000); } catch (InterruptedException e) { return ; } AbstractTerminalNode sink = aaa.getLinkedTuples().getFirstLeftTuple(0).getTupleSink(); System .out.println( "Tuple source not null : " + (sink.getLeftTupleSource() != null )); kfs.generateAndWritePomXML(releaseId); kfs.write(rulePath, ks.getResources().newByteArrayResource(DRL2.getBytes()).setResourceType(ResourceType.DRL)); kieBuilder = ks.newKieBuilder(kfs, RetractExpiredEventNPEMain. class. getClassLoader()); System .out.println( "build new rules..." ); kieBuilder.buildAll(); System .out.println( "update rule version..." ); kcontainer.updateToVersion(releaseId); System .out.println( "Tuple source not null : " + (sink.getLeftTupleSource() != null )); System .out.println( "insert event bbb" ); events.insert( new DispatchEvent( "bbb" )); System .out.println( "insert event ccc" ); events.insert( new DispatchEvent( "ccc" )); try { System .out.println( "wait 60s ..." ); Thread .sleep(60000); } catch (InterruptedException e) { return ; } System .out.println( "trigger retract expired facts..." ); events.insert( new DispatchEvent( "check expire" )); try { Thread .sleep(3000); } catch (InterruptedException e) { return ; } System .out.println( "FactHandles size=" + events.getFactCount()); session.halt(); kcontainer.dispose(); System .exit(0); } Output: fire until halt... received event in old rule: aaa Tuple source not null: true build new rules... update rule version... Tuple source not null: false insert event bbb insert event ccc wait 60s ... received event in new rule=== bbb received event in new rule=== ccc java.lang.NullPointerException at org.drools.core.reteoo.ObjectTypeNode.lambda$retractLeftTuples$0(ObjectTypeNode.java:356) at org.drools.core.common.DefaultFactHandle$SingleLinkedTuples.forEachLeftTuple(DefaultFactHandle.java:739) at org.drools.core.common.DefaultFactHandle.forEachLeftTuple(DefaultFactHandle.java:935) at org.drools.core.reteoo.ObjectTypeNode.retractLeftTuples(ObjectTypeNode.java:354) at org.drools.core.common.DefaultAgenda.doRetract(DefaultAgenda.java:1466) at org.drools.core.common.DefaultAgenda.flushExpirations(DefaultAgenda.java:1458)
    • NEW
    • NEW
    • ---
    • ---

      //ObjectTypeNode.retractLeftTuples(...)
      public static void retractLeftTuples( InternalFactHandle factHandle, PropagationContext context, InternalWorkingMemory workingMemory ) {
              factHandle.forEachLeftTuple( lt -> {
                  LeftTupleSink sink = lt.getTupleSink();
                  ((LeftInputAdapterNode) sink.getLeftTupleSource()).retractLeftTuple(lt,
                                                                                      context,
                                                                                      workingMemory);
              } );
              factHandle.clearLeftTuples();
      }
      

      sink.getLeftTupleSource() will be null after rule updated in work memory.

              mfusco@redhat.com Mario Fusco
              gy.wang.1983 Gy Wang (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated:
                Resolved: