latyshas opened a new issue, #6617:
URL: https://github.com/apache/incubator-kie-drools/issues/6617
Hi everyone, we have a project that uses a very old drools dependency
version - 5.5.0.Final.
Here is the list of dependencies currently used:
```
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>${drools.version}</version>
</dependency>
```
Here is the source code used to compile custom rules for the old
(5.5.0.Final) version:
```
public static KnowledgeBase readKnowledgeBaseFromString(String ruleContent) {
KnowledgeBuilder kbuilder;
try {
Properties props = new Properties();
props.put("drools.dialect.mvel.strict", "false");
PackageBuilderConfiguration config = new
PackageBuilderConfiguration(props);
kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(config);
kbuilder.add(ResourceFactory.newByteArrayResource(ruleContent.getBytes()),
ResourceType.DRL);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
if (!errors.isEmpty()) {
StringBuilder str = new StringBuilder();
for (KnowledgeBuilderError error : errors) {
logger.error(error.getMessage());
str.append(error.getMessage()).append(';');
}
throw new CustomException(str.toString());
}
} catch (RuntimeException e){
throw e;
} catch (Exception e) {
throw new CustomException(e);
}
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
return kbase;
}
```
This static method is executed in different threads, and everything works
fine, so each thread gets its own KnowledgeBase object with corrected rules.
Several months ago, we upgraded the drools dependencies to version 10.0.0
and now to 10.1.0, and the old code was simply modified to adapt to the changes
introduced in the new version.
```
public static KieBase readKnowledgeBaseFromString(String ruleContent) {
try {
KieHelper kieHelper = new KieHelper();
kieHelper.addContent(ruleContent, ResourceType.DRL);
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.ERROR)) {
StringBuilder str = new StringBuilder();
for (Message message :
results.getMessages(Message.Level.ERROR)) {
logger.error("Rule compilation error: {}",
message.getText());
str.append(message.getText()).append(';');
}
throw new CustomException("Rule compilation failed: " + str);
}
return kieHelper.build();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new CustomException(e);
}
}
```
And this is where we encountered a problem. Now, when different threads
execute this static method, the KieBase is not unique. As a result, rules may
be taken from another rule set that was loaded by a different thread.
Here are the tests that reproduce the issue:
```
@Test
void testParallelRuleLoading() throws InterruptedException,
ExecutionException {
int threadCount = 10;
ExecutorService executorService =
Executors.newFixedThreadPool(threadCount);
List<Callable<KieBase>> tasks = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
final int index = i;
tasks.add(() -> {
String packageName = "com.mytest.rule_parallel_test_" +
index;
String rule = "package " + packageName + ";\n" +
"rule \"Test Rule " + index + "\"\n" +
" when\n" +
" then\n" +
"end";
KieBase kieBase =
RuleUtils.readKnowledgeBaseFromString(rule);
return kieBase;
});
}
List<Future<KieBase>> futures = executorService.invokeAll(tasks);
executorService.shutdown();
for (int i = 0; i < futures.size(); i++) {
String expectedPackageName = "com.mytest.rule_parallel_test_" +
i;
KieBase kieBase = futures.get(i).get();
Collection<KiePackage> packages = kieBase.getKiePackages();
List<String> packageNames = packages.stream()
.map(KiePackage::getName)
.collect(Collectors.toList());
assertThat(packageNames)
.as("Each task's KieBase should contain ONLY its unique
package")
.containsExactly(expectedPackageName);
}
}
@Test
void testSequentialRuleLoading() {
for (int i = 0; i < 5; i++) {
String packageName = "com.mytest.rule_sequential_test_" + i;
String rule = "package " + packageName + ";\n" +
"rule \"Test Rule " + i + "\"\n" +
" when\n" +
" then\n" +
"end";
KieBase kieBase = RuleUtils.readKnowledgeBaseFromString(rule);
Collection<KiePackage> packages = kieBase.getKiePackages();
List<String> packageNames = packages.stream()
.map(KiePackage::getName)
.collect(Collectors.toList());
assertThat(packageNames)
.as("Sequential call " + i + "'s KieBase should contain
ONLY its unique package")
.containsExactly(packageName);
}
}
```
When the method is modified, it works as expected:
```
public static KieBase readKnowledgeBaseFromString(String ruleContent) {
try {
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kfs = kieServices.newKieFileSystem();
String uuid = UUID.randomUUID().toString();
ReleaseId releaseId = kieServices.newReleaseId("com.test.rules",
"rule-" + uuid, "1.0.0");
kfs.generateAndWritePomXML(releaseId);
String fileName = "src/main/resources/rule_" + uuid + ".drl";
kfs.write(fileName, ruleContent);
KieBuilder kieBuilder = kieServices.newKieBuilder(kfs);
kieBuilder.buildAll();
Results results = kieBuilder.getResults();
if (results.hasMessages(Message.Level.ERROR)) {
StringBuilder str = new StringBuilder();
for (Message message :
results.getMessages(Message.Level.ERROR)) {
logger.error("Rule compilation error: {}",
message.getText());
str.append(message.getText()).append(';');
}
throw new CustomException("Rule compilation failed: " + str);
}
KieContainer kieContainer =
kieServices.newKieContainer(releaseId);
KieBaseConfiguration config =
kieServices.newKieBaseConfiguration();
KieBase kieBase = kieContainer.newKieBase(config);
kieServices.getRepository().removeKieModule(releaseId);
return kieBase;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new CustomException(e);
}
}
```
So the question is: is this a bug in the new versions of drools, or is it
the expected behavior?
Thanks, and have a good day!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]