From c45f56e8a765fa1bba8513ec9ba08676d036b393 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Fri, 14 Jul 2023 14:53:49 +0900 Subject: [PATCH] [DROOLS-7416] Rebuild RuleUnit option --- .../ruleunits/api/RuleUnitProvider.java | 43 ++++++++- .../ruleunits/dsl/DynamicHelloWorldUnit.java | 58 ++++++++++++ .../ruleunits/dsl/RuleUnitRebuildTest.java | 92 +++++++++++++++++++ .../ruleunits/impl/RuleUnitProviderImpl.java | 12 ++- 4 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/DynamicHelloWorldUnit.java create mode 100644 drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/RuleUnitRebuildTest.java diff --git a/drools-ruleunits/drools-ruleunits-api/src/main/java/org/drools/ruleunits/api/RuleUnitProvider.java b/drools-ruleunits/drools-ruleunits-api/src/main/java/org/drools/ruleunits/api/RuleUnitProvider.java index 851ef906d0e7..adeccfacf556 100644 --- a/drools-ruleunits/drools-ruleunits-api/src/main/java/org/drools/ruleunits/api/RuleUnitProvider.java +++ b/drools-ruleunits/drools-ruleunits-api/src/main/java/org/drools/ruleunits/api/RuleUnitProvider.java @@ -27,7 +27,16 @@ public interface RuleUnitProvider extends KieService { * Provides the {@link RuleUnit} generated for the given {@link RuleUnitData}. * @return The generated {@link RuleUnit} or null if there's no {@link RuleUnit} generated for the given {@link RuleUnitData}. */ - RuleUnit getRuleUnit(T ruleUnitData); + default RuleUnit getRuleUnit(T ruleUnitData) { + return getRuleUnit(ruleUnitData, false); + } + + /** + * Provides the {@link RuleUnit} generated for the given {@link RuleUnitData}. + * @param rebuild if true, the {@link RuleUnit} is regenerated instead of using the cached one. + * @return The generated {@link RuleUnit} or null if there's no {@link RuleUnit} generated for the given {@link RuleUnitData}. + */ + RuleUnit getRuleUnit(T ruleUnitData, boolean rebuild); /** * Creates a new {@link RuleUnitInstance} from the {@link RuleUnit} generated for the given {@link RuleUnitData}. @@ -38,7 +47,21 @@ public interface RuleUnitProvider extends KieService { * throwing a runtime exception if there isn't any {@link RuleUnit} generated for the given {@link RuleUnitData}. */ default RuleUnitInstance createRuleUnitInstance(T ruleUnitData) { - RuleUnit ruleUnit = getRuleUnit(ruleUnitData); + return createRuleUnitInstance(ruleUnitData, false); + } + + /** + * Creates a new {@link RuleUnitInstance} from the {@link RuleUnit} generated for the given {@link RuleUnitData}. + * This is equivalent to + *
+     * RuleUnitProvider.get().getRuleUnit(ruleUnitData, rebuild).createInstance(ruleUnitData);
+     * 
+ * throwing a runtime exception if there isn't any {@link RuleUnit} generated for the given {@link RuleUnitData}. + * + * @param rebuild if true, the {@link RuleUnit} is regenerated before creating the {@link RuleUnitInstance}. + */ + default RuleUnitInstance createRuleUnitInstance(T ruleUnitData, boolean rebuild) { + RuleUnit ruleUnit = getRuleUnit(ruleUnitData, rebuild); if (ruleUnit == null) { throw new RuntimeException("Cannot find any rule unit for RuleUnitData of class:" + ruleUnitData.getClass().getCanonicalName()); } @@ -54,7 +77,21 @@ default RuleUnitInstance createRuleUnitInstance(T ru * throwing a runtime exception if there isn't any {@link RuleUnit} generated for the given {@link RuleUnitData}. */ default RuleUnitInstance createRuleUnitInstance(T ruleUnitData, RuleConfig ruleConfig) { - RuleUnit ruleUnit = getRuleUnit(ruleUnitData); + return createRuleUnitInstance(ruleUnitData, ruleConfig, false); + } + + /** + * Creates a new {@link RuleUnitInstance} from the {@link RuleUnit} generated for the given {@link RuleUnitData} with {@link RuleConfig}. + * This is equivalent to + *
+     * RuleUnitProvider.get().getRuleUnit(ruleUnitData).createInstance(ruleUnitData, ruleConfig);
+     * 
+ * throwing a runtime exception if there isn't any {@link RuleUnit} generated for the given {@link RuleUnitData}. + * + * @param rebuild if true, the {@link RuleUnit} is regenerated before creating the {@link RuleUnitInstance}. + */ + default RuleUnitInstance createRuleUnitInstance(T ruleUnitData, RuleConfig ruleConfig, boolean rebuild) { + RuleUnit ruleUnit = getRuleUnit(ruleUnitData, rebuild); if (ruleUnit == null) { throw new RuntimeException("Cannot find any rule unit for RuleUnitData of class:" + ruleUnitData.getClass().getCanonicalName()); } diff --git a/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/DynamicHelloWorldUnit.java b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/DynamicHelloWorldUnit.java new file mode 100644 index 000000000000..54187898dde8 --- /dev/null +++ b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/DynamicHelloWorldUnit.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 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.ruleunits.dsl; + +import java.util.ArrayList; +import java.util.List; + +import org.drools.ruleunits.api.DataSource; +import org.drools.ruleunits.api.DataStore; + +import static org.drools.model.Index.ConstraintType.EQUAL; +import static org.drools.model.Index.ConstraintType.GREATER_THAN; +import static org.drools.model.Index.ConstraintType.LESS_THAN; + +public class DynamicHelloWorldUnit implements RuleUnitDefinition { + + private final DataStore strings; + + private final List results = new ArrayList<>(); + + private final String expectedMessage; // this is the dynamic part + + public DynamicHelloWorldUnit(String expectedMessage) { + this.strings = DataSource.createStore(); + this.expectedMessage = expectedMessage; + } + + public DataStore getStrings() { + return strings; + } + + + public List getResults() { + return results; + } + + @Override + public void defineRules(RulesFactory rulesFactory) { + // /strings[ this == expectedMessage ] + rulesFactory.rule() + .on(strings) + .filter(EQUAL, expectedMessage) // when no extractor is provided "this" is implicit + .execute(results, r -> r.add("it worked!")); // the consequence can ignore the matched facts + } +} diff --git a/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/RuleUnitRebuildTest.java b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/RuleUnitRebuildTest.java new file mode 100644 index 000000000000..d16d4eb820be --- /dev/null +++ b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/RuleUnitRebuildTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2023 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.ruleunits.dsl; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.drools.core.base.RuleNameStartsWithAgendaFilter; +import org.drools.ruleunits.api.DataHandle; +import org.drools.ruleunits.api.DataProcessor; +import org.drools.ruleunits.api.RuleUnitInstance; +import org.drools.ruleunits.api.RuleUnitProvider; +import org.drools.ruleunits.api.conf.RuleConfig; +import org.drools.ruleunits.dsl.domain.Cheese; +import org.drools.ruleunits.dsl.domain.Person; +import org.drools.ruleunits.impl.listener.TestAgendaEventListener; +import org.drools.ruleunits.impl.listener.TestRuleEventListener; +import org.drools.ruleunits.impl.listener.TestRuleRuntimeEventListener; +import org.junit.jupiter.api.Test; +import org.kie.api.runtime.rule.FactHandle; + +import static org.assertj.core.api.Assertions.assertThat; + +class RuleUnitRebuildTest { + + @Test + void dynamicHelloWorld() { + DynamicHelloWorldUnit unit = new DynamicHelloWorldUnit("Hello World"); + unit.getStrings().add("Hello World"); + + try (RuleUnitInstance unitInstance = RuleUnitProvider.get().createRuleUnitInstance(unit)) { + assertThat(unitInstance.fire()).isEqualTo(1); + assertThat(unit.getResults()).containsExactly("it worked!"); + } + + DynamicHelloWorldUnit newUnit = new DynamicHelloWorldUnit("Goodbye World"); + newUnit.getStrings().add("Hello World"); + + try (RuleUnitInstance newUnitInstance = RuleUnitProvider.get().createRuleUnitInstance(newUnit, true)) { // rebuild + assertThat(newUnitInstance.fire()).isZero(); + assertThat(newUnit.getResults()).isEmpty(); + + newUnit.getStrings().add("Goodbye World"); + assertThat(newUnitInstance.fire()).isEqualTo(1); + assertThat(newUnit.getResults()).containsExactly("it worked!"); + } + } + + @Test + void dynamicHelloWorldWithRuleConfig() { + TestRuleRuntimeEventListener testRuleRuntimeEventListener = new TestRuleRuntimeEventListener(); + + RuleConfig ruleConfig = RuleUnitProvider.get().newRuleConfig(); + ruleConfig.getRuleRuntimeListeners().add(testRuleRuntimeEventListener); + + DynamicHelloWorldUnit unit = new DynamicHelloWorldUnit("Hello World"); + unit.getStrings().add("Hello World"); + + try (RuleUnitInstance unitInstance = RuleUnitProvider.get().createRuleUnitInstance(unit, ruleConfig)) { + assertThat(unitInstance.fire()).isEqualTo(1); + assertThat(unit.getResults()).containsExactly("it worked!"); + assertThat(testRuleRuntimeEventListener.getResults()).containsExactly("objectInserted : Hello World"); + } + testRuleRuntimeEventListener.getResults().clear(); + + DynamicHelloWorldUnit newUnit = new DynamicHelloWorldUnit("Goodbye World"); // rule has changed + newUnit.getStrings().add("Hello World"); + + try (RuleUnitInstance newUnitInstance = RuleUnitProvider.get().createRuleUnitInstance(newUnit, ruleConfig, true)) { // rebuild + assertThat(newUnitInstance.fire()).isZero(); + assertThat(newUnit.getResults()).isEmpty(); + + newUnit.getStrings().add("Goodbye World"); + assertThat(newUnitInstance.fire()).isEqualTo(1); + assertThat(newUnit.getResults()).containsExactly("it worked!"); + assertThat(testRuleRuntimeEventListener.getResults()).containsExactly("objectInserted : Hello World", "objectInserted : Goodbye World"); + } + } +} diff --git a/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/RuleUnitProviderImpl.java b/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/RuleUnitProviderImpl.java index 6dbc8f1df5af..9ddfd5a904ac 100644 --- a/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/RuleUnitProviderImpl.java +++ b/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/RuleUnitProviderImpl.java @@ -68,12 +68,16 @@ public RuleUnitProviderImpl() { } @Override - public RuleUnit getRuleUnit(T ruleUnitData) { + public RuleUnit getRuleUnit(T ruleUnitData, boolean rebuild) { String ruleUnitName = getRuleUnitName(ruleUnitData); - RuleUnit ruleUnit = ruleUnitMap.get(ruleUnitName); - if (ruleUnit != null) { - return ruleUnit; + + if (!rebuild) { + RuleUnit ruleUnit = ruleUnitMap.get(ruleUnitName); + if (ruleUnit != null) { + return ruleUnit; + } } + ruleUnitMap.putAll(generateRuleUnit(ruleUnitData)); return ruleUnitMap.get(ruleUnitName); }