天天看點

[Drools]JAVA規則引擎2 -- Drools執行個體

上一篇文章 http://blog.csdn.net/quzishen/archive/2011/01/25/6163012.aspx 描述了一些常用的drools的文法标簽和一個模拟執行個體即發送積分的場景,這一片優化了一下代碼,在此貼一下,希望有這方面使用經驗的朋友多多交流溝通,指正不足。

通常而言,習慣上我們将規則放到檔案系統中,比如以drl結尾的規則檔案,現在我們要擴充一下,使其放到資料庫中,以供多台伺服器同時使用,同時依然保留檔案系統的支援。

先看下一個接口:

[java]  view plain  copy

  1. public interface PointRuleEngine {  
  2.     public void initEngine();  
  3.     public void refreshEnginRule();  
  4.     public void executeRuleEngine(final PointDomain pointDomain);  
  5. }  

實作過程沒有任何難度,兩種方式封裝過程隻在于讀取規則的方式不同,代碼很簡單:

[java]  view plain  copy

  1. package com.drools.demo.point;  
  2. import <a href="http://lib.csdn.net/base/17" class='replace_word' title="Java EE知識庫" target='_blank' style='color:#df3434; font-weight:bold;'>Java</a>.io.File;  
  3. import java.io.FileNotFoundException;  
  4. import java.io.FileReader;  
  5. import java.io.Reader;  
  6. import java.io.StringReader;  
  7. import java.util.ArrayList;  
  8. import java.util.List;  
  9. import org.drools.RuleBase;  
  10. import org.drools.StatefulSession;  
  11. import org.drools.compiler.PackageBuilder;  
  12. import org.drools.spi.Activation;  
  13. public class PointRuleEngineImpl implements PointRuleEngine {  
  14.     // ~~~ instance filed begin  
  15.     private RuleBase ruleBase;  
  16.     // ~~~ instance filed end  
  17.     public void initEngine() {  
  18.         // 設定時間格式  
  19.         System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");  
  20.         try {  
  21.             synchronized (this) {  
  22.                 ruleBase = RuleBaseFacatory.getRuleBase();  
  23.                 // 優先從DB加載規則,如果沒有加載到或者加載錯誤,則從檔案系統加載  
  24.                 PackageBuilder backageBuilder = getPackBuilderFromDrlDB();  
  25.                 backageBuilder = null == backageBuilder ? getPackageBuilderFromDrlFile()  
  26.                         : backageBuilder;  
  27.                 ruleBase.addPackages(backageBuilder.getPackages());  
  28.             }  
  29.         } catch (Exception e) {  
  30.             e.printStackTrace();  
  31.         }  
  32.     }  
  33.     public void refreshEnginRule() {  
  34.         ruleBase = RuleBaseFacatory.getRuleBase();  
  35.         synchronized (ruleBase) {  
  36.             // 删除所有的添加的Package  
  37.             org.drools.rule.Package[] packages = ruleBase.getPackages();  
  38.             for (org.drools.rule.Package pg : packages) {  
  39.                 ruleBase.removePackage(pg.getName());  
  40.             }  
  41.             // 重新初始化規則引擎  
  42.             initEngine();  
  43.         }  
  44.     }  
  45.     public void executeRuleEngine(final PointDomain pointDomain) {  
  46.         if (null == ruleBase.getPackages() || 0 == ruleBase.getPackages().length) {  
  47.             return;  
  48.         }  
  49.         StatefulSession statefulSession = ruleBase.newStatefulSession();  
  50.         statefulSession.insert(pointDomain);  
  51.         // fire  
  52.         statefulSession.fireAllRules(new org.drools.spi.AgendaFilter() {  
  53.             public boolean accept(Activation activation) {  
  54.                 return !activation.getRule().getName().contains("_test");  
  55.             }  
  56.         });  
  57.         statefulSession.dispose();  
  58.     }  
  59.     private PackageBuilder getPackageBuilderFromDrlFile() {  
  60.         // 裝載規則檔案  
  61.         List<Reader> readers;  
  62.         try {  
  63.             readers = buildReadersFromDrlFile();  
  64.             // 裝載PackageBuilder  
  65.             return buildPackageBuilder(readers);  
  66.         } catch (FileNotFoundException e) {  
  67.             e.printStackTrace();  
  68.             return null;  
  69.         } catch (Exception e) {  
  70.             e.printStackTrace();  
  71.             return null;  
  72.         }  
  73.     }  
  74.     private PackageBuilder getPackBuilderFromDrlDB() {  
  75.         // 裝載規則  
  76.         List<Reader> readers = buildReadersFromDrlDB();  
  77.         // 裝載PackageBuilder  
  78.         try {  
  79.             return buildPackageBuilder(readers);  
  80.         } catch (Exception e) {  
  81.             e.printStackTrace();  
  82.             return null;  
  83.         }  
  84.     }  
  85.     private List<Reader> buildReadersFromDrlDB() {  
  86.         List<Reader> readers = new ArrayList<Reader>();  
  87.         // 擷取腳本  
  88.         List<DroolsRuleDomain> drlRuleDomains = getRuleFromDB();  
  89.         if (null == drlRuleDomains) {  
  90.             return readers;  
  91.         }  
  92.         for (DroolsRuleDomain droolsRuleDomain : drlRuleDomains) {  
  93.             String ruleContext = droolsRuleDomain.getRuleContext();  
  94.             Reader br = new StringReader(ruleContext);  
  95.             readers.add(br);  
  96.         }  
  97.         return readers;  
  98.     }  
  99.     private PackageBuilder buildPackageBuilder(List<Reader> readers)  
  100.             throws Exception {  
  101.         if (null == readers || 0 == readers.size()) {  
  102.             return null;  
  103.         }  
  104.         PackageBuilder backageBuilder = new PackageBuilder();  
  105.         for (Reader r : readers) {  
  106.             backageBuilder.addPackageFromDrl(r);  
  107.         }  
  108.         // 檢查腳本是否有問題  
  109.         if (backageBuilder.hasErrors()) {  
  110.             throw new Exception(backageBuilder.getErrors().toString());  
  111.         }  
  112.         return backageBuilder;  
  113.     }  
  114.     private List<Reader> buildReadersFromDrlFile() throws FileNotFoundException {  
  115.         // 擷取腳本檔案  
  116.         List<String> drlFilePath = getRuleDrlFile();  
  117.         // 裝載腳本檔案  
  118.         return readRuleFromDrlFile(drlFilePath);  
  119.     }  
  120.     private List<Reader> readRuleFromDrlFile(List<String> drlFilePath)  
  121.             throws FileNotFoundException {  
  122.         if (null == drlFilePath || 0 == drlFilePath.size()) {  
  123.             return null;  
  124.         }  
  125.         List<Reader> readers = new ArrayList<Reader>();  
  126.         for (String ruleFilePath : drlFilePath) {  
  127.             readers.add(new FileReader(new File(ruleFilePath)));  
  128.         }  
  129.         return readers;  
  130.     }  
  131.     private List<DroolsRuleDomain> getRuleFromDB() {  
  132.         // 測試代碼  
  133.         List<DroolsRuleDomain> droolsRuleDomains = new ArrayList<DroolsRuleDomain>();  
  134.         DroolsRuleDomain d1 = new DroolsRuleDomain();  
  135.         d1.setId(1);  
  136.         d1.setRuleContext("package com.drools.demo.point" + "/n" +   
  137.                 "import com.drools.demo.point.PointDomain;" + "/n" +   
  138.                 "rule birthdayPoint" + "/n" +   
  139.                 "// 過生日,則加10分,并且将當月交易比數翻倍後再計算積分" + "/n" +   
  140.                 "salience 100" + "/n" +   
  141.                 "lock-on-active true" + "/n" +   
  142.                 "when" + "/n" +   
  143.                 "$pointDomain : PointDomain(birthDay == true)" + "/n" +   
  144.                 "then" + "/n" +   
  145.                 "$pointDomain.setPoint($pointDomain.getPoint()+10);" + "/n" +   
  146.                 "$pointDomain.recordPointLog($pointDomain.getUserName(),/"birthdayPoint/");" + "/n" +   
  147.                 "end");  
  148.         d1.setRuleName("testRule");  
  149.         d1.setVersion(1);  
  150.         droolsRuleDomains.add(d1);  
  151.         return droolsRuleDomains;  
  152.     }  
  153.     private List<String> getRuleDrlFile() {  
  154.         List<String> drlFilePath = new ArrayList<String>();  
  155.         drlFilePath  
  156.                 .add("D:/workspace2/DroolsDemo/src/com/drools/demo/point/addpoint.drl");  
  157.         drlFilePath  
  158.                 .add("D:/workspace2/DroolsDemo/src/com/drools/demo/point/subpoint.drl");  
  159.         return drlFilePath;  
  160.     }  
  161. }  

其中的getRuleFromDB() 和 getRuleDrlFile() 兩個方法即可以重寫以接入個人系統,現在其中編寫的是測試代碼。

其他的檔案與上篇文章相同:

RuleBaseFacatory

[java]  view plain  copy

  1. package com.drools.demo.point;  
  2. import org.drools.RuleBase;  
  3. import org.drools.RuleBaseFactory;  
  4. public class RuleBaseFacatory {  
  5.     private static RuleBase ruleBase;  
  6.     public static RuleBase getRuleBase(){  
  7.         return null != ruleBase ? ruleBase : RuleBaseFactory.newRuleBase();  
  8.     }  
  9. }  

DroolsRuleDomain

[java]  view plain  copy

  1. package com.drools.demo.point;  
  2. public class DroolsRuleDomain {  
  3.     private long id;  
  4.     private String ruleName;  
  5.     private String ruleContext;  
  6.     private int version;  
  7.     private int status;  
  8.     public long getId() {  
  9.         return id;  
  10.     }  
  11.     public void setId(long id) {  
  12.         this.id = id;  
  13.     }  
  14.     public String getRuleName() {  
  15.         return ruleName;  
  16.     }  
  17.     public void setRuleName(String ruleName) {  
  18.         this.ruleName = ruleName;  
  19.     }  
  20.     public String getRuleContext() {  
  21.         return ruleContext;  
  22.     }  
  23.     public void setRuleContext(String ruleContext) {  
  24.         this.ruleContext = ruleContext;  
  25.     }  
  26.     public int getVersion() {  
  27.         return version;  
  28.     }  
  29.     public void setVersion(int version) {  
  30.         this.version = version;  
  31.     }  
  32.     public int getStatus() {  
  33.         return status;  
  34.     }  
  35.     public void setStatus(int status) {  
  36.         this.status = status;  
  37.     }  
  38. }  

PointDomain

[java]  view plain  copy

  1. package com.drools.demo.point;  
  2. public class PointDomain {  
  3.     // 使用者名  
  4.     private String userName;  
  5.     // 是否當日生日  
  6.     private boolean birthDay;  
  7.     // 增加積分數目  
  8.     private long point;  
  9.     // 當月購物次數  
  10.     private int buyNums;  
  11.     // 當月退貨次數  
  12.     private int backNums;  
  13.     // 當月購物總金額  
  14.     private double buyMoney;  
  15.     // 當月退貨總金額  
  16.     private double backMondy;  
  17.     // 當月信用卡還款次數  
  18.     private int billThisMonth;  
  19.     public void recordPointLog(String userName, String type){  
  20.         System.out.println("增加對"+userName+"的類型為"+type+"的積分操作記錄.");  
  21.     }  
  22.     public String getUserName() {  
  23.         return userName;  
  24.     }  
  25.     public void setUserName(String userName) {  
  26.         this.userName = userName;  
  27.     }  
  28.     public boolean isBirthDay() {  
  29.         return birthDay;  
  30.     }  
  31.     public void setBirthDay(boolean birthDay) {  
  32.         this.birthDay = birthDay;  
  33.     }  
  34.     public long getPoint() {  
  35.         return point;  
  36.     }  
  37.     public void setPoint(long point) {  
  38.         this.point = point;  
  39.     }  
  40.     public int getBuyNums() {  
  41.         return buyNums;  
  42.     }  
  43.     public void setBuyNums(int buyNums) {  
  44.         this.buyNums = buyNums;  
  45.     }  
  46.     public int getBackNums() {  
  47.         return backNums;  
  48.     }  
  49.     public void setBackNums(int backNums) {  
  50.         this.backNums = backNums;  
  51.     }  
  52.     public double getBuyMoney() {  
  53.         return buyMoney;  
  54.     }  
  55.     public void setBuyMoney(double buyMoney) {  
  56.         this.buyMoney = buyMoney;  
  57.     }  
  58.     public double getBackMondy() {  
  59.         return backMondy;  
  60.     }  
  61.     public void setBackMondy(double backMondy) {  
  62.         this.backMondy = backMondy;  
  63.     }  
  64.     public int getBillThisMonth() {  
  65.         return billThisMonth;  
  66.     }  
  67.     public void setBillThisMonth(int billThisMonth) {  
  68.         this.billThisMonth = billThisMonth;  
  69.     }  
  70. }  

addpoint.drl

[java]  view plain  copy

  1. package com.drools.demo.point  
  2. import com.drools.demo.point.PointDomain;  
  3. rule birthdayPoint  
  4.     // 過生日,則加10分,并且将當月交易比數翻倍後再計算積分  
  5.     salience 100  
  6.     lock-on-active true  
  7.     when  
  8.         $pointDomain : PointDomain(birthDay == true)  
  9.     then  
  10.         $pointDomain.setPoint($pointDomain.getPoint()+10);  
  11.         $pointDomain.setBuyNums($pointDomain.getBuyNums()*2);  
  12.         $pointDomain.setBuyMoney($pointDomain.getBuyMoney()*2);  
  13.         $pointDomain.setBillThisMonth($pointDomain.getBillThisMonth()*2);  
  14.         $pointDomain.recordPointLog($pointDomain.getUserName(),"birthdayPoint");  
  15. end  
  16. rule billThisMonthPoint  
  17.     // 2011-01-08 - 2011-08-08每月信用卡還款3次以上,每滿3筆贈送30分  
  18.     salience 99  
  19.     lock-on-active true  
  20.     date-effective "2011-01-08 23:59:59"  
  21.     date-expires "2011-08-08 23:59:59"  
  22.     when  
  23.         $pointDomain : PointDomain(billThisMonth >= 3)  
  24.     then  
  25.         $pointDomain.setPoint($pointDomain.getPoint()+$pointDomain.getBillThisMonth()/3*30);  
  26.         $pointDomain.recordPointLog($pointDomain.getUserName(),"billThisMonthPoint");  
  27. end  
  28. rule buyMoneyPoint  
  29.     // 當月購物總金額100以上,每100元贈送10分  
  30.     salience 98  
  31.     lock-on-active true  
  32.     when  
  33.         $pointDomain : PointDomain(buyMoney >= 100)  
  34.     then  
  35.         $pointDomain.setPoint($pointDomain.getPoint()+ (int)$pointDomain.getBuyMoney()/100 * 10);  
  36.         $pointDomain.recordPointLog($pointDomain.getUserName(),"buyMoneyPoint");  
  37. end  
  38. rule buyNumsPoint  
  39.     // 當月購物次數5次以上,每五次贈送50分  
  40.     salience 97  
  41.     lock-on-active true  
  42.     when  
  43.         $pointDomain : PointDomain(buyNums >= 5)  
  44.     then  
  45.         $pointDomain.setPoint($pointDomain.getPoint()+$pointDomain.getBuyNums()/5 * 50);  
  46.         $pointDomain.recordPointLog($pointDomain.getUserName(),"buyNumsPoint");  
  47. end  
  48. rule allFitPoint  
  49.     // 特别的,如果全部滿足了要求,則額外獎勵100分  
  50.     salience 96  
  51.     lock-on-active true  
  52.     when  
  53.         $pointDomain:PointDomain(buyNums >= 5 && billThisMonth >= 3 && buyMoney >= 100)  
  54.     then  
  55.         $pointDomain.setPoint($pointDomain.getPoint()+ 100);  
  56.         $pointDomain.recordPointLog($pointDomain.getUserName(),"allFitPoint");  
  57. end  

subpoint.drl 與上一篇相同,請參見上一篇,此處省略篇幅略

測試代碼

Test

[java]  view plain  copy

  1. package com.drools.demo.point;  
  2. import java.io.BufferedReader;  
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.InputStreamReader;  
  6. public class Test {  
  7.     public static void main(String[] args) throws IOException {  
  8.         PointRuleEngine pointRuleEngine = new PointRuleEngineImpl();  
  9.         boolean isStart = false;  
  10.         while(true){  
  11.             InputStream is = System.in;  
  12.             BufferedReader br = new BufferedReader(new InputStreamReader(is));  
  13.             String input = br.readLine();  
  14.             if (null != input && "s".equals(input)){  
  15.                 System.out.println("初始化規則引擎...");  
  16.                 pointRuleEngine.initEngine();  
  17.                 isStart = true;  
  18.                 System.out.println("初始化規則引擎結束.");  
  19.             } else if ("e".equals(input)){  
  20.                 if (!isStart) {  
  21.                     System.out.println("需要輸入s啟動");  
  22.                 } else {  
  23.                     final PointDomain pointDomain = new PointDomain();  
  24.                     pointDomain.setUserName("hello kity");  
  25.                     pointDomain.setBackMondy(100d);  
  26.                     pointDomain.setBuyMoney(500d);  
  27.                     pointDomain.setBackNums(1);  
  28.                     pointDomain.setBuyNums(5);  
  29.                     pointDomain.setBillThisMonth(5);  
  30.                     pointDomain.setBirthDay(true);  
  31.                     pointDomain.setPoint(0l);  
  32.                     pointRuleEngine.executeRuleEngine(pointDomain);  
  33.                     System.out.println("執行完畢BillThisMonth:"+pointDomain.getBillThisMonth());  
  34.                     System.out.println("執行完畢BuyMoney:"+pointDomain.getBuyMoney());  
  35.                     System.out.println("執行完畢BuyNums:"+pointDomain.getBuyNums());  
  36.                     System.out.println("執行完畢規則引擎決定發送積分:"+pointDomain.getPoint());  
  37.                 }  
  38.             } else if ("r".equals(input)){  
  39.                 System.out.println("重新整理規則檔案...");  
  40.                 pointRuleEngine.refreshEnginRule();  
  41.                 isStart = true;  
  42.                 System.out.println("重新整理規則檔案結束.");  
  43.             } else if ("q".equals(input)) {  
  44.                 System.exit(0);  
  45.             }  
  46.         }  
  47.     }  
  48. }