Esteban's Blog

/dev/rnd

Knowledge Agent: Custom Class Loaders

Yes, you are right! Another Knowledge Agent’s post! I start to feeling like the “creature’s” father.

Today I want to explain one of the latests features introduced by Knowledge Agent (from now on: KA): custom Class Loaders. Indeed, this feature is so new, that it is not present even in 5.1M2 release. So, if you want to tests it, you will need to download the latest snapshot. 

Why does Knowledge Agent need custom Class Loaders?

One of the purposes of KA is to handle remote resources. Given an initial change set, the agent starts monitoring resources. Most of the times, the changes in those resources are trivial: add some rules, modifies some others, etc. But what if a new Fact Type is now being used by a new rule and its definition is not present in KA’s class loader? Previous to this new feature, this problem couldn’t be managed. The KA’s class loader should have all the possible Fact Type’s definitions loaded.

Basically, KA needs class loaders for compile resources. If you are monitoring a non binary resources, KA will need to compile them. If these resources are using classes outside the current class loader, a compilation error will occur.

Now you have two ways to manage this situation: define a custom class loader for KA’s internal builders or advice KA to use the Knowledge Base’s class loader for internal builders. Let’s see the two available options.

1. Define custom class loader for Knowledge Agent

Knowledge Agent uses KnowledgeBuilder internally in order to compile managed resources. If you need to pass custom configuration to these compilers you could pass a KnowledgeBuilderConfiguration object to KnowledgeAgentFactory.newKnowledgeAgent(). This object will be used in every kbuilder the agent creates. Using a KnowledgeBuilderConfiguration you can specify a custom class loader.

<br />
//Create a custom class loader for an external jar<br />
URL jarURL = this.getClass().getResource(&quot;/SomeExternalJar.jar&quot;);<br />
URLClassLoader ucl = new URLClassLoader(new URL[]{jarURL}, this.getClass().getClassLoader());</p>
<p>//Create a kbuilder configuration with the custom class loader<br />
KnowledgeBuilderConfiguration kbuilderConfig =<br />
     KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null, ucl);</p>
<p>//Create the agent using the builder configuration<br />
KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();<br />
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent(&quot;test agent&quot;, kbase, aconf, builderConf);<br />

The downside of this approach is that the configuration could only be passed on creation time.

2. Reuse Knowledge Base’s class loader

The second solution is to reuse the class loader of the agent’s knowledge base.

Most of the times the knowledge base should have the correct class loader, so the rules can run ok. So instead of pass a custom class loader to be used by the agent’s kbuilders, you can advise the agent to reuse the kbase’s class loader.

Another advantage of this approach is that you could change the kbase’s class loader in runtime, and the agent will see that changes immediately. You can reuse kbase’s class loader even if you are not using incremental change set processing. Whenever the agent recreates its kbase, the  kbase’s configuration is reused. Inside this configuration is the class loader.

If you wan’t to use this new feature, you just need to add a new property to Knowledge Agent configuration object: drools.agent.useKBaseClassLoaderForCompiling.

<br />
//Create a custom class loader for an external jar<br />
URL jarURL = this.getClass().getResource(&quot;/SomeExternalJar.jar&quot;);<br />
URLClassLoader ucl = new URLClassLoader(new URL[]{jarURL}, this.getClass().getClassLoader());</p>
<p>//Add the classloader to the kbase<br />
KnowledgeBaseConfiguration kbaseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, ucl);<br />
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig);</p>
<p>//Create the agent using drools.agent.useKBaseClassLoaderForCompiling property<br />
KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();<br />
aconf.setProperty(<br />
     &quot;drools.agent.useKBaseClassLoaderForCompiling&quot;,<br />
     &quot;true&quot;); //by default is false<br />
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent(&quot;test agent&quot;, kbase, aconf);<br />

Advertisements

3 comments on “Knowledge Agent: Custom Class Loaders

  1. Giri
    June 3, 2011

    I am trying to update jar at runtime and fire the rules.When my drl try to access the updated jar i get the class not found exception.
    Here is my code:

    public class DroolsClass {

    public void fireRules(Object object){
    ClassLoader loader=null;
    URL jarPath=null;
    try {
    jarPath=new File(“lib/Billing.jar”).toURI().toURL();
    loader = new URLClassLoader(new URL[] { jarPath },ClassLoader.getSystemClassLoader());
    KnowledgeBase kbase = readKnowledgeBase(loader);
    StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
    ksession.insert(object);
    ksession.fireAllRules();

    } catch (Throwable t) {
    t.printStackTrace();
    }
    }
    private static KnowledgeBase readKnowledgeBase(ClassLoader loader) throws Exception {

    KnowledgeBuilderConfiguration kBuilderConfiguration = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null, loader);
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(kBuilderConfiguration);

    kbuilder.add(ResourceFactory.newFileResource(“./rulefiles/testing.drl”), ResourceType.DRL);

    KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
    kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
    return kbase;
    }
    }

    How can I make this work?
    Thanks.

    • esteban
      June 3, 2011

      Even if this is not directly associated with Knowledge Agent, I will try to help you.
      From what you say, I assume that you have a rule inside “./rulefiles/testing.drl” that is using a class defined in “Billing.jar”.
      You are passing your customized classloader to kbuiler so it is able to compile the drl. That is good.
      What you are missing is to configure the kbase to also know about your custom classloader:

      KnowledgeBaseConfiguration kbaseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, loader);
      KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig);

      • giri
        June 6, 2011

        Thanks :)Thats what I was missing.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Information

This entry was posted on June 22, 2010 by in drools, java.
%d bloggers like this: