borgmater borgmater - 6 months ago 31
Java Question

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
suTotal
s 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 :)

Answer

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 IllegalStateException when there are rules with identical names in a compilation, irrespective of being in the same DRL file or in different files.

Comments