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

Conflict Resolution

Now the folks on the business side are really impressed and are starting to think through the possible options. They've come across a problem with stocks of XYZ Corp and have decided to implement a new rule: Only buy stocks of XYZ Corp if they are less than 10 Euro.

As before, you add the test to our simulator and include the new business rule in our rules file, as per the following listings. First, we add a new method to BusinessRuleTest.java:


 /**
 * Makes sure the system will buy stocks 
 * of XYZ corp only if it really cheap
 */
public void testXYZStockBuy() throws Exception{
        
  //Create a Stock with our simulated values
  StockOffer testOfferLow = new StockOffer();
  StockOffer testOfferHigh = new StockOffer();
                
  testOfferLow.setStockName("XYZ");
  testOfferLow.setStockPrice(9);
  testOfferLow.setStockQuantity(1000);
                
  testOfferHigh.setStockName("XYZ");
  testOfferHigh.setStockPrice(11);
  testOfferHigh.setStockQuantity(1000);
                
  //Run the rules on it and test
  BusinessLayer.evaluateStockPurchase(
    testOfferLow);
  assertTrue("YES".equals(
    testOfferLow.getRecommendPurchase()));
                
  BusinessLayer.evaluateStockPurchase(
    testOfferHigh);
  assertTrue("NO".equals(
    testOfferHigh.getRecommendPurchase()));             
}

Next, we need a new <rule> in BusinessRules.drl:


  <rule name="XYZCorp" salience="-1">
   <!-- Parameters we pass to rule -->
   <parameter identifier="stockOffer">
     <class>StockOffer</class>
   </parameter>
    
   <java:condition>
     stockOffer.getStockName().equals("XYZ")
   </java:condition> 
   <java:condition>
     stockOffer.getRecommendPurchase() == null
   </java:condition>
   <java:condition>
     stockOffer.getStockPrice() > 10
   </java:condition>
        
   <!-- What happens when the business 
                                rule is activated -->
   <java:consequence> 
     stockOffer.setRecommendPurchase(
       StockOffer.NO);  
     printStock(stockOffer);
   </java:consequence>
  </rule>

Note that in the business rules file, after the rule name, we set our salience to -1 (i.e., the lowest priority of all of the rules we have specified so far). Most of the rules in our system conflict, meaning Drools must make some decision on the order in which to fire rules, given that the conditions for all of the rules will be met. The default way of deciding is:

  • Salience: A value we assign, as per the above listing.
  • Recency: How many times we have used a rule.
  • Complexity: Specific rules with more complicated values fire first.
  • LoadOrder: The order in which rules are loaded.

If we did not specify the saliency of our rule in this example, what would happen is:

  • The XYZ Corp rule ("Don't buy XYZ if the price is more than 10 Euro") would fire first (the status of the Recommend Buy flag would be set to No).
  • Then the more general rule ("Buy all stock under 100") fires, setting the Recommended Buy flag to yes.

This would give a result that we don't want. However, since our example does set the saliency factor, the test and our business rules work as expected.

While most of the time, writing clear rules and setting the saliency will give enough information to Drools for it to choose the proper order in which to fire rules, sometimes we want to change the entire manner in which rule conflicts are resolved. An example of how to change this is given below, where we tell the rule engine to fire the simplest rules first. A word of warning: be careful when changing conflict resolution, as it can fundamentally change the behavior of the rule engine--a lot of problems can be solved first with clear and well-written rules.


  //Generate our list of conflict resolvers
  ConflictResolver[] conflictResolvers = 
    new ConflictResolver[] {
      SalienceConflictResolver.getInstance(),
      RecencyConflictResolver.getInstance(),
        SimplicityConflictResolver.getInstance(),
        LoadOrderConflictResolver.getInstance()
    };
                
  //Wrap this up into one composite resolver
  CompositeConflictResolver resolver = 
    new CompositeConflictResolver(
      conflictResolvers);
                        
  //Specify this resolver when we load the rules
  businessRules = RuleBaseLoader.loadFromUrl(
    BusinessLayer.class.getResource( 
      BUSINESS_RULE_FILE),resolver);

For our simple application, driven by JUnit tests, we don't need to alter the way the Drools resolves rule conflicts. It is useful to know how conflict resolution works, especially when your application grows to meet more complex and demanding requirements.

Conclusion

This article demonstrated a problem that most programmers have had to face: how to put some order on the complexity of business logic. We demonstrated a simple application using Drools as a solution and introduced the notion of rule-based programming, including how these rules are resolved at runtime. Later on, a follow-up article will take these foundations and show how to use them in an enterprise Java application.

Resources

Paul Browne , based in Dublin, Ireland, has been consulting in enterprise Java with FirstPartners.net for almost seven years.


Return to ONJava.com.