From 059e505f1a913e62b868a411c68e72231b037721 Mon Sep 17 00:00:00 2001 From: Gabriele Cardosi Date: Mon, 25 Jan 2021 11:21:47 +0100 Subject: [PATCH 1/6] [DROOLS-5964] Begin implementation. Moved exceptions to specific package --- .../api/model/ScenarioSimulationModel.java | 5 +- .../api/model/Settings.java | 20 ++ .../pom.xml | 11 + .../ImpossibleToFindDMNException.java | 2 +- .../ImpossibleToFindPMMLException.java | 31 +++ .../ScenarioAssumptionViolatedException.java | 2 +- .../fluent/PMMLScenarioExecutableBuilder.java | 75 ++++++ .../runner/PMMLScenarioRunnerHelper.java | 206 +++++++++++++++ .../backend/util/DMNSimulationUtils.java | 1 + .../util/InMemoryMigrationStrategy.java | 7 + .../backend/util/MigrationStrategy.java | 6 + .../ScenarioSimulationXMLPersistence.java | 2 + .../backend/util/DMNSimulationUtilsTest.java | 1 + .../ScenarioSimulationXMLPersistenceTest.java | 18 ++ .../folderToTest/scesim-1-8-dmn.scesim | 235 ++++++++++++++++++ .../folderToTest/scesim-1-8-rule.scesim | 132 ++++++++++ .../core/utils/PMMLRequestDataBuilder.java | 9 + 17 files changed, 759 insertions(+), 4 deletions(-) rename drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/{util => exceptions}/ImpossibleToFindDMNException.java (94%) create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ImpossibleToFindPMMLException.java rename drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/{runner => exceptions}/ScenarioAssumptionViolatedException.java (96%) create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-dmn.scesim create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-rule.scesim diff --git a/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/ScenarioSimulationModel.java b/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/ScenarioSimulationModel.java index 4c96a28f2b4..891e318d00e 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/ScenarioSimulationModel.java +++ b/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/ScenarioSimulationModel.java @@ -25,11 +25,12 @@ public class ScenarioSimulationModel public enum Type { RULE, - DMN + DMN, + PMML } @XStreamAsAttribute() - private String version = "1.8"; + private String version = "1.9"; private Simulation simulation; diff --git a/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/Settings.java b/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/Settings.java index 18644ddaae3..5941ac707ce 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/Settings.java +++ b/drools-scenario-simulation/drools-scenario-simulation-api/src/main/java/org/drools/scenariosimulation/api/model/Settings.java @@ -24,6 +24,8 @@ public class Settings { private String dmnFilePath; + private String pmmlFilePath; + private ScenarioSimulationModel.Type type; private String fileName; @@ -38,6 +40,8 @@ public class Settings { private String dmnName; + private String pmmlModelName; + private boolean skipFromBuild = false; private boolean stateless = false; @@ -57,6 +61,14 @@ public void setDmnFilePath(String dmnFilePath) { this.dmnFilePath = dmnFilePath; } + public String getPmmlFilePath() { + return pmmlFilePath; + } + + public void setPmmlFilePath(String pmmlFilePath) { + this.pmmlFilePath = pmmlFilePath; + } + public ScenarioSimulationModel.Type getType() { return type; } @@ -137,6 +149,14 @@ public void setDmnName(String dmnName) { this.dmnName = dmnName; } + public String getPmmlModelName() { + return pmmlModelName; + } + + public void setPmmlModelName(String pmmlModelName) { + this.pmmlModelName = pmmlModelName; + } + public boolean isSkipFromBuild() { return skipFromBuild; } diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml b/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml index 84abf63ec1f..908f715fe1a 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml @@ -76,6 +76,16 @@ kie-dmn-core + + + org.kie + kie-pmml-dependencies + + + org.kie + kie-pmml-evaluator-core + + org.assertj @@ -83,6 +93,7 @@ test + diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/ImpossibleToFindDMNException.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ImpossibleToFindDMNException.java similarity index 94% rename from drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/ImpossibleToFindDMNException.java rename to drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ImpossibleToFindDMNException.java index 9bd8228432d..63b553c10d8 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/ImpossibleToFindDMNException.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ImpossibleToFindDMNException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.drools.scenariosimulation.backend.util; +package org.drools.scenariosimulation.backend.exceptions; /** * Utility that provide classPath scan to retrieve resources diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ImpossibleToFindPMMLException.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ImpossibleToFindPMMLException.java new file mode 100644 index 00000000000..5f460356a3a --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ImpossibleToFindPMMLException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.drools.scenariosimulation.backend.exceptions; + +public class ImpossibleToFindPMMLException extends IllegalArgumentException { + + public ImpossibleToFindPMMLException(String message) { + super(message); + } + + public ImpossibleToFindPMMLException(String message, Throwable cause) { + super(message, cause); + } + + public ImpossibleToFindPMMLException(Throwable cause) { + super(cause.getMessage(), cause); + } +} diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/ScenarioAssumptionViolatedException.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ScenarioAssumptionViolatedException.java similarity index 96% rename from drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/ScenarioAssumptionViolatedException.java rename to drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ScenarioAssumptionViolatedException.java index b7180a2f188..7b0438529c6 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/ScenarioAssumptionViolatedException.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/exceptions/ScenarioAssumptionViolatedException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.drools.scenariosimulation.backend.runner; +package org.drools.scenariosimulation.backend.exceptions; import org.drools.scenariosimulation.api.model.Scenario; import org.drools.scenariosimulation.backend.runner.model.ScenarioResult; diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java new file mode 100644 index 00000000000..78c2a7a882f --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java @@ -0,0 +1,75 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.drools.scenariosimulation.backend.fluent; + +import java.util.Objects; + +import org.drools.core.command.RequestContextImpl; +import org.kie.api.KieBase; +import org.kie.api.pmml.PMML4Result; +import org.kie.api.pmml.PMMLRequestData; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.KieRuntimeFactory; +import org.kie.api.runtime.RequestContext; +import org.kie.pmml.api.exceptions.KiePMMLException; +import org.kie.pmml.api.models.PMMLModel; +import org.kie.pmml.api.runtime.PMMLRuntime; +import org.kie.pmml.evaluator.core.PMMLContextImpl; +import org.kie.pmml.evaluator.core.utils.PMMLRequestDataBuilder; + +public class PMMLScenarioExecutableBuilder { + + public static final String DEFAULT_APPLICATION = "defaultApplication"; + public static final String PMML_RESULT = "pmmlResult"; + public static final String PMML_MODEL = "pmmlModel"; + + private final KieContainer kieContainer; + private final PMMLRequestDataBuilder pmmlRequestDataBuilder; + private final String pmmlFilePath; + + private PMMLScenarioExecutableBuilder(KieContainer kieContainer, String pmmlFilePath, String pmmlModelName) { + this.kieContainer = kieContainer; + this.pmmlFilePath = pmmlFilePath; + String fileName = pmmlFilePath.contains("/") ? pmmlFilePath.substring(pmmlFilePath.lastIndexOf("/") +1) : pmmlFilePath; + pmmlRequestDataBuilder = new PMMLRequestDataBuilder("correlationid", pmmlModelName, fileName); + } + + public static PMMLScenarioExecutableBuilder createBuilder(KieContainer kieContainer, String pmmlFilePath, + String pmmlModelName) { + return new PMMLScenarioExecutableBuilder(kieContainer, pmmlFilePath, pmmlModelName); + } + + public void setValue(String key, Object value) { + Class class1 = value.getClass(); + pmmlRequestDataBuilder.addParameter(key, value, class1); + } + + public RequestContext run() { + Objects.requireNonNull(kieContainer, "KieContainer is null"); + final PMMLRequestData pmmlRequestData = pmmlRequestDataBuilder.build(); + final KieBase kieBase = kieContainer.getKieBase(); + final KieRuntimeFactory kieRuntimeFactory = KieRuntimeFactory.of(kieBase); + final PMMLRuntime pmmlRuntime = kieRuntimeFactory.get(PMMLRuntime.class); + final PMMLModel pmmlModel = pmmlRuntime.getPMMLModel(pmmlRequestData.getModelName()) + .orElseThrow(() -> new KiePMMLException("Failed to retrieve model with name " + pmmlRequestData.getModelName())); + final PMML4Result pmml4Result = pmmlRuntime.evaluate(pmmlRequestData.getModelName(), new PMMLContextImpl(pmmlRequestData)); + final RequestContext toReturn = new RequestContextImpl(); + toReturn.setOutput(PMML_RESULT, pmml4Result); + toReturn.setOutput(PMML_MODEL, pmmlModel); + return toReturn; + } +} diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java new file mode 100644 index 00000000000..f4ed49655bd --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java @@ -0,0 +1,206 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.drools.scenariosimulation.backend.runner; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.drools.scenariosimulation.api.model.ExpressionElement; +import org.drools.scenariosimulation.api.model.ExpressionIdentifier; +import org.drools.scenariosimulation.api.model.FactIdentifier; +import org.drools.scenariosimulation.api.model.FactMapping; +import org.drools.scenariosimulation.api.model.FactMappingValue; +import org.drools.scenariosimulation.api.model.ScenarioSimulationModel; +import org.drools.scenariosimulation.api.model.ScenarioWithIndex; +import org.drools.scenariosimulation.api.model.ScesimModelDescriptor; +import org.drools.scenariosimulation.api.model.Settings; +import org.drools.scenariosimulation.backend.expression.ExpressionEvaluator; +import org.drools.scenariosimulation.backend.expression.ExpressionEvaluatorFactory; +import org.drools.scenariosimulation.backend.fluent.DMNScenarioExecutableBuilder; +import org.drools.scenariosimulation.backend.fluent.PMMLScenarioExecutableBuilder; +import org.drools.scenariosimulation.backend.runner.model.InstanceGiven; +import org.drools.scenariosimulation.backend.runner.model.ScenarioExpect; +import org.drools.scenariosimulation.backend.runner.model.ScenarioResult; +import org.drools.scenariosimulation.backend.runner.model.ScenarioResultMetadata; +import org.drools.scenariosimulation.backend.runner.model.ScenarioRunnerData; +import org.drools.scenariosimulation.backend.runner.model.ValueWrapper; +import org.kie.api.pmml.PMML4Result; +import org.kie.api.runtime.KieContainer; +import org.kie.dmn.api.core.DMNDecisionResult; +import org.kie.dmn.api.core.DMNResult; +import org.kie.pmml.api.models.PMMLModel; + +import static org.drools.scenariosimulation.backend.runner.model.ValueWrapper.errorWithMessage; +import static org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.SUCCEEDED; + +public class PMMLScenarioRunnerHelper extends AbstractRunnerHelper { + + @Override + protected Map executeScenario(KieContainer kieContainer, + ScenarioRunnerData scenarioRunnerData, + ExpressionEvaluatorFactory expressionEvaluatorFactory, + ScesimModelDescriptor scesimModelDescriptor, + Settings settings) { + if (!ScenarioSimulationModel.Type.PMML.equals(settings.getType())) { + throw new ScenarioException("Impossible to run a not-PMML simulation with DMN runner"); + } + PMMLScenarioExecutableBuilder executableBuilder = createBuilderWrapper(kieContainer, settings.getPmmlFilePath(), settings.getPmmlModelName()); + + loadInputData(scenarioRunnerData.getBackgrounds(), executableBuilder); + loadInputData(scenarioRunnerData.getGivens(), executableBuilder); + + return executableBuilder.run().getOutputs(); + } + + protected void loadInputData(List dataToLoad, PMMLScenarioExecutableBuilder executableBuilder) { + for (InstanceGiven input : dataToLoad) { + executableBuilder.setValue(input.getFactIdentifier().getName(), input.getValue()); + } + } + + @Override + protected ScenarioResultMetadata extractResultMetadata(Map requestContext, + ScenarioWithIndex scenarioWithIndex) { + PMMLModel pmmlModel = (PMMLModel) requestContext.get(PMMLScenarioExecutableBuilder.PMML_MODEL); + PMML4Result pmml4Result = (PMML4Result) requestContext.get(PMMLScenarioExecutableBuilder.PMML_RESULT); + + ScenarioResultMetadata scenarioResultMetadata = new ScenarioResultMetadata(scenarioWithIndex); +// for (DecisionNode decision : pmmlModel.getDecisions()) { +// scenarioResultMetadata.addAvailable(decision.getName()); +// } +// final AtomicInteger counter = new AtomicInteger(0); +// for (DMNDecisionResult decisionResult : pmml4Result.getDecisionResults()) { +// if (SUCCEEDED.equals(decisionResult.getEvaluationStatus())) { +// scenarioResultMetadata.addExecuted(decisionResult.getDecisionName()); +// } +// if (decisionResult.getMessages().isEmpty()) { +// scenarioResultMetadata.addAuditMessage(counter.addAndGet(1), +// decisionResult.getDecisionName(), +// decisionResult.getEvaluationStatus().name()); +// } else { +// decisionResult.getMessages().forEach(dmnMessage -> scenarioResultMetadata.addAuditMessage(counter.addAndGet(1), +// decisionResult.getDecisionName(), +// decisionResult.getEvaluationStatus().name(), +// dmnMessage.getLevel().name() + ": " + dmnMessage.getText())); +// } +// } + return scenarioResultMetadata; + } + + @Override + protected void verifyConditions(ScesimModelDescriptor scesimModelDescriptor, + ScenarioRunnerData scenarioRunnerData, + ExpressionEvaluatorFactory expressionEvaluatorFactory, + Map requestContext) { + DMNResult dmnResult = (DMNResult) requestContext.get(DMNScenarioExecutableBuilder.DMN_RESULT); + + for (ScenarioExpect output : scenarioRunnerData.getExpects()) { + FactIdentifier factIdentifier = output.getFactIdentifier(); + String decisionName = factIdentifier.getName(); + DMNDecisionResult decisionResult = dmnResult.getDecisionResultByName(decisionName); + if (decisionResult == null) { + throw new ScenarioException("DMN execution has not generated a decision result with name " + decisionName); + } + + for (FactMappingValue expectedResult : output.getExpectedResult()) { + ExpressionIdentifier expressionIdentifier = expectedResult.getExpressionIdentifier(); + + FactMapping factMapping = scesimModelDescriptor.getFactMapping(factIdentifier, expressionIdentifier) + .orElseThrow(() -> new IllegalStateException("Wrong expression, this should not happen")); + + ExpressionEvaluator expressionEvaluator = expressionEvaluatorFactory.getOrCreate(expectedResult); + + ScenarioResult scenarioResult = fillResult(expectedResult, + () -> getSingleFactValueResult(factMapping, expectedResult, decisionResult, expressionEvaluator), + expressionEvaluator); + + scenarioRunnerData.addResult(scenarioResult); + } + } + } + + @SuppressWarnings("unchecked") + protected ValueWrapper getSingleFactValueResult(FactMapping factMapping, + FactMappingValue expectedResult, + DMNDecisionResult decisionResult, + ExpressionEvaluator expressionEvaluator) { + Object resultRaw = decisionResult.getResult(); + final DMNDecisionResult.DecisionEvaluationStatus evaluationStatus = decisionResult.getEvaluationStatus(); + if (!SUCCEEDED.equals(evaluationStatus)) { + return errorWithMessage("The decision " + + decisionResult.getDecisionName() + + " has not been successfully evaluated: " + + evaluationStatus); + } + + List elementsWithoutClass = factMapping.getExpressionElementsWithoutClass(); + + // DMN engine doesn't generate the whole object when no entry of the decision table match + if (resultRaw != null) { + for (ExpressionElement expressionElement : elementsWithoutClass) { + if (!(resultRaw instanceof Map)) { + throw new ScenarioException("Wrong resultRaw structure because it is not a complex type as expected"); + } + Map result = (Map) resultRaw; + resultRaw = result.get(expressionElement.getStep()); + } + } + + Class resultClass = resultRaw != null ? resultRaw.getClass() : null; + + Object expectedResultRaw = expectedResult.getRawValue(); + return getResultWrapper(factMapping.getClassName(), + expectedResult, + expressionEvaluator, + expectedResultRaw, + resultRaw, + resultClass); + } + + @SuppressWarnings("unchecked") + @Override + protected Object createObject(ValueWrapper initialInstance, String className, Map, Object> params, ClassLoader classLoader) { + // simple types + if (initialInstance.isValid() && !(initialInstance.getValue() instanceof Map)) { + return initialInstance.getValue(); + } + Map toReturn = (Map) initialInstance.orElseGet(HashMap::new); + for (Map.Entry, Object> listObjectEntry : params.entrySet()) { + + // direct mapping already considered + if (listObjectEntry.getKey().isEmpty()) { + continue; + } + + List allSteps = listObjectEntry.getKey(); + List steps = allSteps.subList(0, allSteps.size() - 1); + String lastStep = allSteps.get(allSteps.size() - 1); + + Map targetMap = toReturn; + for (String step : steps) { + targetMap = (Map) targetMap.computeIfAbsent(step, k -> new HashMap<>()); + } + targetMap.put(lastStep, listObjectEntry.getValue()); + } + return toReturn; + } + + protected PMMLScenarioExecutableBuilder createBuilderWrapper(KieContainer kieContainer, String pmmlFilePath, String pmmlModelName) { + return PMMLScenarioExecutableBuilder.createBuilder(kieContainer, pmmlFilePath, pmmlModelName); + } +} diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtils.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtils.java index 5eb12282617..76ade7325e0 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtils.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtils.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.List; +import org.drools.scenariosimulation.backend.exceptions.ImpossibleToFindDMNException; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieRuntimeFactory; import org.kie.dmn.api.core.DMNModel; diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java index ed818da1ace..275bebd9845 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java @@ -235,6 +235,13 @@ public ThrowingConsumer from1_7to1_8() { }; } + @Override + public ThrowingConsumer from1_8to1_9() { + return document -> { + updateVersion(document, "1.9"); + }; + } + private void replaceReference(List simulationFactMappingNodeList, Node containerNode, String referredNodeName) { final List referredNodesList = DOMParserUtil.getChildrenNodesList(containerNode, referredNodeName); if (!referredNodesList.isEmpty()) { diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/MigrationStrategy.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/MigrationStrategy.java index 35d8a6f5787..d9a482bbd7c 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/MigrationStrategy.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/MigrationStrategy.java @@ -80,6 +80,12 @@ default ThrowingConsumer start() { */ ThrowingConsumer from1_7to1_8(); + /** + * Method to obtain the migration function from 1.8 to 1.9 + * @return + */ + ThrowingConsumer from1_8to1_9(); + /** * Method to complete the migration. For instance it can be used to store the new value * @return diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistence.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistence.java index dbc16a3e9b4..7e3f87d5947 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistence.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistence.java @@ -158,6 +158,8 @@ public String migrateIfNecessary(String rawXml) throws Exception { migrator = migrator.andThen(getMigrationStrategy().from1_6to1_7()); case "1.7": migrator = migrator.andThen(getMigrationStrategy().from1_7to1_8()); + case "1.8": + migrator = migrator.andThen(getMigrationStrategy().from1_8to1_9()); supported = true; break; default: diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtilsTest.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtilsTest.java index b012b358a15..b16a861a072 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtilsTest.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/DMNSimulationUtilsTest.java @@ -21,6 +21,7 @@ import java.util.stream.Stream; import org.assertj.core.api.Assertions; +import org.drools.scenariosimulation.backend.exceptions.ImpossibleToFindDMNException; import org.junit.Test; import org.junit.runner.RunWith; import org.kie.api.io.Resource; diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java index d32869cabcc..07a8f6d3973 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java @@ -271,6 +271,24 @@ public void migrateIfNecessary_1_7_to_1_8() throws Exception { commonCheckFactMappingValueType(document, BACKGROUND_NODE); } + @Test + public void migrateIfNecessary_1_8_to_1_9() throws Exception { + String toMigrate = getFileContent("scesim-1-8-dmn.scesim"); + Document document = DOMParserUtil.getDocument(toMigrate); + migrationInstance.from1_8to1_9().accept(document); + commonCheck(toMigrate, document, "1.9"); + commonCheckBackground(document); + commonCheckFactMappingValueType(document, SIMULATION_NODE); + commonCheckFactMappingValueType(document, BACKGROUND_NODE); + toMigrate = getFileContent("scesim-1-8-rule.scesim"); + document = DOMParserUtil.getDocument(toMigrate); + migrationInstance.from1_8to1_9().accept(document); + commonCheck(toMigrate, document, "1.9"); + commonCheckBackground(document); + commonCheckFactMappingValueType(document, SIMULATION_NODE); + commonCheckFactMappingValueType(document, BACKGROUND_NODE); + } + private void commonCheckFactMappingValueType(Document document, String scesimModel) { List factMappingsNodes = DOMParserUtil.getNestedChildrenNodesList(document, scesimModel, SIMULATION_DESCRIPTOR_NODE, FACT_MAPPINGS_NODE); assertNotNull(factMappingsNodes); diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-dmn.scesim b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-dmn.scesim new file mode 100644 index 00000000000..4c560496a01 --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-dmn.scesim @@ -0,0 +1,235 @@ + + + + + + + + + Index + OTHER + + + # + java.lang.Integer + + java.lang.Integer + # + 70.0 + NOT_EXPRESSION + + + + + Description + OTHER + + + Scenario description + java.lang.String + + java.lang.String + Scenario description + 300.0 + NOT_EXPRESSION + + + + + person + + + address + + + + 0|1 + GIVEN + + + person + person + + java.util.List + person + address + + address + + 114.0 + NOT_EXPRESSION + + + + + person + + + age + + + + 0|2 + GIVEN + + + person + person + + age + person + age + + 114.0 + NOT_EXPRESSION + + + + + is the first country European? + + + + 0|3 + EXPECT + + + is the first country European? + is the first country European? + + boolean + is the first country European? + value + + 114.0 + NOT_EXPRESSION + + + + + is young? + + + + 0|4 + EXPECT + + + is young? + is young? + + boolean + is young? + value + + 114.0 + NOT_EXPRESSION + + + + + + + + + Scenario description + java.lang.String + + + Description + OTHER + + + + + person + person + + + 0|1 + GIVEN + + + + + person + person + + + 0|2 + GIVEN + + + + + is the first country European? + is the first country European? + + + 0|3 + EXPECT + + + + + is young? + is young? + + + 0|4 + EXPECT + + + + + + + + + + + src/main/resources/com/list.dmn + DMN + https://github.com/kiegroup/drools/kie-dmn/_CC8924B0-D729-4D70-9588-039B5824FFE9 + a1Collection + false + false + + + + + + NOT_EXPRESSION + + + 1|1 + GIVEN + + + Empty + java.lang.Void + + java.lang.Void + Instance 1 + PROPERTY 1 + + + + + + + + + Empty + java.lang.Void + + + 1|1 + GIVEN + + + + + + + diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-rule.scesim b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-rule.scesim new file mode 100644 index 00000000000..9f864254245 --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-1-8-rule.scesim @@ -0,0 +1,132 @@ + + + + + + + + + Index + OTHER + + + # + java.lang.Integer + + java.lang.Integer + # + 70.0 + NOT_EXPRESSION + + + + + Description + OTHER + + + Scenario description + java.lang.String + + java.lang.String + Scenario description + 300.0 + NOT_EXPRESSION + + + + + 0|1 + GIVEN + + + Empty + java.lang.Void + + java.lang.Void + INSTANCE 1 + PROPERTY 1 + 114.0 + NOT_EXPRESSION + + + + + 0|2 + EXPECT + + + java.lang.Void + INSTANCE 2 + PROPERTY 2 + 114.0 + NOT_EXPRESSION + + + + + + + + + + + + + + + + + + + + + + + + + + + + default + RULE + false + + + + + + NOT_EXPRESSION + + + 1|1 + GIVEN + + + Empty + java.lang.Void + + java.lang.Void + Instance 1 + PROPERTY 1 + + + + + + + + + Empty + java.lang.Void + + + 1|1 + GIVEN + + + + + + + \ No newline at end of file diff --git a/kie-pmml-trusty/kie-pmml-evaluator/kie-pmml-evaluator-core/src/main/java/org/kie/pmml/evaluator/core/utils/PMMLRequestDataBuilder.java b/kie-pmml-trusty/kie-pmml-evaluator/kie-pmml-evaluator-core/src/main/java/org/kie/pmml/evaluator/core/utils/PMMLRequestDataBuilder.java index e8a21a02383..b896918f72a 100644 --- a/kie-pmml-trusty/kie-pmml-evaluator/kie-pmml-evaluator-core/src/main/java/org/kie/pmml/evaluator/core/utils/PMMLRequestDataBuilder.java +++ b/kie-pmml-trusty/kie-pmml-evaluator/kie-pmml-evaluator-core/src/main/java/org/kie/pmml/evaluator/core/utils/PMMLRequestDataBuilder.java @@ -25,8 +25,14 @@ public class PMMLRequestDataBuilder { private String correlationId; private String modelName; + private String source; private List> parameters; + public PMMLRequestDataBuilder(String correlationId, String modelName, String source) { + this(correlationId, modelName); + this.source = source; + } + public PMMLRequestDataBuilder(String correlationId, String modelName) { this.correlationId = correlationId; this.modelName = modelName; @@ -41,6 +47,9 @@ public PMMLRequestDataBuilder addParameter(String paramName, T value, Class< public PMMLRequestData build() { PMMLRequestData data = new PMMLRequestData(correlationId, modelName); parameters.forEach(data::addRequestParam); + if (source != null) { + data.setSource(source); + } return data; } } From c501ab70299bb1580375612f6ad9391825b35b92 Mon Sep 17 00:00:00 2001 From: Gabriele Cardosi Date: Mon, 25 Jan 2021 15:59:45 +0100 Subject: [PATCH 2/6] [DROOLS-5964] Implemented PMMLScenarioRunnerHelper with tests. Updated version of scesim-dmn/scesim-rule --- .../runner/PMMLScenarioRunnerHelper.java | 98 ++--- .../runner/PMMLScenarioRunnerHelperTest.java | 334 ++++++++++++++++++ .../resources/folderToTest/scesim-dmn.scesim | 2 +- .../resources/folderToTest/scesim-rule.scesim | 2 +- 4 files changed, 357 insertions(+), 79 deletions(-) create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java index f4ed49655bd..34874ffda66 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelper.java @@ -16,11 +16,10 @@ package org.drools.scenariosimulation.backend.runner; -import java.util.HashMap; +import java.util.Collection; import java.util.List; import java.util.Map; -import org.drools.scenariosimulation.api.model.ExpressionElement; import org.drools.scenariosimulation.api.model.ExpressionIdentifier; import org.drools.scenariosimulation.api.model.FactIdentifier; import org.drools.scenariosimulation.api.model.FactMapping; @@ -31,7 +30,6 @@ import org.drools.scenariosimulation.api.model.Settings; import org.drools.scenariosimulation.backend.expression.ExpressionEvaluator; import org.drools.scenariosimulation.backend.expression.ExpressionEvaluatorFactory; -import org.drools.scenariosimulation.backend.fluent.DMNScenarioExecutableBuilder; import org.drools.scenariosimulation.backend.fluent.PMMLScenarioExecutableBuilder; import org.drools.scenariosimulation.backend.runner.model.InstanceGiven; import org.drools.scenariosimulation.backend.runner.model.ScenarioExpect; @@ -41,12 +39,10 @@ import org.drools.scenariosimulation.backend.runner.model.ValueWrapper; import org.kie.api.pmml.PMML4Result; import org.kie.api.runtime.KieContainer; -import org.kie.dmn.api.core.DMNDecisionResult; -import org.kie.dmn.api.core.DMNResult; +import org.kie.pmml.api.enums.ResultCode; import org.kie.pmml.api.models.PMMLModel; import static org.drools.scenariosimulation.backend.runner.model.ValueWrapper.errorWithMessage; -import static org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.SUCCEEDED; public class PMMLScenarioRunnerHelper extends AbstractRunnerHelper { @@ -57,7 +53,7 @@ protected Map executeScenario(KieContainer kieContainer, ScesimModelDescriptor scesimModelDescriptor, Settings settings) { if (!ScenarioSimulationModel.Type.PMML.equals(settings.getType())) { - throw new ScenarioException("Impossible to run a not-PMML simulation with DMN runner"); + throw new ScenarioException("Impossible to run a not-PMML simulation with PMML runner"); } PMMLScenarioExecutableBuilder executableBuilder = createBuilderWrapper(kieContainer, settings.getPmmlFilePath(), settings.getPmmlModelName()); @@ -80,25 +76,10 @@ protected ScenarioResultMetadata extractResultMetadata(Map reque PMML4Result pmml4Result = (PMML4Result) requestContext.get(PMMLScenarioExecutableBuilder.PMML_RESULT); ScenarioResultMetadata scenarioResultMetadata = new ScenarioResultMetadata(scenarioWithIndex); -// for (DecisionNode decision : pmmlModel.getDecisions()) { -// scenarioResultMetadata.addAvailable(decision.getName()); -// } -// final AtomicInteger counter = new AtomicInteger(0); -// for (DMNDecisionResult decisionResult : pmml4Result.getDecisionResults()) { -// if (SUCCEEDED.equals(decisionResult.getEvaluationStatus())) { -// scenarioResultMetadata.addExecuted(decisionResult.getDecisionName()); -// } -// if (decisionResult.getMessages().isEmpty()) { -// scenarioResultMetadata.addAuditMessage(counter.addAndGet(1), -// decisionResult.getDecisionName(), -// decisionResult.getEvaluationStatus().name()); -// } else { -// decisionResult.getMessages().forEach(dmnMessage -> scenarioResultMetadata.addAuditMessage(counter.addAndGet(1), -// decisionResult.getDecisionName(), -// decisionResult.getEvaluationStatus().name(), -// dmnMessage.getLevel().name() + ": " + dmnMessage.getText())); -// } -// } + if (ResultCode.OK.getName().equals(pmml4Result.getResultCode())) { + pmml4Result.getResultVariables().keySet().forEach(scenarioResultMetadata::addAvailable); + scenarioResultMetadata.addExecuted(pmmlModel.getName()); + } return scenarioResultMetadata; } @@ -107,16 +88,14 @@ protected void verifyConditions(ScesimModelDescriptor scesimModelDescriptor, ScenarioRunnerData scenarioRunnerData, ExpressionEvaluatorFactory expressionEvaluatorFactory, Map requestContext) { - DMNResult dmnResult = (DMNResult) requestContext.get(DMNScenarioExecutableBuilder.DMN_RESULT); + PMML4Result pmml4Result = (PMML4Result) requestContext.get(PMMLScenarioExecutableBuilder.PMML_RESULT); for (ScenarioExpect output : scenarioRunnerData.getExpects()) { FactIdentifier factIdentifier = output.getFactIdentifier(); - String decisionName = factIdentifier.getName(); - DMNDecisionResult decisionResult = dmnResult.getDecisionResultByName(decisionName); - if (decisionResult == null) { - throw new ScenarioException("DMN execution has not generated a decision result with name " + decisionName); + String requestedField = factIdentifier.getName(); + if (!pmml4Result.getResultVariables().containsKey(requestedField)) { + throw new ScenarioException("PMML execution has not generated a result with name " + requestedField); } - for (FactMappingValue expectedResult : output.getExpectedResult()) { ExpressionIdentifier expressionIdentifier = expectedResult.getExpressionIdentifier(); @@ -126,7 +105,7 @@ protected void verifyConditions(ScesimModelDescriptor scesimModelDescriptor, ExpressionEvaluator expressionEvaluator = expressionEvaluatorFactory.getOrCreate(expectedResult); ScenarioResult scenarioResult = fillResult(expectedResult, - () -> getSingleFactValueResult(factMapping, expectedResult, decisionResult, expressionEvaluator), + () -> getSingleFactValueResult(factMapping, expectedResult, pmml4Result.getResultVariables().get(requestedField), expressionEvaluator), expressionEvaluator); scenarioRunnerData.addResult(scenarioResult); @@ -137,32 +116,14 @@ protected void verifyConditions(ScesimModelDescriptor scesimModelDescriptor, @SuppressWarnings("unchecked") protected ValueWrapper getSingleFactValueResult(FactMapping factMapping, FactMappingValue expectedResult, - DMNDecisionResult decisionResult, + Object resultRaw, ExpressionEvaluator expressionEvaluator) { - Object resultRaw = decisionResult.getResult(); - final DMNDecisionResult.DecisionEvaluationStatus evaluationStatus = decisionResult.getEvaluationStatus(); - if (!SUCCEEDED.equals(evaluationStatus)) { - return errorWithMessage("The decision " + - decisionResult.getDecisionName() + - " has not been successfully evaluated: " + - evaluationStatus); - } - - List elementsWithoutClass = factMapping.getExpressionElementsWithoutClass(); - - // DMN engine doesn't generate the whole object when no entry of the decision table match - if (resultRaw != null) { - for (ExpressionElement expressionElement : elementsWithoutClass) { - if (!(resultRaw instanceof Map)) { - throw new ScenarioException("Wrong resultRaw structure because it is not a complex type as expected"); - } - Map result = (Map) resultRaw; - resultRaw = result.get(expressionElement.getStep()); - } + if (resultRaw == null) { + return errorWithMessage("The prediction " + + factMapping.getFactAlias() + + " has not been successfully evaluated."); } - - Class resultClass = resultRaw != null ? resultRaw.getClass() : null; - + Class resultClass = resultRaw.getClass(); Object expectedResultRaw = expectedResult.getRawValue(); return getResultWrapper(factMapping.getClassName(), expectedResult, @@ -176,28 +137,11 @@ protected ValueWrapper getSingleFactValueResult(FactMapping factMapping, @Override protected Object createObject(ValueWrapper initialInstance, String className, Map, Object> params, ClassLoader classLoader) { // simple types - if (initialInstance.isValid() && !(initialInstance.getValue() instanceof Map)) { + if (initialInstance.isValid() && !(initialInstance.getValue() instanceof Map && !(initialInstance.getValue() instanceof Collection))) { return initialInstance.getValue(); + } else { + throw new ScenarioException("Only simple types allowed for PMML"); } - Map toReturn = (Map) initialInstance.orElseGet(HashMap::new); - for (Map.Entry, Object> listObjectEntry : params.entrySet()) { - - // direct mapping already considered - if (listObjectEntry.getKey().isEmpty()) { - continue; - } - - List allSteps = listObjectEntry.getKey(); - List steps = allSteps.subList(0, allSteps.size() - 1); - String lastStep = allSteps.get(allSteps.size() - 1); - - Map targetMap = toReturn; - for (String step : steps) { - targetMap = (Map) targetMap.computeIfAbsent(step, k -> new HashMap<>()); - } - targetMap.put(lastStep, listObjectEntry.getValue()); - } - return toReturn; } protected PMMLScenarioExecutableBuilder createBuilderWrapper(KieContainer kieContainer, String pmmlFilePath, String pmmlModelName) { diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java new file mode 100644 index 00000000000..d97b6990ec4 --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java @@ -0,0 +1,334 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.drools.scenariosimulation.backend.runner; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.drools.scenariosimulation.api.model.ExpressionIdentifier; +import org.drools.scenariosimulation.api.model.FactIdentifier; +import org.drools.scenariosimulation.api.model.FactMapping; +import org.drools.scenariosimulation.api.model.FactMappingType; +import org.drools.scenariosimulation.api.model.FactMappingValue; +import org.drools.scenariosimulation.api.model.FactMappingValueStatus; +import org.drools.scenariosimulation.api.model.Scenario; +import org.drools.scenariosimulation.api.model.ScenarioSimulationModel; +import org.drools.scenariosimulation.api.model.Settings; +import org.drools.scenariosimulation.api.model.Simulation; +import org.drools.scenariosimulation.backend.expression.BaseExpressionEvaluator; +import org.drools.scenariosimulation.backend.expression.ExpressionEvaluator; +import org.drools.scenariosimulation.backend.expression.ExpressionEvaluatorFactory; +import org.drools.scenariosimulation.backend.fluent.PMMLScenarioExecutableBuilder; +import org.drools.scenariosimulation.backend.runner.model.InstanceGiven; +import org.drools.scenariosimulation.backend.runner.model.ScenarioExpect; +import org.drools.scenariosimulation.backend.runner.model.ScenarioRunnerData; +import org.drools.scenariosimulation.backend.runner.model.ValueWrapper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.kie.api.pmml.PMML4Result; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.RequestContext; +import org.kie.pmml.api.enums.ResultCode; +import org.kie.pmml.api.models.PMMLModel; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class PMMLScenarioRunnerHelperTest { + + private static final String NAME = "NAME"; + private static final String EXPRESSION_NAME = "NAME"; + private static final BigDecimal AMOUNT = BigDecimal.valueOf(10); + private static final String PMML_FILE_PATH = "pmmlFilePath"; + private static final String TEST_DESCRIPTION = "Test description"; + private static final ClassLoader classLoader = RuleScenarioRunnerHelperTest.class.getClassLoader(); + private static final ExpressionEvaluatorFactory expressionEvaluatorFactory = ExpressionEvaluatorFactory.create(classLoader, ScenarioSimulationModel.Type.PMML); + private static final ExpressionEvaluator expressionEvaluator = new BaseExpressionEvaluator(classLoader); + + private final PMMLScenarioRunnerHelper runnerHelper = new PMMLScenarioRunnerHelper() { + @Override + protected PMMLScenarioExecutableBuilder createBuilderWrapper(KieContainer kieContainer, String pmmlFilePath, String pmmlModelName) { + return pmmlScenarioExecutableBuilderMock; + } + }; + @Mock + protected Map resultVariablesMock; + @Mock + protected Map requestContextMock; + @Mock + protected PMML4Result pmml4ResultMock; + @Mock + protected PMMLModel pmmlModelMock; + @Mock + protected PMMLScenarioExecutableBuilder pmmlScenarioExecutableBuilderMock; + @Mock + protected KieContainer kieContainerMock; + private Simulation simulation; + private Settings settings; + private FactIdentifier fact1FactIdentifier; + private ExpressionIdentifier fact1GivenExpressionIdentifier; + private FactMapping fact1GivenFactMapping; + private Scenario scenario1; + private Scenario scenario2; + private ExpressionIdentifier fact1ExpectedExpressionIdentifier; + private FactMapping fact1ExpectedFactMapping; + private FactIdentifier fact2FactIdentifier; + private ExpressionIdentifier fact2GivenExpressionIdentifier; + private FactMapping fact2GivenFactMapping; + private ExpressionIdentifier fact2ExpectedExpressionIdentifier; + private FactMapping fact2ExpectedFactMapping; + private FactMappingValue fact2ExpectedFactMappingValue; + private FactMappingValue fact1ExpectedValue; + + @Before + public void init() { + when(pmmlScenarioExecutableBuilderMock.run()).thenReturn(mock(RequestContext.class)); + + simulation = new Simulation(); + settings = new Settings(); + settings.setType(ScenarioSimulationModel.Type.PMML); + settings.setPmmlFilePath(PMML_FILE_PATH); + fact1FactIdentifier = FactIdentifier.create("Fact 1", String.class.getCanonicalName()); + fact1GivenExpressionIdentifier = ExpressionIdentifier.create("Fact 1 Given", FactMappingType.GIVEN); + fact1GivenFactMapping = simulation.getScesimModelDescriptor().addFactMapping(fact1FactIdentifier, fact1GivenExpressionIdentifier); + fact1GivenFactMapping.addExpressionElement("Fact 1", String.class.getCanonicalName()); + + fact2FactIdentifier = FactIdentifier.create("Fact 2", Double.class.getCanonicalName()); + fact2GivenExpressionIdentifier = ExpressionIdentifier.create("Fact 2 Given", FactMappingType.GIVEN); + fact2GivenFactMapping = simulation.getScesimModelDescriptor().addFactMapping(fact2FactIdentifier, fact2GivenExpressionIdentifier); + fact2GivenFactMapping.addExpressionElement("Fact 2", BigDecimal.class.getCanonicalName()); + + fact1ExpectedExpressionIdentifier = ExpressionIdentifier.create("Fact 1 Expected", FactMappingType.EXPECT); + fact1ExpectedFactMapping = simulation.getScesimModelDescriptor().addFactMapping(fact1FactIdentifier, fact1ExpectedExpressionIdentifier); + fact1ExpectedFactMapping.addExpressionElement("Fact 1", String.class.getCanonicalName()); + + fact2ExpectedExpressionIdentifier = ExpressionIdentifier.create("Fact 2 Expected", FactMappingType.EXPECT); + fact2ExpectedFactMapping = simulation.getScesimModelDescriptor().addFactMapping(fact2FactIdentifier, fact2ExpectedExpressionIdentifier); + fact2ExpectedFactMapping.addExpressionElement("Fact 2", Double.class.getCanonicalName()); + + scenario1 = simulation.addData(); + scenario1.setDescription(TEST_DESCRIPTION); + scenario1.addMappingValue(fact1FactIdentifier, fact1GivenExpressionIdentifier, EXPRESSION_NAME); + fact1ExpectedValue = scenario1.addMappingValue(fact1FactIdentifier, fact1ExpectedExpressionIdentifier, EXPRESSION_NAME); + + scenario2 = simulation.addData(); + scenario2.setDescription(TEST_DESCRIPTION); + scenario2.addMappingValue(fact1FactIdentifier, fact1GivenExpressionIdentifier, EXPRESSION_NAME); + scenario2.addMappingValue(fact1FactIdentifier, fact1ExpectedExpressionIdentifier, EXPRESSION_NAME); + scenario2.addMappingValue(fact2FactIdentifier, fact2GivenExpressionIdentifier, AMOUNT); + fact2ExpectedFactMappingValue = scenario2.addMappingValue(fact2FactIdentifier, fact2ExpectedExpressionIdentifier, AMOUNT); + + when(pmml4ResultMock.getResultVariables()).thenReturn(resultVariablesMock); + + when(requestContextMock.get(PMMLScenarioExecutableBuilder.PMML_RESULT)).thenReturn(pmml4ResultMock); + when(requestContextMock.get(PMMLScenarioExecutableBuilder.PMML_MODEL)).thenReturn(pmmlModelMock); + } + + @Test + public void verifyConditions() { + ScenarioRunnerData scenarioRunnerData1 = new ScenarioRunnerData(); + scenarioRunnerData1.addExpect(new ScenarioExpect(fact1FactIdentifier, singletonList(fact1ExpectedValue))); + + // test 1 - no result generated for specific requestedField + assertThatThrownBy(() -> runnerHelper.verifyConditions(simulation.getScesimModelDescriptor(), scenarioRunnerData1, expressionEvaluatorFactory, requestContextMock)) + .isInstanceOf(ScenarioException.class) + .hasMessage("PMML execution has not generated a result with name Fact 1"); + + // test 2 - when requestedField contains a null value skip the steps and just do the comparison (that should be false in this case) + when(resultVariablesMock.containsKey(anyString())).thenReturn(true); + when(resultVariablesMock.get(anyString())).thenReturn(null); + when(pmml4ResultMock.getResultCode()).thenReturn(ResultCode.OK.getName()); + + runnerHelper.verifyConditions(simulation.getScesimModelDescriptor(), scenarioRunnerData1, expressionEvaluatorFactory, requestContextMock); + assertEquals(1, scenarioRunnerData1.getResults().size()); + assertFalse(scenarioRunnerData1.getResults().get(0).getResult()); + + // test 3 - check are performed (but fail) + Map resultMap = new HashMap<>(); + resultMap.put("Fact 1", "WrongValue"); + when(pmml4ResultMock.getResultVariables()).thenReturn(resultMap); + ScenarioRunnerData scenarioRunnerData2 = new ScenarioRunnerData(); + scenarioRunnerData2.addExpect(new ScenarioExpect(fact1FactIdentifier, singletonList(fact1ExpectedValue))); + runnerHelper.verifyConditions(simulation.getScesimModelDescriptor(), scenarioRunnerData2, expressionEvaluatorFactory, requestContextMock); + assertEquals(1, scenarioRunnerData2.getResults().size()); + assertFalse(scenarioRunnerData2.getResults().get(0).getResult()); + + // test 4 - check are performed (but success) + ScenarioRunnerData scenarioRunnerData3 = new ScenarioRunnerData(); + scenarioRunnerData3.addExpect(new ScenarioExpect(fact1FactIdentifier, singletonList(fact1ExpectedValue))); + resultMap.put("Fact 1", NAME); + runnerHelper.verifyConditions(simulation.getScesimModelDescriptor(), scenarioRunnerData3, expressionEvaluatorFactory, requestContextMock); + assertEquals(1, scenarioRunnerData3.getResults().size()); + assertTrue(scenarioRunnerData3.getResults().get(0).getResult()); + + // test 5 - verify that when expression evaluation fails the corresponding expression is marked as error + ExpressionEvaluatorFactory expressionEvaluatorFactoryMock = mock(ExpressionEvaluatorFactory.class); + when(expressionEvaluatorFactoryMock.getOrCreate(any())).thenReturn(mock(ExpressionEvaluator.class)); + runnerHelper.verifyConditions(simulation.getScesimModelDescriptor(), + scenarioRunnerData3, + expressionEvaluatorFactoryMock, + requestContextMock); + assertEquals(FactMappingValueStatus.FAILED_WITH_ERROR, scenarioRunnerData3.getResults().get(0).getFactMappingValue().getStatus()); + } + + @SuppressWarnings("unchecked") + @Test + public void createObjectInvalid() { + // test 1 singleton lists + Map, Object> params = new HashMap<>(); + params.put(singletonList("name"), "TestName"); + params.put(singletonList("age"), BigDecimal.valueOf(10)); + + ValueWrapper initialInstance1 = runnerHelper.getDirectMapping(params); + assertThatThrownBy(() -> runnerHelper.createObject( + initialInstance1, + String.class.getCanonicalName(), + params, + this.getClass().getClassLoader())) + .isInstanceOf(ScenarioException.class) + .hasMessage("Only simple types allowed for PMML"); + + // test 2 lists + params.clear(); + params.put(asList("creation", "name"), "TestName"); + params.put(singletonList("age"), BigDecimal.valueOf(10)); + + ValueWrapper initialInstance2 = runnerHelper.getDirectMapping(params); + assertThatThrownBy(() -> runnerHelper.createObject( + initialInstance2, + String.class.getCanonicalName(), + params, + this.getClass().getClassLoader())) + .isInstanceOf(ScenarioException.class) + .hasMessage("Only simple types allowed for PMML"); + + // test 3 complex types + params.clear(); + Map directMappingComplexTypeValue = new HashMap<>(); + directMappingComplexTypeValue.put("key1", "value1"); + params.put(emptyList(), directMappingComplexTypeValue); + params.put(singletonList("key2"), "value2"); + + ValueWrapper initialInstance3 = runnerHelper.getDirectMapping(params); + assertThatThrownBy(() -> runnerHelper.createObject( + initialInstance3, + Map.class.getCanonicalName(), + params, + this.getClass().getClassLoader())) + .isInstanceOf(ScenarioException.class) + .hasMessage("Only simple types allowed for PMML"); + } + + @Test + public void createObjectDirectMappingSimpleType() { + Map, Object> params = new HashMap<>(); + String directMappingSimpleTypeValue = "TestName"; + params.put(emptyList(), directMappingSimpleTypeValue); + + ValueWrapper initialInstance = runnerHelper.getDirectMapping(params); + Object objectRaw = runnerHelper.createObject( + initialInstance, + String.class.getCanonicalName(), + params, + this.getClass().getClassLoader()); + + assertTrue(objectRaw instanceof String); + + assertEquals(directMappingSimpleTypeValue, objectRaw); + } + + @Test + public void createObjectDirectMappingSimpleTypeNull() { + Map, Object> params = new HashMap<>(); + params.put(emptyList(), null); + + ValueWrapper initialInstance = runnerHelper.getDirectMapping(params); + Object objectRaw = runnerHelper.createObject( + initialInstance, + String.class.getCanonicalName(), + params, + this.getClass().getClassLoader()); + + assertNull(objectRaw); + } + + @Test + public void getSingleFactValueResultNullPrediction() { + String factName = "Fact 1"; + FactMapping factMappingMock = mock(FactMapping.class); + when(factMappingMock.getFactAlias()).thenReturn(factName); + ValueWrapper failedResult = runnerHelper.getSingleFactValueResult(factMappingMock, + null, + null, + expressionEvaluator); + assertFalse(failedResult.isValid()); + assertEquals("The prediction " + + factName + + " has not been successfully evaluated.", + failedResult.getErrorMessage().get()); + } + + @Test + public void executeScenario() { + ArgumentCaptor setValueCaptor = ArgumentCaptor.forClass(Object.class); + + ScenarioRunnerData scenarioRunnerData = new ScenarioRunnerData(); + scenarioRunnerData.addBackground(new InstanceGiven(fact1FactIdentifier, "")); + scenarioRunnerData.addBackground(new InstanceGiven(fact2FactIdentifier, new BigDecimal(10))); + scenarioRunnerData.addGiven(new InstanceGiven(fact1FactIdentifier, NAME)); + FactMappingValue factMappingValue = new FactMappingValue(fact1FactIdentifier, fact1ExpectedExpressionIdentifier, NAME); + scenarioRunnerData.addExpect(new ScenarioExpect(fact1FactIdentifier, singletonList(factMappingValue), false)); + scenarioRunnerData.addExpect(new ScenarioExpect(fact1FactIdentifier, singletonList(factMappingValue), true)); + + int inputObjects = scenarioRunnerData.getBackgrounds().size() + scenarioRunnerData.getGivens().size(); + + runnerHelper.executeScenario(kieContainerMock, scenarioRunnerData, expressionEvaluatorFactory, simulation.getScesimModelDescriptor(), settings); + + verify(pmmlScenarioExecutableBuilderMock, times(inputObjects)).setValue(anyString(), setValueCaptor.capture()); + for (Object value : setValueCaptor.getAllValues()) { + assertTrue(value instanceof String || value instanceof BigDecimal); + } + + verify(pmmlScenarioExecutableBuilderMock, times(1)).run(); + + // test not pmml error + settings.setType(ScenarioSimulationModel.Type.RULE); + assertThatThrownBy(() -> runnerHelper.executeScenario(kieContainerMock, scenarioRunnerData, expressionEvaluatorFactory, simulation.getScesimModelDescriptor(), settings)) + .isInstanceOf(ScenarioException.class) + .hasMessageStartingWith("Impossible to run"); + } + +} \ No newline at end of file diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-dmn.scesim b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-dmn.scesim index b413ce4d2b4..408d793ef33 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-dmn.scesim +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-dmn.scesim @@ -1,4 +1,4 @@ - + diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-rule.scesim b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-rule.scesim index 93ab3adddb0..2c4e8d474fe 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-rule.scesim +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-rule.scesim @@ -1,4 +1,4 @@ - + From e9d1507b62a456a60f8b3c220023eec37626b24b Mon Sep 17 00:00:00 2001 From: Gabriele Cardosi Date: Tue, 26 Jan 2021 12:37:29 +0100 Subject: [PATCH 3/6] [DROOLS-5964] Implemented pmml-scesim backend integration. Implemented full roundtrip test --- .../pom.xml | 8 +- .../runner/AbstractScenarioRunner.java | 15 +- .../backend/runner/PMMLScenarioRunner.java | 37 ++++ .../backend/PMMLRunnerTest.java | 91 ++++++++++ .../ScenarioSimulationXMLPersistenceTest.java | 8 + .../CategoricalVariablesRegression.pmml | 38 +++++ .../resources/folderToTest/scesim-pmml.scesim | 159 ++++++++++++++++++ 7 files changed, 346 insertions(+), 10 deletions(-) create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunner.java create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/PMMLRunnerTest.java create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/CategoricalVariablesRegression.pmml create mode 100644 drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-pmml.scesim diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml b/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml index 908f715fe1a..bea1530e1a9 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/pom.xml @@ -81,19 +81,19 @@ org.kie kie-pmml-dependencies + + org.kie - kie-pmml-evaluator-core + kie-test-util + test - - org.assertj assertj-core test - diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java index 0b16c61a7e1..1dd74c45c6c 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java @@ -68,12 +68,15 @@ public static Description getDescriptionForScenario(Optional className, } public static ScenarioRunnerProvider getSpecificRunnerProvider(Type type) { - if (Type.RULE.equals(type)) { - return RuleScenarioRunner::new; - } else if (Type.DMN.equals(type)) { - return DMNScenarioRunner::new; - } else { - throw new IllegalArgumentException("Impossible to run simulation of type " + type); + switch (type) { + case RULE: + return RuleScenarioRunner::new; + case DMN: + return DMNScenarioRunner::new; + case PMML: + return PMMLScenarioRunner::new; + default: + throw new IllegalArgumentException("Impossible to run simulation of type " + type); } } diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunner.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunner.java new file mode 100644 index 00000000000..a2a261e2b24 --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunner.java @@ -0,0 +1,37 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.drools.scenariosimulation.backend.runner; + +import org.drools.scenariosimulation.backend.expression.ExpressionEvaluatorFactory; +import org.drools.scenariosimulation.backend.runner.model.ScenarioRunnerDTO; +import org.kie.api.runtime.KieContainer; + +public class PMMLScenarioRunner extends AbstractScenarioRunner { + + public PMMLScenarioRunner(KieContainer kieContainer, ScenarioRunnerDTO scenarioRunnerDTO) { + super(kieContainer, + scenarioRunnerDTO, + ExpressionEvaluatorFactory.create( + kieContainer.getClassLoader(), + scenarioRunnerDTO.getSettings().getType())); + } + + @Override + protected AbstractRunnerHelper newRunnerHelper() { + return new PMMLScenarioRunnerHelper(); + } +} diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/PMMLRunnerTest.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/PMMLRunnerTest.java new file mode 100644 index 00000000000..9eb1e3adba2 --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/PMMLRunnerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.drools.scenariosimulation.backend; + +import java.io.File; +import java.util.Scanner; + +import org.drools.core.impl.KnowledgeBaseImpl; +import org.drools.scenariosimulation.api.model.ScenarioSimulationModel; +import org.drools.scenariosimulation.backend.runner.PMMLScenarioRunner; +import org.drools.scenariosimulation.backend.runner.model.ScenarioRunnerDTO; +import org.drools.scenariosimulation.backend.util.ScenarioSimulationXMLPersistence; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; +import org.kie.api.runtime.KieContainer; +import org.kie.pmml.evaluator.assembler.factories.PMMLRuntimeFactoryImpl; +import org.kie.pmml.evaluator.core.service.PMMLRuntimeInternalImpl; +import org.kie.test.util.filesystem.FileUtils; + +import static org.junit.Assert.fail; + +public class PMMLRunnerTest { + + private final String PMML_FILE = "CategoricalVariablesRegression.pmml"; + private final String SCESIM_FILE = "scesim-pmml.scesim"; + + @Test + public void runSuccessScenario() throws Exception { + KieContainer kieContainer = getKieContainer(); + ScenarioRunnerDTO scenarioRunnerDTO = getScenarioRunnerDTO(); + PMMLScenarioRunner runner = new PMMLScenarioRunner(kieContainer, scenarioRunnerDTO); + runner.run(getRunNotifier()); + } + + private RunNotifier getRunNotifier() { + RunNotifier toReturn = new RunNotifier(); + toReturn.addListener(new RunListenerForTest()); + return toReturn; + } + + + private KieContainer getKieContainer() { + File pmmlFile = FileUtils.getFile(PMML_FILE); + PMMLRuntimeInternalImpl pmmlRuntime = (PMMLRuntimeInternalImpl) new PMMLRuntimeFactoryImpl().getPMMLRuntimeFromFile(pmmlFile); + KnowledgeBaseImpl kieBase = (KnowledgeBaseImpl) pmmlRuntime.getKnowledgeBase(); + return kieBase.getKieContainer(); + } + + private ScenarioRunnerDTO getScenarioRunnerDTO() throws Exception { + File scesimFile = FileUtils.getFile(SCESIM_FILE); + final Scanner scanner = new Scanner(scesimFile); + String rawFile = scanner.useDelimiter("\\Z").next(); + ScenarioSimulationModel scenarioSimulationModel = getXmlReader().unmarshal(rawFile); + return new ScenarioRunnerDTO(scenarioSimulationModel, scesimFile.getPath()); + } + + private ScenarioSimulationXMLPersistence getXmlReader() { + return ScenarioSimulationXMLPersistence.getInstance(); + } + + + private class RunListenerForTest extends RunListener { + + @Override + public void testFinished(Description description) throws Exception { + super.testFinished(description); + } + + @Override + public void testFailure(Failure failure) throws Exception { + super.testFailure(failure); + fail(failure.getMessage()); + } + } +} diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java index 07a8f6d3973..ccf246fd731 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java @@ -349,6 +349,14 @@ public void unmarshalDMN() throws Exception { commonCheckSimulation(retrieved); } + @Test + public void unmarshalPMML() throws Exception { + String toUnmarshal = getFileContent("scesim-pmml.scesim"); + final ScenarioSimulationModel retrieved = ScenarioSimulationXMLPersistence.getInstance().unmarshal(toUnmarshal); + assertEquals(ScenarioSimulationModel.Type.PMML, retrieved.getSettings().getType()); + commonCheckSimulation(retrieved); + } + /** * Verify the given Map has one single entry, whose List value also has a single children. * If expectedTextContent is given, it also check the children text content match diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/CategoricalVariablesRegression.pmml b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/CategoricalVariablesRegression.pmml new file mode 100644 index 00000000000..4c4df7e8b41 --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/CategoricalVariablesRegression.pmml @@ -0,0 +1,38 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-pmml.scesim b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-pmml.scesim new file mode 100644 index 00000000000..6e8b2dca91d --- /dev/null +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/resources/folderToTest/scesim-pmml.scesim @@ -0,0 +1,159 @@ + + + + + + + + Index + OTHER + + + # + java.lang.Integer + + java.lang.Integer + # + + + + + Description + OTHER + + + Scenario description + java.lang.String + + java.lang.String + Scenario description + + + + + value + + + + 0|1 + GIVEN + + + x + java.lang.String + + java.lang.String + x + + + + + value + + + + 0|2 + GIVEN + + + y + java.lang.String + + java.lang.String + y + + + + + value + + + + 0|3 + EXPECT + + + result + java.lang.String + + java.lang.String + result + + + + + + + + + # + java.lang.Integer + + + Index + OTHER + + 1 + + + + Description + OTHER + + + Scenario description + java.lang.String + + Description + + + + 0|1 + GIVEN + + + x + java.lang.String + + red + + + + 0|2 + GIVEN + + + y + java.lang.String + + classA + + + + 0|3 + EXPECT + + + result + java.lang.String + + -16.6 + + + + + + + + src/test/resources/folderToTest/CategoricalVariablesRegression.pmml + CategoricalVariablesRegression + PMML + + + + + + + + + From 507f71ff04ecc82abf5d83d1cd4df8eaaf9cbead Mon Sep 17 00:00:00 2001 From: Gabriele Cardosi Date: Tue, 26 Jan 2021 14:54:09 +0100 Subject: [PATCH 4/6] [DROOLS-5964] Fixed AbstractScenarioRunner.getSpecificRunnerProvider for test failure --- .../backend/runner/AbstractScenarioRunner.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java index 1dd74c45c6c..9f02667fea8 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/runner/AbstractScenarioRunner.java @@ -68,6 +68,9 @@ public static Description getDescriptionForScenario(Optional className, } public static ScenarioRunnerProvider getSpecificRunnerProvider(Type type) { + if (type == null) { + throw new IllegalArgumentException("Impossible to run simulation of 'null' type"); + } switch (type) { case RULE: return RuleScenarioRunner::new; From f1b5ddacfe11f8d2ffd6ef8a4bc873ac3ae65de7 Mon Sep 17 00:00:00 2001 From: Gabriele Cardosi Date: Tue, 24 Aug 2021 17:46:50 +0200 Subject: [PATCH 5/6] [DROOLS-5964] Working status. TODO: add tests, cleanup --- .../util/ScenarioSimulationXMLPersistenceTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java index ccf246fd731..dd9a33a5764 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/util/ScenarioSimulationXMLPersistenceTest.java @@ -20,6 +20,7 @@ import org.assertj.core.api.Assertions; import org.drools.scenariosimulation.api.model.ScenarioSimulationModel; +import org.drools.scenariosimulation.api.model.Settings; import org.junit.Test; import org.kie.soup.project.datamodel.imports.Import; import org.w3c.dom.Document; @@ -353,7 +354,12 @@ public void unmarshalDMN() throws Exception { public void unmarshalPMML() throws Exception { String toUnmarshal = getFileContent("scesim-pmml.scesim"); final ScenarioSimulationModel retrieved = ScenarioSimulationXMLPersistence.getInstance().unmarshal(toUnmarshal); - assertEquals(ScenarioSimulationModel.Type.PMML, retrieved.getSettings().getType()); + Settings retrievedSettings = retrieved.getSettings(); + assertEquals(ScenarioSimulationModel.Type.PMML, retrievedSettings.getType()); + String expected = "src/test/resources/folderToTest/CategoricalVariablesRegression.pmml"; + assertEquals(expected, retrievedSettings.getPmmlFilePath()); + expected = "CategoricalVariablesRegression"; + assertEquals(expected, retrievedSettings.getPmmlModelName()); commonCheckSimulation(retrieved); } From ef1ece5cab0168cddf5ec70ae6f7bbfed221eb7a Mon Sep 17 00:00:00 2001 From: Gabriele Cardosi Date: Wed, 25 Aug 2021 14:37:52 +0200 Subject: [PATCH 6/6] [DROOLS-5964] Working status. --- .../backend/fluent/PMMLScenarioExecutableBuilder.java | 11 ++++++++--- .../backend/util/InMemoryMigrationStrategy.java | 4 +--- .../backend/runner/PMMLScenarioRunnerHelperTest.java | 3 --- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java index 78c2a7a882f..c96a9a7a9fe 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/fluent/PMMLScenarioExecutableBuilder.java @@ -16,16 +16,17 @@ package org.drools.scenariosimulation.backend.fluent; +import java.nio.file.FileSystems; import java.util.Objects; import org.drools.core.command.RequestContextImpl; +import org.drools.scenariosimulation.backend.exceptions.ImpossibleToFindPMMLException; import org.kie.api.KieBase; import org.kie.api.pmml.PMML4Result; import org.kie.api.pmml.PMMLRequestData; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieRuntimeFactory; import org.kie.api.runtime.RequestContext; -import org.kie.pmml.api.exceptions.KiePMMLException; import org.kie.pmml.api.models.PMMLModel; import org.kie.pmml.api.runtime.PMMLRuntime; import org.kie.pmml.evaluator.core.PMMLContextImpl; @@ -37,6 +38,8 @@ public class PMMLScenarioExecutableBuilder { public static final String PMML_RESULT = "pmmlResult"; public static final String PMML_MODEL = "pmmlModel"; + private static final String SEPARATOR = FileSystems.getDefault().getSeparator(); + private final KieContainer kieContainer; private final PMMLRequestDataBuilder pmmlRequestDataBuilder; private final String pmmlFilePath; @@ -44,7 +47,9 @@ public class PMMLScenarioExecutableBuilder { private PMMLScenarioExecutableBuilder(KieContainer kieContainer, String pmmlFilePath, String pmmlModelName) { this.kieContainer = kieContainer; this.pmmlFilePath = pmmlFilePath; - String fileName = pmmlFilePath.contains("/") ? pmmlFilePath.substring(pmmlFilePath.lastIndexOf("/") +1) : pmmlFilePath; + + + String fileName = pmmlFilePath.contains(SEPARATOR) ? pmmlFilePath.substring(pmmlFilePath.lastIndexOf(SEPARATOR) +1) : pmmlFilePath; pmmlRequestDataBuilder = new PMMLRequestDataBuilder("correlationid", pmmlModelName, fileName); } @@ -65,7 +70,7 @@ public RequestContext run() { final KieRuntimeFactory kieRuntimeFactory = KieRuntimeFactory.of(kieBase); final PMMLRuntime pmmlRuntime = kieRuntimeFactory.get(PMMLRuntime.class); final PMMLModel pmmlModel = pmmlRuntime.getPMMLModel(pmmlRequestData.getModelName()) - .orElseThrow(() -> new KiePMMLException("Failed to retrieve model with name " + pmmlRequestData.getModelName())); + .orElseThrow(() -> new ImpossibleToFindPMMLException("Failed to retrieve model with name " + pmmlRequestData.getModelName())); final PMML4Result pmml4Result = pmmlRuntime.evaluate(pmmlRequestData.getModelName(), new PMMLContextImpl(pmmlRequestData)); final RequestContext toReturn = new RequestContextImpl(); toReturn.setOutput(PMML_RESULT, pmml4Result); diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java index a74bbd6dc66..decff2ae504 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/main/java/org/drools/scenariosimulation/backend/util/InMemoryMigrationStrategy.java @@ -237,9 +237,7 @@ public ThrowingConsumer from1_7to1_8() { @Override public ThrowingConsumer from1_8to1_9() { - return document -> { - updateVersion(document, "1.9"); - }; + return document -> updateVersion(document, "1.9"); } private void replaceReference(List simulationFactMappingNodeList, Node containerNode, String referredNodeName) { diff --git a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java index d97b6990ec4..f32ba054e52 100644 --- a/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java +++ b/drools-scenario-simulation/drools-scenario-simulation-backend/src/test/java/org/drools/scenariosimulation/backend/runner/PMMLScenarioRunnerHelperTest.java @@ -45,7 +45,6 @@ import org.kie.api.pmml.PMML4Result; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.RequestContext; -import org.kie.pmml.api.enums.ResultCode; import org.kie.pmml.api.models.PMMLModel; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -154,7 +153,6 @@ public void init() { when(pmml4ResultMock.getResultVariables()).thenReturn(resultVariablesMock); when(requestContextMock.get(PMMLScenarioExecutableBuilder.PMML_RESULT)).thenReturn(pmml4ResultMock); - when(requestContextMock.get(PMMLScenarioExecutableBuilder.PMML_MODEL)).thenReturn(pmmlModelMock); } @Test @@ -170,7 +168,6 @@ public void verifyConditions() { // test 2 - when requestedField contains a null value skip the steps and just do the comparison (that should be false in this case) when(resultVariablesMock.containsKey(anyString())).thenReturn(true); when(resultVariablesMock.get(anyString())).thenReturn(null); - when(pmml4ResultMock.getResultCode()).thenReturn(ResultCode.OK.getName()); runnerHelper.verifyConditions(simulation.getScesimModelDescriptor(), scenarioRunnerData1, expressionEvaluatorFactory, requestContextMock); assertEquals(1, scenarioRunnerData1.getResults().size());