I've upgraded OptaPlanner to use the KIE api and I have some
feedback :)
What I want to do:
Given of list of DRL's on the classpath, build a KieSession.
Here's how I did it with the new KIE api:
public KieBase newKieBase(List<String> scoreDrlList) {
KieServices kieServices =
KieServices.Factory.get();
KieResources kieResources
= kieServices.getResources();
KieFileSystem
kieFileSystem = kieServices.newKieFileSystem();
for (String scoreDrl : scoreDrlList) {
InputStream scoreDrlIn =
getClass().getResourceAsStream(scoreDrl);
// TODO newClassPathResource() instead, but that
breaks (mfusco is looking at it)
String path =
"src/main/resources/optaplanner-kie-namespace/" + scoreDrl;
kieFileSystem.write(path,
kieResources.newInputStreamResource(scoreDrlIn, "UTF-8"));
}
KieBuilder kieBuilder =
kieServices.newKieBuilder(kieFileSystem);
kieBuilder.buildAll();
Results results =
kieBuilder.getResults();
if (results.hasMessages(Message.Level.ERROR)) {
throw new IllegalStateException("There are errors in
the scoreDrl's:\n"
+ results.toString());
} else if (results.hasMessages(Message.Level.WARNING)) {
logger.warn("There are warning in the scoreDrl's:\n"
+ results.toString());
}
KieContainer kieContainer
= kieServices.newKieContainer(kieBuilder.getKieModule().getReleaseId());
KieBase kieBase =
kieContainer.getKieBase();
return kieBase;
}
public KieSession newKieSession() {
...
KieSession kieSession = kieBase.newKieSession();
return kieSession;
}
Feedback (note: these are suggestions, not demands):
1) To do this, I needed to get acquainted with 9 new concepts:
KieServices, KieResources, KieFileSystem, KieBuilder, Results,
KieModule, KieContainer, KieBase, KieSession.
I only care about 2 of those:
- KieSession
- KieBase (= the factory to build a new KieSession, which I do
regularly).
The other 7 concepts probably represent wonderful advanced features
which I might want to use later on,
so they should exist,
But in a simple use case like this, I should not have to see them or
deal with them.
2) The kie filesystem features leaks into the simple api, even if I
don't care about the KieFileSystem at all.
- I should not explicitly need to create a KieFileSystem:
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
- Adding certain Resource types fails if you don't specify the
fileSystem's path. This fails for example:
kieFileSystem.write(kieResources.newInputStreamResource(...));
- For the default KieBase to work, the path needs to start with a magical prefix
String path = "src/main/resources/.../"
+ ...;
3) To create a KieBase from that kieFileSystem, I need to explicitly
do 5+ calls, in the right order:
- call kieServices.newKieBuilder(...)
- call kieBuilder.buildAll()
- call kieBuilder.getKieModule()
- call kieServices.newKieContainer(...)
- call kieContainer.getKieBase()
Some of these have non-obvious parameters.
I'd rather just call:
kieFileSystem.buildKieBase()
4) I don't like having to write the boilerplate code to check if the
are errors:
if (results.hasMessages(Message.Level.ERROR)) {...}
If there are errors, then kieFileSystem.buildKieBase() should just
throw a DroolsRuntimeException.
Solution proposal:
A) Write a facade class that presumes there is only 1 module and the
user doesn't need (or want to) to access any of those 7 other
concepts.
public KieBase newKieBase(List<String> scoreDrlList) {
KieBaseFactory kieBaseFactory =
kieServices.newKieBaseFactory();
for (String scoreDrl : scoreDrlList) {
kieBaseFactory.addInputStreamResource(scoreDrl, "UTF-8");
}
return kieBaseFactory.buildKieBase; // throws exception upon
compilation error
}
Mario's KieHelper is similar, but slightly different.
wkr,
Geoffrey