ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Give Your Business Logic a Framework with Drools
Pages: 1, 2, 3, 4, 5, 6, 7

Smarter Rules

Fresh from building the application, you demonstrate the prototype above to the business users, and they remember a few more rules that they forgot to mention earlier. One of the new rules is that we shouldn't trade stocks where the quantity is a negative number (<0). "No problem," you say, and return to your desk, secure in the knowledge that you can quickly evolve your system.

The first thing you do is to update your simulator, and add the following code to BusinessRuleTest.java:


  
  /**
   * Tests the purchase of a stock 
   * makes sure the system will not accept 
   * negative numbers.
   */
  public void testNegativeStockBuy() 
                                throws Exception{
                
    //Create a Stock with our simulated values
      StockOffer testOffer = new StockOffer();
        testOffer.setStockName("MEGACORP");
        testOffer.setStockPrice(-22);
        testOffer.setStockQuantity(1000);
                
        //Run the rules on it
        BusinessLayer
              .evaluateStockPurchase(testOffer);
                
        //Is it what we expected?
        assertTrue("NO".equals(
          testOffer.getRecommendPurchase()));
}

This tests for the new rule described by the business users. If we run this JUnit test, our new test fails, as expected. We need to add a new rule to our .drl file, as follows.


<!-- Ensure that negative prices 
                            are not accepted-->      
  <rule name="Stock Price Not Negative">
    <!-- Parameters we can pass into 
                          the business rule -->
    <parameter identifier="stockOffer">
      <class>StockOffer</class>
    </parameter>
    <!-- Conditions or 'Left Hand Side' (LHS) 
       that must be met for rule to fire -->
    <java:condition>
      stockOffer.getStockPrice() < 0
    </java:condition>
    <!-- What happens when the business rule 
                              is activated -->
    <java:consequence>
      stockOffer.setRecommendPurchase(
                                  StockOffer.NO);       
      printStock(stockOffer);
    </java:consequence>
  </rule>

This rule is similar in format to the previous one, expect that our <java:condition> is different (testing for negative numbers) and the <java:consequence> sets the recommend purchase to No. We run our unit tests/simulator again, and this time the test passes.

At this point, if you're used to procedural programming (like most Java programmers), you may be scratching your head: here we have a file containing two separate business rules, yet we haven't told the rule engine which is more important. However, our stock price (of -22) satisfies both rules (i.e., it is less than 0 and it is less than 100). Despite this, we get the correct result, even if we swap the order of the rules around. How does this work?

The extract of the console output below helps us to see what is going on. We see that both rules are firing (the [activationfired] line), and that the Recommend Buy is first set to Yes and then to No. How does Drools know to fire these rules in the correct order? If you look at the Stock Price Low Enough rule, you will see that one of the conditions is that recommendPurchase() is null. This is enough for the Drools rule engine to decide that the Stock Price Low Enough rule should be fired before the Stock Price Not Negative rule. This process is called conflict resolution.


FIRE RULES
----------
[ConditionTested: rule=Stock Price Not Negative; 
  condition=[Condition: stockOffer.getStockPrice() 
  < 0]; passed=true; tuple={[]}]
[ActivationCreated: rule=Stock Price Not Negative; 
  tuple={[]}]
[ObjectAsserted: handle=[fid:2];
   object=net.firstpartners.rp.StockOffer@16546ef]
[ActivationFired: rule=Stock Price Low Enough; 
   tuple={[]}]
[ActivationFired: rule=Stock Price Not Negative; 
   tuple={[]}]
Name:MEGACORP Price: -22 BUY:YES
Name:MEGACORP Price: -22 BUY:NO

If you're a procedural programmer, no matter how clever you think this is, you still may not trust it completely. That is why we have our unit tests/simulator: "hard" JUnit tests (using normal Java code) ensure that the rule engine makes its decisions along the lines we want it to. (And doesn't spend billions on worthless stock!) At the same time, the power and the flexibility of our rule engine allows us to quickly develop the business logic.

Later on, we will see more sophisticated forms of conflict resolution.

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow