Skip to content

Commit

Permalink
[DROOLS-7416] Add Invalidate RuleUnit method (#5397)
Browse files Browse the repository at this point in the history
* [DROOLS-7416] Invalidate RuleUnit

* - invalidate multiple named rule units test
  • Loading branch information
tkobayas authored Jul 25, 2023
1 parent 94e6671 commit 97f1f92
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ default <T extends RuleUnitData> RuleUnitInstance<T> createRuleUnitInstance(T ru
return ruleUnit.createInstance(ruleUnitData, ruleConfig);
}

/**
* Invalidates all {@link RuleUnit}s generated from the given class.
* @return The number of invalidated ruleunits.
*/
<T extends RuleUnitData> int invalidateRuleUnits(Class<T> ruleUnitDataClass);

/**
* Creates a new RuleConfig instance.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> strings;

private final List<String> 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<String> getStrings() {
return strings;
}


public List<String> 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
}
}
Original file line number Diff line number Diff line change
@@ -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<String> strings;
private final List<String> results = new ArrayList<>();

private final String expectedMessage;

public NamedHelloWorldUnit(String expectedMessage) {
this.strings = DataSource.createStore();
this.expectedMessage = expectedMessage;
}

public DataStore<String> getStrings() {
return strings;
}

public List<String> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<DynamicHelloWorldUnit> 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<DynamicHelloWorldUnit> 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<NamedHelloWorldUnit> 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<NamedHelloWorldUnit> 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<NamedHelloWorldUnit> newUnitInstance = RuleUnitProvider.get().createRuleUnitInstance(unit3)) {
assertThat(newUnitInstance.fire()).isEqualTo(1);
assertThat(unit3.getResults()).containsExactly("it worked!");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -233,4 +234,29 @@ public RuleConfig newRuleConfig() {
return new RuleConfigImpl();
}

@Override
public <T extends RuleUnitData> int invalidateRuleUnits(Class<T> ruleUnitDataClass) {
if (NamedRuleUnitData.class.isAssignableFrom(ruleUnitDataClass)) {
// NamedRuleUnitData may create multiple RuleUnits
List<String> 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 <T extends RuleUnitData> boolean hasSameRuleUnitDataClass(RuleUnit ruleUnit, Class<T> ruleUnitDataClass) {
if (ruleUnit instanceof InternalRuleUnit) {
return ((InternalRuleUnit) ruleUnit).getRuleUnitDataClass().equals(ruleUnitDataClass);
} else {
return false;
}
}
}

0 comments on commit 97f1f92

Please sign in to comment.