我的应用程序是一个服务器程序,其中包含执行Drools规则的功能。 Drools定义位于已配置文件夹中的.drl文件中。在程序启动时,我创建了一个带有几行Java代码的KieContainer
。然后我使用文件夹监视器来监视文件更改,在这种情况下,我创建了一个新的辅助KieContainer。如果失败(由kieBuilder.getResults().hasMessages(Message.Level.ERROR)
检查),则会记录错误,规则人员可以修复它。此外,现场的KieContainer将替换为新的。
我有一个工作版本,但似乎有副作用。
我不打算使用Maven和KieScanner
重新部署新版本。我使用Maven,但我真的不认为这是我的理由。
也不是具有REST API的执行服务器
我的解决方案应该很简单。与Logback相比,它甚至可以在幕后自行重新加载logback.xml
配置文件。我的感觉是,这比必要的更复杂。
create()方法在程序启动时使用,然后在文件系统看到更改时再次使用:
private KieContainer create() {
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
File[] rulesFiles = rulesDir.listFiles((dir, name) -> name.endsWith(".drl"));
for (File rulesFile : rulesFiles) {
final Resource resource = ResourceFactory.newFileResource(rulesFile.getAbsolutePath());
String targetPath = rulesFile.getAbsolutePath().replaceAll("\\\\", "\\/"); //slashes must be standardized for it to work
resource.setTargetPath(targetPath);
kieFileSystem.write(resource);
}
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
kieBuilder.buildAll();
Results results = kieBuilder.getResults();
if (results.hasMessages(Message.Level.ERROR)) {
//logging not shown for brevity
//PROBLEM: even though we abort here, it seems that the lines above modify the kieContainer that is currently in use!
throw new IllegalStateException("Errors occurred while reading rules.");
}
KieModule kieModule = kieBuilder.getKieModule();
return kieServices.newKieContainer(kieModule.getReleaseId());
}
当Drools文件出现问题时,创建将中止,并保留当前的KieContainer。
如果成功,则要替换的代码如下:
/**
* Called from the directory watcher, so we are in a separate thread here.
*/
private void refresh() {
try {
KieContainer oldContainer;
//first try to create a new one behind the scenes, while requests concurrently still use the old one:
KieContainer newContainer = create();
//then swap it out, thread safe:
synchronized (this) {
oldContainer = this.kieContainer;
this.kieContainer = newContainer;
}
//log not shown for brevity
//then we have time to dispose the old one while no one waits:
if (oldContainer != null) {
//the current container may still be used by current threads.
//i am not aware what dispose does in this regard, i think we should wait a bit until all use is finished.
Thread.sleep(2000);
oldContainer.dispose();
}
} catch (Exception e) {
//log not shown for brevity
}
}
当有人编辑.drl
个文件并且出现错误时,有几次发生了奇怪的事情。 KieContainer
未被替换(根据需要),但Drools方法调用的副作用似乎已经发生。
KieServices
是单身,很好。
我们创建一个新的KieFileSystem
,看起来不错,新的断开连接的实例。
我们打电话给kieFileSystem.write()
,应该没问题?
然后kieServices.newKieBuilder()
再次返回一个新的断开连接的实例。
因此kieBuilder.buildAll()
应该没问题?
调试时,我可以看到两个KieBuilder都有相同的KieModule实例?
错误和中止刷新的一种方式是Drools编辑人员复制.drl文件,然后打算编辑它。文件系统接收更改,并且加载规则失败,因为我们有一个重复的文件(相同的内容,相同的包)。
我的代码出了什么问题,我可以更改什么才能使新的KieContainer创建与以前加载的完全断开?