From d881202b41072a521787f6980e623191de623c29 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Tue, 25 Jul 2023 15:48:52 +0900 Subject: [PATCH] [DROOLS-7416] Add Invalidate RuleUnit method (#5397) * [DROOLS-7416] Invalidate RuleUnit * - invalidate multiple named rule units test --- .../ruleunits/api/RuleUnitProvider.java | 6 ++ .../ruleunits/dsl/DynamicHelloWorldUnit.java | 57 +++++++++++++ .../ruleunits/dsl/NamedHelloWorldUnit.java | 61 ++++++++++++++ .../ruleunits/dsl/RuleUnitRebuildTest.java | 81 +++++++++++++++++++ .../ruleunits/impl/RuleUnitProviderImpl.java | 26 ++++++ 5 files changed, 231 insertions(+) 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/NamedHelloWorldUnit.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 851ef906d0e..761d627169b 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 @@ -61,6 +61,12 @@ default RuleUnitInstance createRuleUnitInstance(T ru return ruleUnit.createInstance(ruleUnitData, ruleConfig); } + /** + * Invalidates all {@link RuleUnit}s generated from the given class. + * @return The number of invalidated ruleunits. + */ + int invalidateRuleUnits(Class ruleUnitDataClass); + /** * Creates a new RuleConfig instance. */ 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 00000000000..f28da071116 --- /dev/null +++ b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/DynamicHelloWorldUnit.java @@ -0,0 +1,57 @@ +/* + * 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 org.drools.ruleunits.impl.NamedRuleUnitData; + +import static org.drools.model.Index.ConstraintType.EQUAL; + +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/NamedHelloWorldUnit.java b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/NamedHelloWorldUnit.java new file mode 100644 index 00000000000..b78cadf982c --- /dev/null +++ b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/NamedHelloWorldUnit.java @@ -0,0 +1,61 @@ +/* + * 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 org.drools.ruleunits.impl.NamedRuleUnitData; + +import static org.drools.model.Index.ConstraintType.EQUAL; + +public class NamedHelloWorldUnit implements RuleUnitDefinition, + NamedRuleUnitData { + + private final DataStore strings; + private final List results = new ArrayList<>(); + + private final String expectedMessage; + + public NamedHelloWorldUnit(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 == "Hello World" ] + 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 + } + + @Override + public String getUnitName() { + return getClass().getCanonicalName() + "#" + expectedMessage; + } +} 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 00000000000..ffd15b00464 --- /dev/null +++ b/drools-ruleunits/drools-ruleunits-dsl/src/test/java/org/drools/ruleunits/dsl/RuleUnitRebuildTest.java @@ -0,0 +1,81 @@ +/* + * 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 org.drools.ruleunits.api.RuleUnitInstance; +import org.drools.ruleunits.api.RuleUnitProvider; +import org.junit.jupiter.api.Test; + +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!"); + } + + int invalidated = RuleUnitProvider.get().invalidateRuleUnits(DynamicHelloWorldUnit.class); + assertThat(invalidated).as("Invalidate 1 rule unit").isEqualTo(1); + + DynamicHelloWorldUnit newUnit = new DynamicHelloWorldUnit("Goodbye World"); + newUnit.getStrings().add("Hello World"); + + try (RuleUnitInstance newUnitInstance = RuleUnitProvider.get().createRuleUnitInstance(newUnit)) { + 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 invalidateMultipleNamedRuleUnits() { + NamedHelloWorldUnit unit1 = new NamedHelloWorldUnit("Name-1"); + unit1.getStrings().add("Name-1"); + + try (RuleUnitInstance unitInstance = RuleUnitProvider.get().createRuleUnitInstance(unit1)) { + assertThat(unitInstance.fire()).isEqualTo(1); + assertThat(unit1.getResults()).containsExactly("it worked!"); + } + + NamedHelloWorldUnit unit2 = new NamedHelloWorldUnit("Name-2"); + unit2.getStrings().add("Name-2"); + + try (RuleUnitInstance unitInstance = RuleUnitProvider.get().createRuleUnitInstance(unit2)) { + assertThat(unitInstance.fire()).isEqualTo(1); + assertThat(unit2.getResults()).containsExactly("it worked!"); + } + + int invalidated = RuleUnitProvider.get().invalidateRuleUnits(NamedHelloWorldUnit.class); + assertThat(invalidated).as("Invalidate 2 rule units").isEqualTo(2); + + NamedHelloWorldUnit unit3 = new NamedHelloWorldUnit("Name-3"); + unit3.getStrings().add("Name-3"); + + try (RuleUnitInstance newUnitInstance = RuleUnitProvider.get().createRuleUnitInstance(unit3)) { + assertThat(newUnitInstance.fire()).isEqualTo(1); + assertThat(unit3.getResults()).containsExactly("it worked!"); + } + } +} 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 6dbc8f1df5a..99ebf543af8 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 @@ -31,6 +31,7 @@ import java.util.ServiceLoader; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.stream.Collectors; import org.drools.compiler.builder.conf.DecisionTableConfigurationImpl; import org.drools.compiler.kie.builder.impl.DrlProject; @@ -233,4 +234,29 @@ public RuleConfig newRuleConfig() { return new RuleConfigImpl(); } + @Override + public int invalidateRuleUnits(Class ruleUnitDataClass) { + if (NamedRuleUnitData.class.isAssignableFrom(ruleUnitDataClass)) { + // NamedRuleUnitData may create multiple RuleUnits + List invalidateKeys = ruleUnitMap.entrySet() + .stream() + .filter(entry -> hasSameRuleUnitDataClass(entry.getValue(), ruleUnitDataClass)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + invalidateKeys.forEach(ruleUnitMap::remove); + return invalidateKeys.size(); + } else { + String ruleUnitName = getRuleUnitName(ruleUnitDataClass); + RuleUnit remove = ruleUnitMap.remove(ruleUnitName); + return remove == null ? 0 : 1; + } + } + + private static boolean hasSameRuleUnitDataClass(RuleUnit ruleUnit, Class ruleUnitDataClass) { + if (ruleUnit instanceof InternalRuleUnit) { + return ((InternalRuleUnit) ruleUnit).getRuleUnitDataClass().equals(ruleUnitDataClass); + } else { + return false; + } + } }