Search code examples
mavendrools

With Drools 6.x how do I avoid Maven and the Compiler?


I'm looking into providing a 'hot-swap' capability for switching between different versions of sets of drools rules. The target deployment is in WebSphere, and the rules will all be run in stateless session beans. Ideally I want to pre-compile and package the rules into some discrete file and get the Application Server to determine that new rules are available, and load them (skipping the compilation step).

The Drools documentation indicates this should be possible - but is splendidly vague on the mechanics. The details on the Java EE side are not an issue but the pre-compilation is providing an headache.

Following some posts I've pulled together some logic which I thought would do the trick - the following compiles a rules file and serializes it out to disk:

private void process(String outputFile, String inputFile) throws Exception {
    String drl = fileToString(inputFile);
    KieServices ks = KieServices.Factory.get();
    KieFileSystem kfs = ks.newKieFileSystem().write( "src/main/resources/r1.drl", drl );
    ks.newKieBuilder( kfs ).buildAll();
    ReleaseId releaseId = ks.getRepository().getDefaultReleaseId();
    InternalKieModule kieModule = (InternalKieModule) ks.getRepository().getKieModule( releaseId );
    byte[] jar = kieModule.getBytes();
    serialize(jar, outputFile);
    System.out.println("Class of InternalKieModule is " + kieModule.getClass());
}

The following reads it back ready to create a KieSession:

private KieContainer getKbase(KieServices ks) throws Exception {
    byte[] jar = deserialize(RULES_SER);
    Resource jarRes = ks.getResources().newByteArrayResource(jar);
    KieModule km = ks.getRepository().addKieModule(jarRes);
    releaseId = km.getReleaseId();
    KieContainer kc = ks.newKieContainer(km.getReleaseId());
    return kc;
}

Works fine BUT ... I was horrified to discover I seem to need everything but the kitchen sink to actually run it - just under 50 separate jar files - taking up 16 MB and including the Ant jar, 10 Maven jars, and the drools compiler jar even though I'm not supposed to be compiling anything, and I'm certainly not trying to use Maven or Ant! Of the 67 Jar files in the distribution nearly all seem directly linked to Maven, and very few (I'd judge from the approach below about 10 to 15) actually have anything to do with a rules runtime.

Going back to an example I built with Drools 5, several years back, I came up with the following code to reload a drl file I'd compiled separately using the Knowledge Base APIs:

private KnowledgeBase readSavedKnowledgeBase() throws Exception {
    byte[] serializedKb = (byte[]) deserialize(RULES_SER);
    ByteArrayInputStream bais = new ByteArrayInputStream(serializedKb);
    DroolsObjectInputStream ois = new DroolsObjectInputStream(bais);
    KnowledgeBase kbase = (KnowledgeBase) ois.readObject();
    ois.close();
    return kbase;
}

This works fine with 6.1. I only need 9 jars, (and I don't need the compiler jar) - and my run-time is a fairly reasonable 5MB. The down side is that Eclipse is unhappy since the KnowledgeBase class is now deprecated.

Could someone point the way to allowing me to get the same result using non-deprecated classes but without all the baggage of Maven and so on which I don't want or need to actually run some rules?


Solution

  • Some 6.x code for building:

        KieServices ks = KieServices.Factory.get();
        KieFileSystem kfs = ks.newKieFileSystem();
        FileInputStream fis = new FileInputStream( "sale/sale.drl" );
        kfs.write( "src/main/resources/sale.drl",
                       ks.getResources().newInputStreamResource( fis ) );
        KieBuilder kieBuilder = ks.newKieBuilder( kfs ).buildAll();
        Results results = kieBuilder.getResults();
        if( results.hasMessages( Message.Level.ERROR ) ){
            System.out.println( results.getMessages() );
            throw new IllegalStateException( "### errors ###" );
        }
        KieContainer kieContainer =
            ks.newKieContainer( ks.getRepository().getDefaultReleaseId() );
    
        // CEP - get the KIE related configuration container and set the EventProcessing (from default cloud) to Stream
        KieBaseConfiguration config = ks.newKieBaseConfiguration();
        config.setOption( EventProcessingOption.STREAM );
        KieBase kieBase = kieContainer.newKieBase( config );
        //      KieSession kieSession = kieContainer.newKieSession();
        KieSession kieSession = kieBase.newKieSession();
    

    You can serialize out the KieBase and back in for fast startup. I run this for various tests using the below CLASSPATH, but for execution you'll not need antlr, decisiontables, templates, and (maybe) others.

    export CLASSPATH=".:$root/drools-core-${tag}.jar:$root/kie-api-${tag}.jar:$roo/kie-internal-${tag}.jar:$root/knowledge-internal-api-${tag}.jar:$root/drools-compiler-${tag}.jar:$root/antlr-runtime-3.5.jar:$root/ecj-4.3.1.jar:$root/mvel2-2.1.9.Final.jar:$root/drools-decisiontables-${tag}.jar:$root/drools-templates-${tag}.jar:$root/protobuf-java-2.5.0.jar:$root/slf4j-api-1.7.2.jar:$root/xstream-1.4.7.jar:$SLF4J"
    

    For serialiazation and its counterpart use the usual mantra:

    ObjectOutputStream out =
            new ObjectOutputStream( new FileOutputStream( pkgPath ) );
    out.writeObject( kieBase );
    out.close();
    //---------- In another program ---------------
    ObjectInputStream in =
           new ObjectInputStream( new FileInputStream( pkgPath ) );
    @SuppressWarnings( "unchecked" )
    KieBase kieBase = (KieBase)in.readObject();
    in.close();