Question

I have been looking at how to simplify some of my rules which are manually written in DRL, becoming difficult to maintain.

Searching through google resulted in "decision tables is the best way to go forawad".

But unfortunately our facts are very complex, So at moment drools spreadsheetconverter, can not handle so much complexity on facts,

So the first question is how do developers normally deal with handling very complex facts in the drools knowledge base?

For example We have facts like

Person->List<Cars>->List<Insurances>->Each insurance Has List<History>

Now i have to write a rule say Person Has bad history for his Insurance claim. Then i find very diffcult to put it in speadsheet, where as its easier to manually write this rule on the drl file.

Thanks for the help. Any help on the above example would be very good too .

Was it helpful?

Solution

For complex rules like this, we use Drools Templates. You write a rule template with parameter expansions for fields to populate and you have much more flexibility in where the actual values come from to populate the skeleton rule.

This capability is built into Drools Guvnor, but writing the complex rule templates through the GUI is somewhat tedious. I have also written standalone Java to populate template drl files from lists of values pulled from properties files, and recently developed a SmartGWT web app that allows the user to populate rule values and generate DRL.

Edit: Adding sample program. DroolsTemplateBuilder creates a list of TestType objects, which have fields that map to the template keys in Test.drl. The generated DRL is printed and also compiled to a pkg which is written out to a file called Test.pkg.

Libraries: antlr-3.3.jar, antlr-runtime-3.3.jar, drools-compiler-5.2.0.Final.jar, drools-core-5.2.0.Final.jar, drools-templates-5.2.0.Final.jar, ecj-4.2.jar, knowledge-api-5.2.0.Final.jar, mvel2-2.1.0.drools.jar (these may not all be necessary).

Note: This example uses 5.2.0 libraries and some functionality may be different in newer releases. build.xml should make it clear how to structure your project.

DroolsTemplateBuilder.java:

package some.test.pkg;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;

import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.common.DroolsObjectOutputStream;
import org.drools.definitions.impl.KnowledgePackageImp;
import org.drools.io.ResourceFactory;
import org.drools.template.ObjectDataCompiler;

public class DroolsTemplateBuilder {

    private String filePath;
    private String drl;

    public static void main(String[] args) {
        DroolsTemplateBuilder test = new DroolsTemplateBuilder();
        test.filePath = args[0] + File.separator + "Test.drl";
        test.runTest();
    }

    public void runTest() {
        buildPackage();
        writeRulePackageToFile();
    }

    public void buildPackage() {

        Collection<Object> templateList = new ArrayList<Object>();
        templateList.add(new TestType(1, "John", "Manager"));
        templateList.add(new TestType(2, "Peter", "CEO"));
        templateList.add(new TestType(3, "Kate", "Engineer"));

        try {
            ObjectDataCompiler converter = new ObjectDataCompiler();
            InputStream templateStream = new FileInputStream(filePath);

            String myDrl = inputStreamToString(templateStream, 200);
            // I use this ##### replacement instead of just a newline in the
            // template
            // because of windows/linux issues with newline and carriage return.
            // Drools template
            // builder, at least in 5.2.0, was very picky about the template
            // structure, including
            // where newlines are expected.
            myDrl = myDrl.replaceAll("#####", "\n");

            InputStream tempStream = new ByteArrayInputStream(myDrl.getBytes());

            drl = converter.compile(templateList, tempStream);
            System.out.println(drl);
        } catch (Exception e) {
            System.out.println("Exception: " + e.getMessage());
        }

    }

    public void writeRulePackageToFile() {
        try {
            KnowledgeBuilder kBuilder = KnowledgeBuilderFactory
                    .newKnowledgeBuilder();

            Reader rdr = new StringReader(drl);

            kBuilder.add(ResourceFactory.newReaderResource(rdr),
                    ResourceType.DRL);

            if (kBuilder.hasErrors()) {
                System.out.println("Drools blew up on");
                for (KnowledgeBuilderError err : kBuilder.getErrors()) {
                    System.out.println(err.getMessage());
                }
            } else {
                String outFile = filePath.replaceFirst("\\.drl", ".pkg");
                OutputStream os = new FileOutputStream(outFile);
                ObjectOutputStream oos = new DroolsObjectOutputStream(os);
                KnowledgePackageImp kPackage = (KnowledgePackageImp) kBuilder
                        .getKnowledgePackages().iterator().next();
                oos.writeObject(kPackage);
                oos.close();
            }
        } catch (Exception e) {
            System.out.println("Exception " + e.getMessage());
        }
    }

    public String inputStreamToString(final InputStream is, final int bufferSize) {
        final char[] buffer = new char[bufferSize];
        final StringBuilder out = new StringBuilder();
        try {
            final Reader in = new InputStreamReader(is, "UTF-8");
            try {
                for (;;) {
                    int rsz = in.read(buffer, 0, buffer.length);
                    if (rsz < 0)
                        break;
                    out.append(buffer, 0, rsz);
                }
            } finally {
                in.close();
            }
        } catch (Exception ex) {
            System.out.println("Something went wrong: " + ex.getMessage());
        }
        return out.toString();
    }

}

Test.drl:

template header
id
name
title
#####
package some.test.pkg;

template "sampleTemplate"

rule "id filter_@{row.rowNumber}"
no-loop true
dialect "java"
when
    $t : TestType(id=="@{id}")
then
    System.out.println("Doing something special...");
end

end template

template "anotherSample"

rule "another rule_@{row.rowNumber}"
no-loop true
dialect "java"
when
    $t : TestType((name=="@{name}") || (title=="@{title}"))
then
    System.out.println("Doing something else...");
end

end template

TestType.java:

package some.test.pkg;

public class TestType {

    private int id;
    private String name;
    private String title;

    public TestType(int id, String name, String title) {
        this.id = id;
        this.name = name;
        this.title = title;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

build.xml:

<project name="TemplateTest" basedir="." default="jar">

    <property name="src.dir" value="src" />

    <property name="build.dir" value="build" />
    <property name="drl.dir" value="${basedir}/drl" />
    <property name="classes.dir" value="${build.dir}/classes" />
    <property name="jar.dir" value="${build.dir}/jar" />
    <property name="lib.dir" value="${basedir}/lib" />

    <path id="compile.classpath">
        <fileset dir="${lib.dir}" includes="*.jar" />
    </path>

    <path id="run.classpath">
        <fileset dir="${jar.dir}" includes="*.jar" />
        <fileset dir="${lib.dir}" includes="*.jar" />
    </path>

    <target name="clean">
        <delete dir="${classes.dir}" />
        <delete dir="${jar.dir}" />
    </target>

    <target name="compile" depends="clean">
        <mkdir dir="${classes.dir}" />
        <mkdir dir="${jar.dir}" />
        <javac includeantruntime="false" srcdir="${src.dir}" classpathref="compile.classpath" destdir="${classes.dir}" />
    </target>

    <target name="jar" depends="compile">
        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
        </jar>
    </target>

    <target name="run" depends="jar" description="run">
        <java classpathref="run.classpath" classname="some.test.pkg.DroolsTemplateBuilder" fork="true">
            <arg value="${drl.dir}" />
        </java>
    </target>

</project>

OTHER TIPS

We also use Templates and our facts (and resulting rules) are quite complex. The values in the template table are used in the rules for method calls, setting rule "options," timer values, etc.

Templates help when the rule parameters lend themselves to a tabular format. If access control is of concern you may end up needing multiple templates with the same logic, just different values. (Easy to do in guvnor by just copying the 1st template).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top