javajbossdroolsrule-engine

How to fix a non-firing rule in drools


I am trying to learn drools. Following is the code – and the rule is not firing though fireAllRules is called .

Product class

package com.abc.main;
public class Product {
    
    private String RegionName = "South";
    private String CouponCode = "A";
    
    public String getType() {
        return RegionName;
    }
    public void setType(String regionName) {
        this.RegionName = regionName;
    }
    public String getCouponCode() {
        return CouponCode;
    }
    public void setCouponCode(String couponCode) {
        this.CouponCode = couponCode;
    }
}

Rules.drl file

package com.abc.main;

import com. abc.main.Product

rule "Offer for Diamond"
    when 
        productObject: Product(RegionName=="South")
    then
        productObject.setCouponCode("B");
    end
rule "Offer for Gold"
    when 
        productObject: Product(RegionName=="North")
    then
        productObject.setCouponCode("C");
    end

Class6 (Test class)

package com.abc.main;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;

import org.apache.commons.io.IOUtils;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.compiler.compiler.PackageBuilder;
import org.drools.core.RuleBase;
import org.drools.core.RuleBaseFactory;
import org.drools.core.WorkingMemory;

public class Class6 {


public static void main(String[] args) throws DroolsParserException,IOException 
{
        Class6 droolsTest = new Class6();
        System.out.println("Reached point 1A");
        droolsTest.executeDrools2();
}

public void executeDrools2() throws DroolsParserException, IOException 
{

System.out.println("Reached point 2J");
PackageBuilder packageBuilder = new PackageBuilder();

String ruleFile = "Rules.drl";
InputStream resourceAsStream = getClass().getResourceAsStream(ruleFile);

System.out.println("Reached point 3");
String result = IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
System.out.println("Reached point 3b - " + result);
System.out.println("Reached point 3C");

Reader reader = new InputStreamReader(resourceAsStream);
System.out.println("Reached point 4K");

packageBuilder.addPackageFromDrl(reader);
System.out.println("Reached point 5");

org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(rulesPackage);


WorkingMemory workingMemory = ruleBase.newStatefulSession();

Product product = new Product();
product.setType("South");

System.out.println("Original product " + product.getType()+ "'s coupon code is " + product.getCouponCode());

//Working Memory
workingMemory.insert(product);
workingMemory.fireAllRules();

System.out.println("The coupon code for the product " + product.getType() + " is " + product.getCouponCode());

}

}

Output

enter image description here

Though I added the fact and rule in memory, looks like the rules are not fired with “fireAllRules” call. How to make this rule firing? Is the stateful session causing this issue (with “newStatefulSession” ) ?

enter image description here


Solution

  • I think you based your code in this example.

    After reviewing both code snippets, I think the problem with your code is related to this debug related code fragment:

    System.out.println("Reached point 3");
    String result = IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
    System.out.println("Reached point 3b - " + result);
    System.out.println("Reached point 3C");
    

    Note that with that code fragment you are consuming the InputStream that points to the rules definition file obtained here:

    String ruleFile = "Rules.drl";
    InputStream resourceAsStream = getClass().getResourceAsStream(ruleFile);
    

    As a consequence, no rules are associated with your rule package when running this code fragment:

    Reader reader = new InputStreamReader(resourceAsStream);
    System.out.println("Reached point 4K");
    
    packageBuilder.addPackageFromDrl(reader);
    

    and that causes the wrong behavior you see later when trying activating then.

    Please, as stated, to solve the problem, consider comment that debug code fragment:

    package com.abc.main;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.nio.charset.StandardCharsets;
    
    import org.apache.commons.io.IOUtils;
    import org.drools.compiler.compiler.DroolsParserException;
    import org.drools.compiler.compiler.PackageBuilder;
    import org.drools.core.RuleBase;
    import org.drools.core.RuleBaseFactory;
    import org.drools.core.WorkingMemory;
    
    public class Class6 {
    
    
      public static void main(String[] args) throws DroolsParserException,IOException
      {
        Class6 droolsTest = new Class6();
        System.out.println("Reached point 1A");
        droolsTest.executeDrools2();
      }
    
      public void executeDrools2() throws DroolsParserException, IOException
      {
    
        System.out.println("Reached point 2J");
        PackageBuilder packageBuilder = new PackageBuilder();
    
        String ruleFile = "Rules.drl";
        InputStream resourceAsStream = getClass().getResourceAsStream(ruleFile);
    
        /*System.out.println("Reached point 3");
        String result = IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
        System.out.println("Reached point 3b - " + result);
        System.out.println("Reached point 3C");*/
    
        Reader reader = new InputStreamReader(resourceAsStream);
        System.out.println("Reached point 4K");
    
        packageBuilder.addPackageFromDrl(reader);
        System.out.println("Reached point 5");
    
        org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
        RuleBase ruleBase = RuleBaseFactory.newRuleBase();
        ruleBase.addPackage(rulesPackage);
    
    
        WorkingMemory workingMemory = ruleBase.newStatefulSession();
    
        Product product = new Product();
        product.setType("South");
    
        System.out.println("Original product " + product.getType()+ "'s coupon code is " + product.getCouponCode());
    
    //Working Memory
        workingMemory.insert(product);
        workingMemory.fireAllRules();
    
        System.out.println("The coupon code for the product " + product.getType() + " is " + product.getCouponCode());
    
      }
    
    }
    

    As an alternative, if you prefer to keep your debug traces, you can try building the Reader passed as argument to addPackageFromDrl in the following way:

    Reader reader = new StringReader(result);
    System.out.println("Reached point 4K");
    
    packageBuilder.addPackageFromDrl(reader);
    System.out.println("Reached point 5");
    

    Please, note the use of StringReader. The entire code fragment:

    package com.abc.main;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.Reader;
    import java.io.StringReader;
    import java.nio.charset.StandardCharsets;
    
    import org.apache.commons.io.IOUtils;
    import org.drools.compiler.compiler.DroolsParserException;
    import org.drools.compiler.compiler.PackageBuilder;
    import org.drools.core.RuleBase;
    import org.drools.core.RuleBaseFactory;
    import org.drools.core.WorkingMemory;
    
    public class Class6 {
    
    
      public static void main(String[] args) throws DroolsParserException,IOException
      {
        Class6 droolsTest = new Class6();
        System.out.println("Reached point 1A");
        droolsTest.executeDrools2();
      }
    
      public void executeDrools2() throws DroolsParserException, IOException
      {
        System.out.println("Reached point 2J");
        PackageBuilder packageBuilder = new PackageBuilder();
    
        String ruleFile = "Rules.drl";
        InputStream resourceAsStream = getClass().getResourceAsStream(ruleFile);
    
        System.out.println("Reached point 3");
        String result = IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
        System.out.println("Reached point 3b - " + result);
        System.out.println("Reached point 3C");
    
        Reader reader = new StringReader(result);
        System.out.println("Reached point 4K");
    
        packageBuilder.addPackageFromDrl(reader);
        System.out.println("Reached point 5");
    
        org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
        RuleBase ruleBase = RuleBaseFactory.newRuleBase();
        ruleBase.addPackage(rulesPackage);
    
        WorkingMemory workingMemory = ruleBase.newStatefulSession();
    
        Product product = new Product();
        product.setType("South");
    
        System.out.println("Original product " + product.getType()+ "'s coupon code is " + product.getCouponCode());
    
    //Working Memory
        workingMemory.insert(product);
        workingMemory.fireAllRules();
    
        System.out.println("The coupon code for the product " + product.getType() + " is " + product.getCouponCode());
    
      }
    
    }
    

    Which outputs:

    program output

    In any case, to make the example work you will need to change your rules file and use type instead of RegionName as well:

    package com.abc.main
    
    import com. abc.main.Product
    
    rule "Offer for Diamond"
        when
            productObject: Product(type=="South")
        then
            productObject.setCouponCode("B");
        end
    rule "Offer for Gold"
        when
            productObject: Product(type=="North")
        then
            productObject.setCouponCode("C");
        end
    

    Note that Drools uses JavaBeans conventions to access model properties, al least I always took it for sure: like in C#, the property is defined by the combination of a read (get) and write (set) optional methods but, in contrast, in Java you need to explicitly include the property name in the get and set methods. for instance, for defining the property type in Product you need to define the proper getType and/or setType methods. The name of the internal variable used to hold the property value, in your example, RegionName, doesn't actually matter. By convention in Java you will use always camel case syntax for property names, with the first letter being lowercase, not uppercase such as in C#.

    As described, the use of the stateful session is not related to the problem.

    Having said all that, as pointed out by @RoddyoftheFrozenPeas in his comment, you could let Drools trying finding the different rules defined in your classpath. You can follow his answer as reference. This Github repository provides great guidance as well for this and other stuff.

    Please note that the last references provided use the new KIE APIs instead of the old Drools ones that you are using. This excellent blog post provides a great introduction about the change.

    If, as you mentioned, you are learning Drools, the aforementioned blog contains posts of very good quality that could be of help. Drools itself provides an excellent documentation and additional help resources too.