Reputation: 76
Recently i was working on a drools project where i came across certain issues and i need some help. In my project i access jar at runtime by making use of the URLClassLoader.Here is the code :
Object object=null;
Class myclass=null;
URL jarPath=null;
try{
jarPath=new File("lib/Billing.jar").toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] { jarPath },ClassLoader.getSystemClassLoader());
ruleclass = loader.loadClass("dynamicclasses.Billing");
object = ruleclass.newInstance();
}
catch (Exception e) {e.printStackTrace}
After getting the class instance I will set the values and pass the object to my drools class
new DroolsClass().fireRules(object);
The drools class contain the following code :
public class DroolsClass {
public void fireRules(Object object){
try {
KnowledgeBase kbase = readKnowledgeBase();
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
ksession.insert(object);
ksession.fireAllRules();
logger.close();
} catch (Throwable t) {
t.printStackTrace();
}
}
private static KnowledgeBase readKnowledgeBase() throws Exception {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newFileResource("./rulefiles/testing.drl"), ResourceType.DRL);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
// ------ some code
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
return kbase;
}
Next I have a drl file testing.drl which is present in the file directoty and which accesses the same class dynamicclasses.Billing which is present in Billing.jar
Here is the drl file content :
import dynamicclasses.Billing;
rule "rule 3"
salience 10
dialect "mvel"
no-loop true
when
m : Billing(bplan=="plan1")
then
System.out.println("You have opted for plan1");
end
The problem I encounter is when the jar i.e Billing.jar get updated at runtime the drl file i.e testing.drl is not able to access the updated jar.
The following things i am trying to do.
1) I will create jar at runtime and update it if required. 2) I will create a drl file at runtime which will import the class present in the jar i.e. dynamicclasses.Billing
I am able to access the updated jar contents in my java class using URLClassLoader .But once i pass the object to my drools class I get the following exception
Unable to resolve ObjectType 'Billing' : [Rule name='rule 3']
Error importing : 'dynamicclasses.Billing'
java.lang.IllegalArgumentException: Could not parse knowledge.
If i restart my application i get no exception since the jar is already present but the object doesn't seem to pass to the drl and i get no result.
I made the following changes to the code :
URLClassLoader loader = new URLClassLoader(new URL[] { jarPath },this.getClass().getClassLoader());
Here i get the same exception initially. But after i restart my application it works fine and the rule get fired and i will get the result.
But again if i update the jar it will access only the previous content.
So its clear that the drl file is not able to access the jar intially or even after updating. Is there any way so that i can make it work?
Thanks.
Upvotes: 3
Views: 8200
Reputation: 458
For Drool 6.5.0, Class Loader can be added by:
kieServices = KieServices.Factory.get();
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
kieFileSystem.write("./rulefiles/testing.drl", drl);
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem, loader);
kieBuilder.buildAll();
KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId(),
pluginCtxObject.getClass().getClassLoader());
StatelessKieSession statelessKieSession = kieContainer.getKieBase().newStatelessKieSession();
statelessKieSession.execute(object);
Upvotes: 0
Reputation: 76
Thanks :) Finally I am able to solve this.You need to pass your custom class loader not only to KnowledgeBuilderConfiguration but also to your KnowledgeBaseConfiguration to make even your knowledge base aware of your custom class loader .
private static KnowledgeBase readKnowledgeBase(ClassLoader loader) throws Exception {
KnowledgeBuilderConfiguration kBuilderConfiguration = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null, loader);
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(kBuilderConfiguration);
KnowledgeBaseConfiguration kbaseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, loader);
kbuilder.add(ResourceFactory.newFileResource("./rulefiles/testing.drl"), ResourceType.DRL);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
if (errors.size() > 0) {
for (KnowledgeBuilderError error: errors) {
System.err.println(error);
}
throw new IllegalArgumentException("Could not parse knowledge.");
}
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig);
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
return kbase;
}
Upvotes: 2
Reputation: 983
You can try passing your class loader for the KnowledgeBuilder using KnowledgeBuilderConfiguration.
ClassLoader loader = new URLClassLoader(
new URL[] { jarPath },
ClassLoader.getSystemClassLoader());
KnowledgeBuilderConfiguration kBuilderConfiguration
= KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null, loader);
KnowledgeBuilder kbuilder
= KnowledgeBuilderFactory.newKnowledgeBuilder(kBuilderConfiguration);
Upvotes: 1