Search code examples
javadroolskie

Drools: Rules involving a Map from String to Object


I need help writing a Drools rule. I have two classes named Context and CreditReport.

Context is inserted as a fact into the knowledge session before the rules are fired.

I need to write a rule that prints 'Excellent' on the console when the Credit Score is more than 800.

Ideally, I'd insert CreditReport directly into the session, but unfortunately I do not have that option.

The rule that I've written doesn't look good as:

  1. The then part has an if statement
  2. I am type-casting Object to CreditReport

Thanks for your help!

// Context.java

public class Context {
    private Map<String, Object> data = Maps.newHashMap();

    public <T> T getData(final String key, final Class<T> clazz) {
        return clazz.cast(data.get(key));
    }

    public void putData(final String key, final Object value) {
        this.data.put(key, value);
    }
}

// CreditReport.java

public class CreditReport {
    private final String name;
    private final int creditScore;

    public String getName() {
        return this.name;
    }

    public int getCreditScore() {
        return this.creditScore;
    }

}

// Main method

context.put("creditReport", new CreditReport("John", 810));
session.insert(Arrays.asList(context));
session.fireAllRules();

// Rule

rule "Excellent_Score"
when Context($creditReportObject : getData("creditReport"))
then
    final CreditReport creditReport = (CreditReport) $creditReportObject;
    if (creditReport.getCreditScore() >= 800) {
       System.out.println("Excellent");
    }
end 

Solution

  • What makes you insert a List<Context> containing a single Context object? The Java code should do

    context.put("creditReport", new CreditReport("John", 810));
    session.insert( context );
    session.fireAllRules();
    

    The rule can now be written as

    rule "Excellent_Score"
    when 
       Context( $creditReportObject : getData("creditReport") )
       CreditReport( creditScore > 800 ) from $creditReportObject 
    then
       System.out.println("Excellent");
    end
    

    You could, of course, get and insert the CreditReport from Context. - I suspect it's more convoluted that what you've shown, but "I do not have that option" is a code smell anyway.

    Edit A single rule for more than one reason for printing "excellent" could be written like the one below, although this isn't much better that two rules, considering that you can wrap the RHS into a method or DRL function.

    rule "Excellent_Score_2"
    when 
       Context( $creditReport : getData("creditReport"),
                $account: getData("account") )
       ( CreditReport( creditScore > 800 ) from $creditReport
         or
         Account( balance >= 5000 ) from $account )
    then
       System.out.println("Excellent");
    end