2

I am working on a system that uses drools to evaluate certain objects. However, these objects can be of classes that are loaded at runtime using jodd. I am able to load a file fine using the following function:

 public static void loadClassFile(File file) {
    try {
        // use Jodd ClassLoaderUtil to load class into the current ClassLoader
        ClassLoaderUtil.defineClass(getBytesFromFile(file));
    } catch (IOException e) {
        exceptionLog(LOG_ERROR, getInstance(), e);
    }
 }

Now lets say I have created a class called Tire and loaded it using the function above. Is there a way I can use the Tire class in my rule file:

rule "Tire Operational"
when
    $t: Tire(pressure == 30)
then

end

Right now if i try to add this rule i get an error saying unable to resolve ObjectType Tire. My assumption would be that I would somehow need to import Tire in the rule, but I'm not really sure how to do that.

UBIK LOAD PACK
  • 33,980
  • 5
  • 71
  • 116
  • I tried writing "import datatypes.Tire" in the rule file. (datatypes is the package that the tire class should be in). Now instead of saying cannot resolve tire, it says that Tire cannot be resolved to a type. So I am interpreting this as that Tire is being resolved to a package instead of a type and therefore cannot be imported. Why might this be? – Kevin Waitforit Desmond Jul 05 '12 at 15:22

1 Answers1

1

Haven't use Drools since version 3, but will try to help anyway. When you load class this way (dynamically, in the run-time, no matter if you use e.g. Class.forName() or Jodd), loaded class name is simply not available to be explicitly used in the code. I believe we can simplify your problem with the following sudo-code, where you first load a class and then try to use its name:

defineClass('Tire.class');
Tire tire = new Tire();

This obviously doesn't work since Tire type is not available at compile time: compiler does not know what type you gonna load during the execution.

What would work is to have Tire implementing some interface (e.g. VehiclePart). So then you could use the following sudo-code:

Class tireClass = defineClass('Tire.class');
VehiclePart tire = tireClass.newInstance();
System.out.println(tire.getPartName()); // prints 'tire' for example

Then maybe you can build your Drools rules over the interface VehiclePart and getPartName() property.

Addendum

Above make sense only when interface covers all the properties of dynamically loaded class. In most cases, this is not a valid solution: dynamically loaded classes simply do not share properties. So, here is another approach.

Instead of using explicit class loading, this problem can be solved by 'extending' the classloader class path. Be warn, this is a hack!

In Jodd, there is method: ClassLoaderUtil.addFileToClassPath() that can add a file or a path to the classloader in the runtime. So here are the steps that worked for me:

1) Put all dynamically created classes into some root folder, with the respect of their packages. For example, lets say we want to use a jodd.samples.TestBean class, that has two properties: number (int) and a value (string). We then need to put it this class into the root/jodd/samples folder.

2) After building all dynamic classes, extend the classloaders path:

ClassLoaderUtil.addFileToClassPath("root", ClassLoader.getSystemClassLoader());

3) load class and create it before creating KnowledgeBuilder:

Class testBeanClass = Class.forName("jodd.samples.TestBean");
Object testBean = testBeanClass.newInstance();

4) At this point you can use BeanUtils (from Jodd, for example:) to manipulate properties of the testBean instance

5) Create Drools stuff and add insert testBean into session:

knowledgeSession.insert(testBean);

6) Use it in rule file:

import jodd.samples.TestBean;

rule "xxx"
    when
    $t: TestBean(number == 173)
then
    System.out.println("!!!");
end

This worked for me. Note that on step #2 you can try using different classloader, but you might need it to pass it to the KnowledgeBuilderFactory via KnowledgeBuilderConfiguration (i.e. PackageBuilderConfiguration).

Another solution

Another solution is to simply copy all object properties to a map, and deal with the map in the rules files. So you can use something like this at step #4:

Map map = new HashMap();
BeanTool.copy(testBean, map);

and later (step #5) add a map to Drools context instead of the bean instance. In this case it would be even better to use defineClass() method to explicitly define each class.

igr
  • 10,199
  • 13
  • 65
  • 111
  • thank you! We already have an interface called DataType so your solution should be very simple to implement. Thank you very much! – Kevin Waitforit Desmond Jul 09 '12 at 13:07
  • I tried your solution and I received the following error. I defined the class in the when clause, is this correct or should it be somewhere else? [12,7]: [ERR 102] Line 12:7 mismatched input 'tireClass' in rule "Tire Operational" [0,0]: Parser returned a null Package – Kevin Waitforit Desmond Jul 09 '12 at 13:13
  • oops i just realized you said sudo code of course that doesnt work. sorry – Kevin Waitforit Desmond Jul 09 '12 at 13:34
  • building the Drools rules over the interface won't be very useful in my system. I need to be able to use the dynamic classes in the rules. For example i need to test the pressure of the Tire. The interface wont have the attribute pressure. – Kevin Waitforit Desmond Jul 09 '12 at 16:43
  • Thank you very much for all the help. Our project wasn't working because the directories of the classes didn't match the package names. – Kevin Waitforit Desmond Jul 10 '12 at 13:46