borgmater
borgmater

Reputation: 706

One out of two rules not firing

I'm kinda beginner to JAVA and Drools in particular, so don't ask why i'm doing this :D The task was to implement a simple system that will calculate tariffs and prices of 2 items (different colors). I made a simple class with getters and setters :

package com.sample;

public class Pen {

    private String color;
    private int quantity;
    private double tariff;
    private double subTotal;

    public String getColor(){
        return color;
    }

    public void setColor(String color){
        this.color=color;
    }

    public int getQuantity(){
        return quantity;
    }

    public void setQuantity(int quantity){
        this.quantity=quantity;
    }

    public double getTariff(){
        return tariff;
    }

    public void setTariff(double tariff){
        this.tariff=tariff;
    }

    public double getSubTotal(){
        return subTotal;
    }

    public void setSubTotal(double subTotal){
        this.subTotal=subTotal;
    }

}

Task was to read some predefined data from a CSV file and write it to another CSV (writing not yet implemented). Class that processes the input and calls the session is as follows :

public class CSVReader {
    public void CSVToJava() {
        String CSVFile = "csvExample.csv";
        BufferedReader buffer = null;
        String line = "";
        String delimiter = ",";
        List<Pen> penList = new ArrayList<Pen>();

        try {
            // load up the knowledge base
            KnowledgeBase kbase = readKnowledgeBase();
            StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
            buffer = new BufferedReader(new FileReader(CSVFile));
            while ((line = buffer.readLine()) != null) {
                String[] pens = line.split(delimiter);
                Pen redPen = new Pen();
                Pen bluePen = new Pen();
                if (pens[0].equalsIgnoreCase("Blue Pen")) {
                    bluePen.setColor(pens[0]);
                    bluePen.setQuantity(Integer.parseInt(pens[1].trim()));
                    penList.add(bluePen);
                    ksession.insert(bluePen);
                } else {
                    redPen.setColor(pens[0]);
                    redPen.setQuantity(Integer.parseInt(pens[1].trim()));
                    penList.add(redPen);
                    ksession.insert(redPen);
                }
            }
            ksession.fireAllRules();
            printPenList(penList);        
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            if (buffer != null) {
                try {
                    buffer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void printPenList(List<Pen> penListToPrint) {
        for (int i = 0; i < penListToPrint.size(); i++) {
            System.out.println(penListToPrint.get(i).getColor() + "," + penListToPrint.get(i).getQuantity() + ","
                    + penListToPrint.get(i).getTariff() + "," + penListToPrint.get(i).getSubTotal());
        }

    }

    private static KnowledgeBase readKnowledgeBase() throws Exception {

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

        kbuilder.add(ResourceFactory.newClassPathResource("com/Drools/BluePen.drl"), ResourceType.DRL);
        kbuilder.add(ResourceFactory.newClassPathResource("com/Drools/RedPen.drl"), ResourceType.DRL);

        KnowledgeBuilderErrors errors = kbuilder.getErrors();

        if (errors.size() > 0) {
            for (KnowledgeBuilderError error : errors) {
                System.err.println(error);
            }
            throw new IllegalArgumentException("Could not parse knowledge.");
        }

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());

        return kbase;
    }

}

I also have 2 separate DRL files, for each item color. Based on the calculation of the rules, i need to print out 2 additional properties, simple multiplications (subTotal). Each file has 2 rules :

import com.sample.Pen; //FIRST ONE

rule "less or equal to 10"

    when
        item : Pen(item.getColor().equalsIgnoreCase("BLUE PEN"), (item.getQuantity()) <= 10.0)
    then
        item.setTariff(3.0);
        item.setSubTotal(3.0 * ((double) item.getQuantity()));      
end

rule "more than 10"
    when
        item : Pen(item.getColor().equalsIgnoreCase("BLUE PEN"), (item.getQuantity()) > 10.0)
    then
        item.setTariff(2.5);
        item.setSubTotal(2.5 * ((double) item.getQuantity()));
end



import com.sample.Pen; //SECOND ONE


rule "less or equal to 10"

        when
            item : Pen(item.getColor().equalsIgnoreCase("RED PEN"), (item.getQuantity()) <= 10.0)
        then
            item.setTariff(3.5);
            item.setSubTotal(3.5 * ((double) item.getQuantity()));      
    end

    rule "more than 10"
        when
            item : Pen(item.getColor().equalsIgnoreCase("RED PEN"),   (item.getQuantity()) > 10.0)
        then
            item.setTariff(3.0);
            item.setSubTotal(3.0 * ((double) item.getQuantity()));
    end

Also, there is a class where i call the whole method for starting the process :

public class App {

    public static void main(String[] args) {
          CSVReader csvReader = new CSVReader();
          csvReader.CSVToJava();
    }

}

Everything looks like this packageExplorer

My main problem is that the printout looks like this -->

Red Pen,6,3.5,21.0
Blue Pen,12,0.0,0.0

Each row should consist of 4 fields, first two are calculated int he CSVReader class in while loop, while the last two (3.5 and 21.0 in the case or Red Pen) are calculated with Drools. As you can see, rules for the blue pen are not firing at all and i cannot solve the problem... If someone could help, i would be more than thankful :)

EDIT: After solving this, I edited the code with another 3 rule spread over 2 DRL files :

package com.drools //First.drl

import com.sample.Pen;

rule "Subtotal for blue pen" salience 2

    when
        item : Pen(item.getColor().equalsIgnoreCase("BLUE PEN"))
    then
        item.setTotal(item.getTotal() + item.getSubTotal());    
end

rule "Subtotal for red pen" salience 2

    when
        item : Pen(item.getColor().equalsIgnoreCase("RED PEN"))
    then
        item.setTotal(item.getTotal() + item.getSubTotal());    
end


package com.drools //Second.drl

import com.sample.Pen;

rule "Discounted total price" salience 1

    when
        item : Pen((item.getTotal()) > 100.0)
    then
        item.setTotal(0.85 * item.getTotal());
end

I have added the file classpaths inside my session also, in order to be fireable. When the fireAll method is invoked, there is no rule firing for the second drl fire, where the total amount is being multiplied by 0.85 (total is a static variable declared inside the Pen class, so I could collect the addition of suTotals from each color), eventhough salience is the lowest --> should fire last and the correct amount should be 140*0.85 = 119.

Looks like this :

Red Pen,30,3.0,90.0
Blue Pen,20,2.5,50.0
140 

Maybe someone can figure this out also, you guys have been great so far with the advice :)

Upvotes: 4

Views: 1628

Answers (3)

laune
laune

Reputation: 31300

Both files have rules

rule "less or equal to 10"
rule "more than 10"

You can't have that. Rules are identified by name; a rule with the same name as another one will silently overwrite the first one. Load order from different DRL files is undefined.

There was a long discussion whether and how this should work during Drools 5.x, but this is now water under the bridge and you'll have to stick with it. (I'm not sure whether Drools 6.x permits this. Consult the docs.)

Thanks to @Wis for confirming that Drools 6.x raises an compilation error when there are rules with identical names in a compilation, irrespective of being in the same DRL file or in different files.

Upvotes: 2

Wis
Wis

Reputation: 725

String comparison in rules conditions should not use Java equals method. Drools is using its own rule language. (But take care - 5.x syntax is somewhat more restricted than what is shown in the referenced document. And it also varies between minor versions, 5.2/3/... [laune])

Have a look at example 4.4 (I corrected the missing " in the example) :

1: rule simple_rule
2:   when
3:     Student( name == "Andy" )
4:   then
5: end

You should try this syntax for your rules (also note that your quantity is an integer and not a float) :

when
    item : Pen(color == "BLUE PEN", quantity > 10)

As a side note, your (double) casts in the action part of your rules are not mandatory. Multiplying a double and an integer in Java ends up with a double value, which is already the type of your tariff and subTotal members.

Upvotes: 1

techi
techi

Reputation: 133

I am not expert, but I would suggest to switch RED PEN and BLUE PEN to see if it is not triggering the rules after first rule. or it could be problem with CSV file reading. thx

Upvotes: 1

Related Questions