Skip to content

Commit

Permalink
[incubator-kie-issues#1478] Regiser DRG callbacks to assigned models …
Browse files Browse the repository at this point in the history
…from DMNCompilerImpl
  • Loading branch information
samuel-beniamin committed Sep 30, 2024
1 parent 5cdf554 commit a713f41
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.xml.namespace.QName;

import org.drools.io.FileSystemResource;
import org.kie.api.io.Resource;
import org.kie.dmn.api.core.DMNCompiler;
Expand Down Expand Up @@ -113,8 +111,8 @@ public class DMNCompilerImpl implements DMNCompiler {
private static final Logger logger = LoggerFactory.getLogger( DMNCompilerImpl.class );

private final DMNDecisionLogicCompiler evaluatorCompiler;
private DMNCompilerConfiguration dmnCompilerConfig;
private Deque<DRGElementCompiler> drgCompilers = new LinkedList<>();
private final DMNCompilerConfiguration dmnCompilerConfig;
private final Deque<DRGElementCompiler> drgCompilers = new LinkedList<>();
{
drgCompilers.add( new InputDataCompiler() );
drgCompilers.add( new BusinessKnowledgeModelCompiler() );
Expand All @@ -123,6 +121,7 @@ public class DMNCompilerImpl implements DMNCompiler {
drgCompilers.add( new KnowledgeSourceCompiler() ); // keep last as it's a void compiler
}
private final List<AfterProcessDrgElements> afterDRGcallbacks = new ArrayList<>();
private final Map<DMNModel, List<AfterProcessDrgElements>> afterDRGcallbacksForModel = new HashMap<>();
private final static Pattern QNAME_PAT = Pattern.compile("(\\{([^\\}]*)\\})?(([^:]*):)?(.*)");

public DMNCompilerImpl() {
Expand Down Expand Up @@ -477,6 +476,9 @@ private void processDrgElements(DMNCompilerContext ctx, DMNModelImpl model, Defi
}
}

afterDRGcallbacksForModel.getOrDefault(model, Collections.emptyList()).
forEach(processDrgElements -> processDrgElements.callback(this, ctx, model));

for (AfterProcessDrgElements callback : afterDRGcallbacks) {
logger.debug("About to invoke callback: {}", callback);
callback.callback(this, ctx, model);
Expand All @@ -491,9 +493,30 @@ private void processDrgElements(DMNCompilerContext ctx, DMNModelImpl model, Defi
public interface AfterProcessDrgElements {
void callback(DMNCompilerImpl compiler, DMNCompilerContext ctx, DMNModelImpl model);
}


/**
* Adds a callback that will be invoked after the DRG elements have been processed, for all the following models that are compiled with this compiler instance.
* Which may lead to the callback being called multiple times, and for unrelated models. Unless, the callback is registered to a specific model using:
* {@link #addCallbackForModel(AfterProcessDrgElements, DMNModel)}.
*/
public void addCallback(AfterProcessDrgElements callback) {
this.afterDRGcallbacks.add(callback);
afterDRGcallbacks.add(callback);
}


/**
* Adds a callback that will be invoked after the DRG elements have been processed, for a given model.
*/
public void addCallbackForModel(AfterProcessDrgElements callback, DMNModel model) {
this.afterDRGcallbacksForModel.compute(model, (k, v) -> {
if (v == null) {
v = new ArrayList<>();
}
if (callback != null) {
v.add(callback);
}
return v;
});
}

private void detectCycles( DMNModelImpl model ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,51 @@
*/
package org.kie.dmn.core.compiler;

import java.io.StringReader;
import java.util.Collections;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.kie.dmn.api.core.DMNCompilerConfiguration;
import org.kie.dmn.api.core.DMNModel;
import org.kie.dmn.core.api.DMNFactory;
import org.kie.dmn.core.impl.DMNModelImpl;
import org.kie.dmn.model.api.DMNElementReference;
import org.kie.dmn.model.api.Definitions;
import org.kie.dmn.model.api.InformationRequirement;
import org.kie.dmn.model.v1_5.TDMNElementReference;
import org.kie.dmn.model.v1_5.TDefinitions;
import org.kie.dmn.model.v1_5.TInformationRequirement;
import org.mockito.Mockito;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

class DMNCompilerImplTest {

private static final String nameSpace = "http://www.montera.com.au/spec/DMN/local-hrefs";
private static Definitions parent;
private static DMNCompilerImpl dmnCompiler;
private static DMNCompilerImpl.AfterProcessDrgElements mockCallback;
private static DMNCompilerImpl.AfterProcessDrgElements mockCallbackForModel;

@BeforeAll
static void setup() {
String modelName = "LocalHrefs";
parent = new TDefinitions();
parent.setName(modelName);
parent.setNamespace(nameSpace);

DMNCompilerConfiguration config = DMNFactory.newCompilerConfiguration();
dmnCompiler = new DMNCompilerImpl(config);
mockCallback = Mockito.mock(DMNCompilerImpl.AfterProcessDrgElements.class);
dmnCompiler.addCallback(mockCallback);
mockCallbackForModel = Mockito.mock(DMNCompilerImpl.AfterProcessDrgElements.class);
}

@Test
Expand Down Expand Up @@ -76,4 +98,27 @@ void getRootElement() {
retrieved = DMNCompilerImpl.getRootElement(elementReference);
assertThat(retrieved).isNotNull().isEqualTo(parent);
}

@Test
void testCompileWithCallback() {
String dmnXml = """
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" id="definitions" name="definitions" namespace="http://www.trisotech.com/definitions/_f52ca843-504b-4c3b-a6bc-4d377bffef7a">
<decision id="decision" name="Decision">
<variable id="variable" name="Decision" typeRef="string"/>
</decision>
</definitions>""";
DMNModel mockModel = mock(DMNModel.class);
dmnCompiler.addCallbackForModel(mockCallbackForModel, mockModel);


Definitions definitions = dmnCompiler.getMarshaller().unmarshal(new StringReader(dmnXml));
DMNModel model = dmnCompiler.compile(definitions, Collections.emptyList());
dmnCompiler.addCallbackForModel(mockCallbackForModel, model);
dmnCompiler.compile(definitions, Collections.emptyList());


assertThat(model).isNotNull();
verify(mockCallback, times(2)).callback(eq(dmnCompiler), any(), any(DMNModelImpl.class));
verify(mockCallbackForModel, never()).callback(any(), any(), any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@
import org.kie.dmn.feel.runtime.functions.SumFunction;
import org.kie.dmn.feel.util.NumberEvalHelper;
import org.kie.dmn.model.api.DMNElement.ExtensionElements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
Expand All @@ -65,7 +63,6 @@
@XStreamAlias("MultiInstanceDecisionLogic")
public class MultiInstanceDecisionLogic {

private static Logger logger = LoggerFactory.getLogger(MultiInstanceDecisionLogic.class);

@XStreamAlias("iterationExpression")
private String iterationExpression;
Expand Down Expand Up @@ -135,17 +132,11 @@ public void compileEvaluator(DMNNode node, DMNCompilerImpl compiler, DMNCompiler
final MultiInstanceDecisionNodeEvaluator miEvaluator = new MultiInstanceDecisionNodeEvaluator(midl, model, di, ctx.getFeelHelper().newFEELInstance());
di.setEvaluator(miEvaluator);

compiler.addCallback((cCompiler, cCtx, cModel) -> {
if (cModel != model) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping MID processing for imported model: {}", cModel.getName());
}
return;
}
compiler.addCallbackForModel((cCompiler, cCtx, cModel) -> {
MIDDependenciesProcessor processor = new MIDDependenciesProcessor(midl, cModel);
addRequiredDecisions(miEvaluator, processor);
removeChildElementsFromIndex(cModel, processor);
});
}, model);
}

private void addRequiredDecisions(MultiInstanceDecisionNodeEvaluator miEvaluator,
Expand Down

0 comments on commit a713f41

Please sign in to comment.