apache / incubator-kie-issues

Apache License 2.0
12 stars 1 forks source link

NPE thrown when loading multiple DMN XML files including a Signavio's multi instance decision #1474

Closed samuel-beniamin closed 2 months ago

samuel-beniamin commented 2 months ago

Current behaviour

Currently when trying to load multiple DMN in a certain order, where an XML file that contain a MID (Multi Instance Decision) loads first, then load another XML file. We get the following exception:

[main] ERROR o.k.d.core.compiler.DMNCompilerImpl.compile:200 - Error compiling model from source.
java.lang.NullPointerException: Cannot invoke "org.kie.dmn.core.ast.DMNBaseNode.getDependencies()" because "node" is null
    at org.kie.dmn.core.internal.utils.DRGAnalysisUtils.internalDependencies(DRGAnalysisUtils.java:60)
    at org.kie.dmn.core.internal.utils.DRGAnalysisUtils.dependencies(DRGAnalysisUtils.java:54)
    at org.kie.dmn.signavio.MultiInstanceDecisionLogic$MIDDependenciesProcessor.findAllDependencies(MultiInstanceDecisionLogic.java:176)
    at org.kie.dmn.signavio.MultiInstanceDecisionLogic$MultiInstanceDecisionNodeCompiler.addRequiredDecisions(MultiInstanceDecisionLogic.java:147)
    at org.kie.dmn.signavio.MultiInstanceDecisionLogic$MultiInstanceDecisionNodeCompiler.lambda$compileEvaluator$1(MultiInstanceDecisionLogic.java:139)
    at org.kie.dmn.core.compiler.DMNCompilerImpl.processDrgElements(DMNCompilerImpl.java:482)
    at org.kie.dmn.core.compiler.DMNCompilerImpl.compile(DMNCompilerImpl.java:263)
    at org.kie.dmn.core.compiler.DMNCompilerImpl.compile(DMNCompilerImpl.java:198)
    at org.kie.dmn.core.assembler.DMNAssemblerService.compileResourceToModel(DMNAssemblerService.java:177)
    at org.kie.dmn.core.assembler.DMNAssemblerService.internalAddResource(DMNAssemblerService.java:152)
    at org.kie.dmn.core.assembler.DMNAssemblerService.addResourcesAfterRules(DMNAssemblerService.java:127)
    at org.kie.internal.services.KieAssemblersImpl.addResourcesAfterRules(KieAssemblersImpl.java:86)
    at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.buildAssemblerResourcesAfterRules(CompositeKnowledgeBuilderImpl.java:204)
    at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.build(CompositeKnowledgeBuilderImpl.java:124)
    at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.build(CompositeKnowledgeBuilderImpl.java:109)
    at org.drools.compiler.kie.builder.impl.AbstractKieProject.buildKnowledgePackages(AbstractKieProject.java:280)
    at org.drools.compiler.kie.builder.impl.AbstractKieProject.buildKnowledgePackages(AbstractKieProject.java:220)
    at org.drools.compiler.kie.builder.impl.AbstractKieProject.verify(AbstractKieProject.java:84)
    at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildKieProject(KieBuilderImpl.java:285)
    at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:251)
    at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:198)

What, after some debug, I see is happening:

After the introduction of the afterDRGcallbacks in the DMNCompilerImpl. The MultiInstanceDecisionLogic decided to use the callbacks. Which adds a callback to the DMNCompilerImpl which in turn executes this callback regardless of which model it is evaluating currently. This lead to the evaluation of some callbacks, which were originally registered/added for the MID on some other models. This leads to a NPE when trying to execute the call back on a different model.

Expected behaviour

Load multiple models from XML that contain MIDs and non MIDs in a any order, then we are able to build a KieBuilder for them all.

How to reproduce?

Load multiple files with Mids into a single runtime:

public void createRuntime() {  
    final KieServices ks = KieServices.Factory.get();  
    final KieFileSystem kfs = ks.newKieFileSystem();  

    KieModuleModel kmm = ks.newKieModuleModel();  
    kmm.setConfigurationProperty("org.kie.dmn.profiles.signavio", "org.kie.dmn.signavio.KieDMNSignavioProfile");  
    kfs.writeKModuleXML(kmm.toXML());

    // Where here both files contain Signavio MIDs 
    kfs.write(ks.getResources().newClassPathResource("file1.xml", this.getClass())
    kfs.write(ks.getResources().newClassPathResource("file2.xml", this.getClass())

    // Throws NPE
    KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll();
}
jomarko commented 2 months ago

Hi @yesamer @samuel-beniamin could you please clarify a little bit a term MID (Multi Instance Decision)? I think it is a new for me. Is there a xml/dmn file of such model? My intention is to double-check opening such model in KIE tooling. Thank you.

gitgabrio commented 2 months ago

@samuel-beniamin Could you please clarify if this issue relates to some implementation bug, or to the usage in some test ? I have the impression is the latter, but it is not clear at all...

samuel-beniamin commented 2 months ago

Hi @yesamer @samuel-beniamin could you please clarify a little bit a term MID (Multi Instance Decision)? I think it is a new for me. Is there a xml/dmn file of such model? My intention is to double-check opening such model in KIE tooling. Thank you.

Sure, it is a concept introduced by Signavio, inside Signavio DMN extension that allows iterating over a list of input values, executing a decision table, finally deciding the operation to do with the results after each iteration (e.g. collect, sum, max). Long

You can read more about it here https://www.signavio.com/post/decision-modeling-and-the-breakthrough-power-of-the-multi-instance-decision-mid/

For an example, please check the test resources under the following directory incubator-kie-drools/kie-dmn/kie-dmn-signavio/src/test/resources/org/kie/dmn/signavio. I hope that helps, please reach out if you need more information.

samuel-beniamin commented 2 months ago

@samuel-beniamin Could you please clarify if this issue relates to some implementation bug, or to the usage in some test ? I have the impression is the latter, but it is not clear at all...

Hello @gitgabrio it is actually an implementation bug, I would claim in the compiler is not checking which model is it currently evaluating, and this just calls the call back. I prevented the call in the callback itself, but maybe the proper fix should be in the DMNCompilerImpl.

Nonetheless, the fix is simple yes, just skip the call back if this is a call for a different model by:

if (cModel != model) {
     // Logs are secondary to skipping the rest of the execution.
     return;
}