I am trying to evaluate JBoss Drools for a validation service that we are building from scratch (via Spring Boot) but I am having some trouble understanding and differentiating between the multiple APIs that exist.
My first try was a rather simple approach, taken more or less directly from a web tutorial but it worked. With the single dependency of drools-core 6.0.1 I could get a RuleBase with the following code:
public static RuleBase readRule(String ruleFile) throws Exception {
// read in the source
Reader source = new InputStreamReader(RuleUtils.class.getClassLoader().getResourceAsStream("rules/" + ruleFile));
PackageBuilder builder = new PackageBuilder();
// this will parse and compile in one step
builder.addPackageFromDrl(source);
//check for errors
if (builder.hasErrors()) {
System.out.println(builder.getErrors().toString());
}
// get the compiled package (which is serializable)
Package pkg = builder.getPackage();
// add the package to a rulebase (deploy the rule package).
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(pkg);
return ruleBase;
}
With that RuleBase, I could create a new stateful session (WorkingMemory) and then insert objects and fire my rules. However, I have read here that this approach seems to be deprecated and also I would not want to use an obsolete version of Drools for a shiny new project (6.0.1 as opposed to the current version 7.5.0).
Another API that was very common in a lot of tutorials was this:
public static RuleBase readRule(String ruleFile) throws Exception {
// read in the source
Reader source = new InputStreamReader(RuleUtils.class.getClassLoader().getResourceAsStream("rules/" + ruleFile));
KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
// this will parse and compile in one step
kBuilder.add(ResourceFactory.newReaderResource(source), ResourceType.DRL);
//check for errors
if (kBuilder.hasErrors()) {
System.out.println(kBuilder.getErrors().toString());
}
// get the compiled package (which is serializable)
Collection<KnowledgePackage> pkgs = kBuilder.getKnowledgePackages();
// add the package to a rulebase (deploy the rule package).
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(pkgs);
return kbase;
}
But that is where the confusion starts. All of these classes seem to be available in two separate libraries (org.drools and org.kie.internal). With org.drools I could not get the above code to work - I always got a java.lang.ClassNotFoundException:
org.drools.builder.impl.KnowledgeBuilderFactoryServiceImpl - I have no idea which library I might have missed. This is an excerpt of my pom.xml which already is much larger than with the first approach, even though I am not sure which libraries I actually need (the official documentation is not extremely helpful here):
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-api</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>7.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-internal</artifactId>
<version>7.5.0.Final</version>
</dependency>
When I use the other implementation, my KnowledgeBase gets marked as deprecated, which seems to be an obvious hint not to use that anymore. I have then found yet another suggestion on how to actually implement this with Drools 6.x and tried that as well:
public static KieBase readRuleKie(String ruleFile) throws Exception {
// read in the source
Reader source = new InputStreamReader(RuleUtils.class.getClassLoader().getResourceAsStream("rules/" + ruleFile));
// Get access to Drools services
KieServices services = KieServices.Factory.get();
// Obtain a new empty virtual file system
KieFileSystem fileSystem = services.newKieFileSystem();
// Load a DRL resource from src/main/resources into the virtual file system
String location = "/rules/" + ruleFile;
InputStream stream = RuleUtils.class.getClassLoader().getResourceAsStream("rules/" + ruleFile);
Resource resource = ResourceFactory.newInputStreamResource(stream);
fileSystem.write("src/main/resources" + location, resource);
// Convert the files in the virtual file system into a builder
KieBuilder builder = services.newKieBuilder(fileSystem).buildAll();
// Check for errors, print them and stop if any
Results results = builder.getResults();
if (results.hasMessages(Message.Level.ERROR)) {
System.out.println(results.getMessages());
}
// Create a new kie base out of a repository and a container
KieRepository repository = services.getRepository();
KieContainer container = services.newKieContainer(repository.getDefaultReleaseId());
KieBase base = container.getKieBase();
return base;
}
Now, this actually worked, but not before also adding a dependency to XStream in Version 1.4.10 to my project. But still, I seem to be using deprecated libraries for building my KnowledgeBase/KieBase/RuleBase. Furthermore, the API seems to get more and more verbose with every iteration.
The question is, what would I need to do or change in the above code so that I can get it to work with Drools 7? The API seems to have changed completely and I'd rather not use a kmodule.xml file in my META-INF which seems to be the favored approach by JBoss.
I could always just go with the first example, but that is no longer possible with the newer Drools APIs, and I want to avoid being dependent on old libraries.
This should work with any Drools version of 7.x:
private KieSession kieSession;
public void build() throws Exception {
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kfs = kieServices.newKieFileSystem();
FileInputStream fis = new FileInputStream( "simple/simple.drl" );
kfs.write( "src/main/resources/simple.drl",
kieServices.getResources().newInputStreamResource( fis ) );
KieBuilder kieBuilder = kieServices.newKieBuilder( kfs ).buildAll();
Results results = kieBuilder.getResults();
if( results.hasMessages( Message.Level.ERROR ) ){
System.out.println( results.getMessages() );
throw new IllegalStateException( "### errors ###" );
}
KieContainer kieContainer =
kieServices.newKieContainer( kieServices.getRepository().getDefaultReleaseId() );
KieBase kieBase = kieContainer.getKieBase();
kieSession = kieBase.newKieSession();
}
Considering that you need to have the option of loading multiple resources into the kfs, need a point where you can respond to errors and need an opportunity to serialize the created KieBase, this isn't so bad.
As for the libraries, check what the distribution package contains and proceed accordingly. For 7.3.0 Final, a basic classpath setup would be like so (without any guarantees - just as a guideline):
root=/extra/drools-distribution-7.3.0.Final/binaries
tag=7.3.0.Final
export CLASSPATH=".:$root/drools-core-${tag}.jar:$root/kie-api-${tag}.jar:$root/kie-internal-${tag}.jar:$root/knowledge-internal-api-${tag}.jar:$root/drools-compiler-${tag}.jar:$root/antlr-runtime-3.5.2.jar:$root/ecj-4.4.2.jar:$root/mvel2-2.3.0.Final.jar:/extra/quartz-1.8.3/quartz-1.8.3.jar:$root/drools-decisiontables-${tag}.jar:$root/drools-templates-${tag}.jar:$root/protobuf-java-2.6.0.jar:$root/slf4j-api-1.7.7.jar:$root/xstream-1.4.9.jar:$SLF4J"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With