Friday, May 15, 2015

How to use Drools 6.2 in .Net using IKVM?



I have seen many posts asking about how to use or convert drools 6.2 in .net. I found few posts where someone had converted an older version of drools to .net but a lot has changed since then. In this post, I will show you how to use IKVM to convert Drools 6.2 jar files to .Net dll and use it in C#.


IKVM


IKVM.NET is an implementation of Java for Mono and the Microsoft .NET Framework. IKVM comes with IKVMC utility which is a Java bytecode to .NET IL translator. We will be using IKVMC to convert jar files to .net dll.


You can read more about IKVM at http://www.ikvm.net

  • Before you start, make sure that you have already installed java and drools on you machine.
  • Make sure JAVA_HOME variable is already set.


Convert Jar files to Dll


The simplest way to convert drools is to convert all jar files present in the binaries folder of drools installation. This will give you one big dll but you will save your self with class loading issues. It is a tedious task to find complete dependency tree of each jar and then convert them to dlls in correct order. This post will only focus on converting all jars to one dll solution but if you want to convert only required jars to a dll then you can read about jar2ikvmc and jaranalyzer
  • Open command prompt and change your path to drools installation e.g. C:\drools\6.2\drools-distribution-6.2.0.Final
  • Run following command: 
                 ikvmc -target:library -recurse:binaries/*.jar -out:mydrools.dll

Ignore all exceptions that you see on your command prompt and make sure you have mydrools.dll generated under your main drools directory outside binaries folder.

Using .Net dll in C#

  • Open Visual Studio and create a test console or windows application. 
  • Add a reference to following dlls
    • mydrools.dll (This is the dll that you just generated using ikvmc)
    • ikvm.runtime
    • ikvm.openjdk.core
Depending on how you have written your rules and code, you might also need to add references to ikvm.openjdk.jdbc and ikvm.openjdk.xml.parse
  • Here is the typical code that you would write in order to dynamically build KieBase, build your Rules and fire them.
            KieServices ks = KieServices.Factory.get();
            Resource rs = ks.getResources().newFileSystemResource(@"c:\drools\myrules.drl");
            KieFileSystem kfs = ks.newKieFileSystem();

            kfs.write("src/main/resources/some.drl", rs);
            KieBuilder kb = ks.newKieBuilder(kfs).buildAll();
            Results results = kb.getResults();
            if (results.hasMessages(org.kie.api.builder.Message.Level.ERROR))
                Console.WriteLine(results.getMessages(Message.Level.ERROR));

            KieContainer kc = ks.newKieContainer(ks.getRepository().getDefaultReleaseId());
            KieBase kBase = kc.getKieBase();
            KieSession session = kBase.newKieSession();
    session.fireAllRules();
            session.dispose();


Potential Issues

I ran into few issues during this process and I will document them here with the solutions in case anyone encounter them.

  • If you are trying to convert java code compiled with the latest version of jdk (1.8.*), you might get class format error "51.0" from ikvmc. SOLUTION: This happens because class format has changed in jdk 1.8.*. You can fix this issue by compiling your java source code with an earlier version of jdk or try compiling ikvm source code against latest jdk.
  • After converting the drools to .Net, I noticed that the performance was a bit slower and also found the memory leak issues. The whole process of reloading the knowledge base with new rules was slower as it seemed that previous objects were not being cleared. After trying different workarounds and solutions, I found out that IKVM version available for download was compiled against .Net framework 2.0, which didn't offer code garbage collection back then. SOLUTION: Download the source for for IKVM and compile it against .Net framework 4.0. it comes with a read me file that has instructions on how to compile the code with nant.

Alternative to IKVM

While I was running into issues with IKVM, I also tried JNBride (http://jnbridge.com), which is a licensed software and unlike IKVM you will have to spend $$$ to use it. This is basically a bridge between .Net and Java and use proxy classes to communicate with JVM. I found it to be much faster than IKVM and basically performance was as good as running it directly in java. We are talking about milliseconds here when it comes to firing the rules. Building the knowledge base and your rules is an expensive operation that usually takes seconds. With IKVM it took 21 sec to build a drool file with 1,000 rules whereas JNBridge only took 9 secs.

I hope it helps.

4 comments:

  1. Hello Asif( I konw this is a fake name :) ), thank you for your post, this really help me a lot. I am not familiar with drools neither ikvm, but I still convert the jars to .net assembly follow your instruction. When I try the code in this post, I got some file dependency missing errors. I am no idea how to fix it. Can you give some suggestions please? There no email or any other information to in touch with you,so can you mail me your email address? my email is:intecax@gmail.com. thank you very much.

    ReplyDelete
  2. Hi Asif I am also looking for using a BRE with .NET stack and it seems that Drools.Net is quite old. Were you able to successfully use Drool with .NET on a production system?

    With no well maintained opensource BRE for .NET I believe I am left only with Sql
    http://stackoverflow.com/questions/40583908/database-drive-business-rule-engine

    ReplyDelete
  3. Hi Asif,

    I am trying to do the same thing, in my case drl file is not recognizing my class files which are a part of same dll.

    Any help will be really appreciated.

    Thanks

    ReplyDelete
  4. hi,
    kie drools wb distribution 6.2 is used to compile rules and execute in visual studio 2015, when i call a c# based function in rule, its show errors. giving kie drools vs 2015 c# code and rule engine drl file codes are given below,

    c# code

    CustomerRecord workingObj = new CustomerRecord();

    // Load the knowledge base:
    KieServices ks = KieServices.Factory.get();

    workingObj.Id = "111-111";
    workingObj.Status = "NORMAL";
    workingObj.Value = 5;
    workingObj.DaysSinceLastOrder = 95;


    Resource rs = ks.getResources().newFileSystemResource(@"C:\Users\Binu\Music\KIE_Rule1.drl");
    KieFileSystem kfs = ks.newKieFileSystem();

    kfs.write("src/main/resources/simple.drl", rs);
    Console.WriteLine("\nStream Set ");
    Console.WriteLine("\nReading DRL (Please wait... this may take a few moments...) ");

    KieBuilder kb = ks.newKieBuilder(kfs).buildAll();
    Console.WriteLine("... Finished ");
    Results results = kb.getResults();
    if (results.hasMessages(org.kie.api.builder.Message.Level.ERROR))
    Console.WriteLine(results.getMessages(Message.Level.ERROR));



    KieContainer kContainer = ks.newKieContainer(ks.getRepository().getDefaultReleaseId());
    KieSession kSession = kContainer.newKieSession();



    kSession.insert(workingObj);
    kSession.fireAllRules();
    Console.ReadLine();
    rule.drl

    package com.IKVM_Drools_App
    rule "LargeOrder"
    when
    custOrder : CustomerRecord( Value > 50 )
    then
    System.out.println("Large Order");
    end


    kie drools wb distribution 6.2 is used to compile rules and execute in visual studio 2015, when i call a c# based function in rule, its show errors. giving kie drools vs 2015 c# code and rule engine drl file codes are given below,

    c# code

    CustomerRecord workingObj = new CustomerRecord();

    // Load the knowledge base:
    KieServices ks = KieServices.Factory.get();

    workingObj.Id = "111-111";
    workingObj.Status = "NORMAL";
    workingObj.Value = 5;
    workingObj.DaysSinceLastOrder = 95;


    Resource rs = ks.getResources().newFileSystemResource(@"C:\Users\Binu\Music\KIE_Rule1.drl");
    KieFileSystem kfs = ks.newKieFileSystem();

    kfs.write("src/main/resources/simple.drl", rs);
    Console.WriteLine("\nStream Set ");
    Console.WriteLine("\nReading DRL (Please wait... this may take a few moments...) ");

    KieBuilder kb = ks.newKieBuilder(kfs).buildAll();
    Console.WriteLine("... Finished ");
    Results results = kb.getResults();
    if (results.hasMessages(org.kie.api.builder.Message.Level.ERROR))
    Console.WriteLine(results.getMessages(Message.Level.ERROR));



    KieContainer kContainer = ks.newKieContainer(ks.getRepository().getDefaultReleaseId());
    KieSession kSession = kContainer.newKieSession();



    kSession.insert(workingObj);
    kSession.fireAllRules();
    Console.ReadLine();
    rule.drl

    package com.IKVM_Drools_App
    rule "LargeOrder"
    when
    custOrder : CustomerRecord( Value > 50 )
    then
    System.out.println("Large Order");
    end
    Run code snippetExpand snippet
    this rule produce errors when compile, help me to find a solution, thanks in advance..

    errors showing in Console given "[Message [id=1, level=ERROR, path=simple.drl, line=5, column=0 text=Unable to resolve ObjectType 'CustomerRecord']]"

    ReplyDelete