Search code examples
javadrools

How to make drools drl file access updated jar at runtime


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.


Solution

  • 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;
        }