[Drools]JAVA規則引擎 -- Drools 2

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

通常而言,習慣上我們將規則放到文件系統中,比如以drl結尾的規則文件,現在我們要擴充一下,使其放到數據庫中,以供多臺服務器同時使用,同時依然保留文件系統的支持。

先看下一個接口:

 

  1. /** 
  2.  * 規則接口 
  3.  * @author quzishen 
  4.  */  
  5. public interface PointRuleEngine {  
  6.       
  7.     /** 
  8.      * 初始化規則引擎 
  9.      */  
  10.     public void initEngine();  
  11.       
  12.     /** 
  13.      * 刷新規則引擎中的規則 
  14.      */  
  15.     public void refreshEnginRule();  
  16.       
  17.     /** 
  18.      * 執行規則引擎 
  19.      * @param pointDomain 積分Fact 
  20.      */  
  21.     public void executeRuleEngine(final PointDomain pointDomain);  
  22. }  

 

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

 

  1. package com.drools.demo.point;  
  2.   
  3. 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;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.FileReader;  
  6. import java.io.Reader;  
  7. import java.io.StringReader;  
  8. import java.util.ArrayList;  
  9. import java.util.List;  
  10. import org.drools.RuleBase;  
  11. import org.drools.StatefulSession;  
  12. import org.drools.compiler.PackageBuilder;  
  13. import org.drools.spi.Activation;  
  14.   
  15. /** 
  16.  * 規則接口實現類 
  17.  *  
  18.  * @author quzishen 
  19.  */  
  20. public class PointRuleEngineImpl implements PointRuleEngine {  
  21.     // ~~~ instance filed begin  
  22.     /** RuleBase */  
  23.     private RuleBase ruleBase;  
  24.     // ~~~ instance filed end  
  25.   
  26.     /* 
  27.      * (non-Javadoc) 
  28.      * @see com.drools.demo.point.PointRuleEngine#initEngine() 
  29.      */  
  30.     public void initEngine() {  
  31.         // 設置時間格式  
  32.         System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");  
  33.         try {  
  34.             synchronized (this) {  
  35.                 ruleBase = RuleBaseFacatory.getRuleBase();  
  36.                 // 優先從DB加載規則,如果沒有加載到或者加載錯誤,則從文件系統加載  
  37.                 PackageBuilder backageBuilder = getPackBuilderFromDrlDB();  
  38.                 backageBuilder = null == backageBuilder ? getPackageBuilderFromDrlFile()  
  39.                         : backageBuilder;  
  40.   
  41.                 ruleBase.addPackages(backageBuilder.getPackages());  
  42.             }  
  43.         } catch (Exception e) {  
  44.             e.printStackTrace();  
  45.         }  
  46.     }  
  47.   
  48.     /* 
  49.      * (non-Javadoc) 
  50.      * @see com.drools.demo.point.PointRuleEngine#refreshEnginRule() 
  51.      */  
  52.     public void refreshEnginRule() {  
  53.         ruleBase = RuleBaseFacatory.getRuleBase();  
  54.         synchronized (ruleBase) {  
  55.             // 刪除所有的添加的Package  
  56.             org.drools.rule.Package[] packages = ruleBase.getPackages();  
  57.             for (org.drools.rule.Package pg : packages) {  
  58.                 ruleBase.removePackage(pg.getName());  
  59.             }  
  60.               
  61.             // 重新初始化規則引擎  
  62.             initEngine();  
  63.         }  
  64.     }  
  65.       
  66.     /* 
  67.      * (non-Javadoc) 
  68.      * @see com.drools.demo.point.PointRuleEngine#executeRuleEngine(com.drools.demo.point.PointDomain) 
  69.      */  
  70.     public void executeRuleEngine(final PointDomain pointDomain) {  
  71.         if (null == ruleBase.getPackages() || 0 == ruleBase.getPackages().length) {  
  72.             return;  
  73.         }  
  74.   
  75.         StatefulSession statefulSession = ruleBase.newStatefulSession();  
  76.         statefulSession.insert(pointDomain);  
  77.   
  78.         // fire  
  79.         statefulSession.fireAllRules(new org.drools.spi.AgendaFilter() {  
  80.             public boolean accept(Activation activation) {  
  81.                 return !activation.getRule().getName().contains("_test");  
  82.             }  
  83.         });  
  84.   
  85.         statefulSession.dispose();  
  86.     }  
  87.   
  88.     /** 
  89.      * 從Drl規則文件中讀取規則 
  90.      *  
  91.      * @return 
  92.      * @throws Exception 
  93.      */  
  94.     private PackageBuilder getPackageBuilderFromDrlFile() {  
  95.         // 裝載規則文件  
  96.         List<Reader> readers;  
  97.         try {  
  98.             readers = buildReadersFromDrlFile();  
  99.             // 裝載PackageBuilder  
  100.             return buildPackageBuilder(readers);  
  101.         } catch (FileNotFoundException e) {  
  102.             e.printStackTrace();  
  103.             return null;  
  104.         } catch (Exception e) {  
  105.             e.printStackTrace();  
  106.             return null;  
  107.         }  
  108.     }  
  109.   
  110.     /** 
  111.      * 從Drl規則DB中讀取規則 
  112.      *  
  113.      * @return 
  114.      * @throws Exception 
  115.      */  
  116.     private PackageBuilder getPackBuilderFromDrlDB() {  
  117.         // 裝載規則  
  118.         List<Reader> readers = buildReadersFromDrlDB();  
  119.   
  120.         // 裝載PackageBuilder  
  121.         try {  
  122.             return buildPackageBuilder(readers);  
  123.         } catch (Exception e) {  
  124.             e.printStackTrace();  
  125.             return null;  
  126.         }  
  127.     }  
  128.   
  129.     /** 
  130.      * 裝載db中的規則到List<Reader> 
  131.      *  
  132.      * @return 
  133.      */  
  134.     private List<Reader> buildReadersFromDrlDB() {  
  135.         List<Reader> readers = new ArrayList<Reader>();  
  136.         // 獲取腳本  
  137.         List<DroolsRuleDomain> drlRuleDomains = getRuleFromDB();  
  138.   
  139.         if (null == drlRuleDomains) {  
  140.             return readers;  
  141.         }  
  142.   
  143.         for (DroolsRuleDomain droolsRuleDomain : drlRuleDomains) {  
  144.             String ruleContext = droolsRuleDomain.getRuleContext();  
  145.   
  146.             Reader br = new StringReader(ruleContext);  
  147.             readers.add(br);  
  148.         }  
  149.         return readers;  
  150.     }  
  151.   
  152.     /** 
  153.      * 裝載PackageBuilder 
  154.      *  
  155.      * @param readers 
  156.      * @return 
  157.      * @throws Exception 
  158.      */  
  159.     private PackageBuilder buildPackageBuilder(List<Reader> readers)  
  160.             throws Exception {  
  161.         if (null == readers || 0 == readers.size()) {  
  162.             return null;  
  163.         }  
  164.   
  165.         PackageBuilder backageBuilder = new PackageBuilder();  
  166.         for (Reader r : readers) {  
  167.             backageBuilder.addPackageFromDrl(r);  
  168.         }  
  169.   
  170.         // 檢查腳本是否有問題  
  171.         if (backageBuilder.hasErrors()) {  
  172.             throw new Exception(backageBuilder.getErrors().toString());  
  173.         }  
  174.   
  175.         return backageBuilder;  
  176.     }  
  177.   
  178.     /** 
  179.      * 裝載規則文件到Reader中 
  180.      *  
  181.      * @return 
  182.      * @throws FileNotFoundException 
  183.      */  
  184.     private List<Reader> buildReadersFromDrlFile() throws FileNotFoundException {  
  185.         // 獲取腳本文件  
  186.         List<String> drlFilePath = getRuleDrlFile();  
  187.         // 裝載腳本文件  
  188.         return readRuleFromDrlFile(drlFilePath);  
  189.     }  
  190.   
  191.     /** 
  192.      * 從規則文件中讀取規則 
  193.      *  
  194.      * @param drlFilePath 腳本文件路徑 
  195.      * @return 
  196.      * @throws FileNotFoundException 
  197.      */  
  198.     private List<Reader> readRuleFromDrlFile(List<String> drlFilePath)  
  199.             throws FileNotFoundException {  
  200.         if (null == drlFilePath || 0 == drlFilePath.size()) {  
  201.             return null;  
  202.         }  
  203.   
  204.         List<Reader> readers = new ArrayList<Reader>();  
  205.   
  206.         for (String ruleFilePath : drlFilePath) {  
  207.             readers.add(new FileReader(new File(ruleFilePath)));  
  208.         }  
  209.   
  210.         return readers;  
  211.     }  
  212.   
  213.     /** 
  214.      * 從數據庫中獲取規則腳本內容 
  215.      *  
  216.      * @return 
  217.      */  
  218.     private List<DroolsRuleDomain> getRuleFromDB() {  
  219.         // 測試代碼  
  220.         List<DroolsRuleDomain> droolsRuleDomains = new ArrayList<DroolsRuleDomain>();  
  221.           
  222.         DroolsRuleDomain d1 = new DroolsRuleDomain();  
  223.         d1.setId(1);  
  224.         d1.setRuleContext("package com.drools.demo.point" + "/n" +   
  225.                 "import com.drools.demo.point.PointDomain;" + "/n" +   
  226.                 "rule birthdayPoint" + "/n" +   
  227.                 "// 過生日,則加10分,並且將當月交易比數翻倍後再計算積分" + "/n" +   
  228.                 "salience 100" + "/n" +   
  229.                 "lock-on-active true" + "/n" +   
  230.                 "when" + "/n" +   
  231.                 "$pointDomain : PointDomain(birthDay == true)" + "/n" +   
  232.                 "then" + "/n" +   
  233.                 "pointDomain.setPoint(
pointDomain.getPoint()+10);" + "/n" +                   "pointDomain.recordPointLog(
  1. pointDomain.getUserName(),/"birthdayPoint/");" + "/n" +   
  2.                 "end");  
  3.           
  4.         d1.setRuleName("testRule");  
  5.         d1.setVersion(1);  
  6.           
  7.         droolsRuleDomains.add(d1);  
  8.   
  9.         return droolsRuleDomains;  
  10.     }  
  11.   
  12.     /** 
  13.      * 獲取規則文件 
  14.      *  
  15.      * @return 
  16.      */  
  17.     private List<String> getRuleDrlFile() {  
  18.         List<String> drlFilePath = new ArrayList<String>();  
  19.         drlFilePath  
  20.                 .add("D:/workspace2/DroolsDemo/src/com/drools/demo/point/addpoint.drl");  
  21.         drlFilePath  
  22.                 .add("D:/workspace2/DroolsDemo/src/com/drools/demo/point/subpoint.drl");  
  23.   
  24.         return drlFilePath;  
  25.     }  
  26. }  

 

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

其他的文件與上篇文章相同:

 

RuleBaseFacatory

 

  1. package com.drools.demo.point;  
  2.   
  3. import org.drools.RuleBase;  
  4. import org.drools.RuleBaseFactory;  
  5.   
  6. /** 
  7.  * RuleBaseFacatory 單實例RuleBase生成工具 
  8.  * @author quzishen 
  9.  */  
  10. public class RuleBaseFacatory {  
  11.     private static RuleBase ruleBase;  
  12.       
  13.     public static RuleBase getRuleBase(){  
  14.         return null != ruleBase ? ruleBase : RuleBaseFactory.newRuleBase();  
  15.     }  
  16. }  

 

DroolsRuleDomain

 

  1. package com.drools.demo.point;  
  2.   
  3. /** 
  4.  * 規則內容domain 
  5.  *  
  6.  * @author quzishen 
  7.  */  
  8. public class DroolsRuleDomain {  
  9.     /** 數據庫記錄ID */  
  10.     private long id;  
  11.     /** 規則名稱 */  
  12.     private String ruleName;  
  13.     /** 規則正文  */  
  14.     private String ruleContext;  
  15.     /** 規則版本 */  
  16.     private int version;  
  17.     /** 規則腳本狀態 */  
  18.     private int status;  
  19.   
  20.     public long getId() {  
  21.         return id;  
  22.     }  
  23.   
  24.     public void setId(long id) {  
  25.         this.id = id;  
  26.     }  
  27.   
  28.     public String getRuleName() {  
  29.         return ruleName;  
  30.     }  
  31.   
  32.     public void setRuleName(String ruleName) {  
  33.         this.ruleName = ruleName;  
  34.     }  
  35.   
  36.     public String getRuleContext() {  
  37.         return ruleContext;  
  38.     }  
  39.   
  40.     public void setRuleContext(String ruleContext) {  
  41.         this.ruleContext = ruleContext;  
  42.     }  
  43.   
  44.     public int getVersion() {  
  45.         return version;  
  46.     }  
  47.   
  48.     public void setVersion(int version) {  
  49.         this.version = version;  
  50.     }  
  51.   
  52.     public int getStatus() {  
  53.         return status;  
  54.     }  
  55.   
  56.     public void setStatus(int status) {  
  57.         this.status = status;  
  58.     }  
  59.       
  60. }  

 

PointDomain

 

  1. package com.drools.demo.point;  
  2.   
  3.   
  4. /** 
  5.  * 積分計算對象 
  6.  * @author quzishen 
  7.  */  
  8. public class PointDomain {  
  9.     // 用戶名  
  10.     private String userName;  
  11.     // 是否當日生日  
  12.     private boolean birthDay;  
  13.     // 增加積分數目  
  14.     private long point;  
  15.     // 當月購物次數  
  16.     private int buyNums;  
  17.     // 當月退貨次數  
  18.     private int backNums;  
  19.     // 當月購物總金額  
  20.     private double buyMoney;  
  21.     // 當月退貨總金額  
  22.     private double backMondy;  
  23.     // 當月信用卡還款次數  
  24.     private int billThisMonth;  
  25.       
  26.     /** 
  27.      * 記錄積分發送流水,防止重複發放 
  28.      * @param userName 用戶名 
  29.      * @param type 積分發放類型 
  30.      */  
  31.     public void recordPointLog(String userName, String type){  
  32.         System.out.println("增加對"+userName+"的類型爲"+type+"的積分操作記錄.");  
  33.     }  
  34.   
  35.     public String getUserName() {  
  36.         return userName;  
  37.     }  
  38.   
  39.     public void setUserName(String userName) {  
  40.         this.userName = userName;  
  41.     }  
  42.   
  43.     public boolean isBirthDay() {  
  44.         return birthDay;  
  45.     }  
  46.   
  47.     public void setBirthDay(boolean birthDay) {  
  48.         this.birthDay = birthDay;  
  49.     }  
  50.   
  51.     public long getPoint() {  
  52.         return point;  
  53.     }  
  54.   
  55.     public void setPoint(long point) {  
  56.         this.point = point;  
  57.     }  
  58.   
  59.     public int getBuyNums() {  
  60.         return buyNums;  
  61.     }  
  62.   
  63.     public void setBuyNums(int buyNums) {  
  64.         this.buyNums = buyNums;  
  65.     }  
  66.   
  67.     public int getBackNums() {  
  68.         return backNums;  
  69.     }  
  70.   
  71.     public void setBackNums(int backNums) {  
  72.         this.backNums = backNums;  
  73.     }  
  74.   
  75.     public double getBuyMoney() {  
  76.         return buyMoney;  
  77.     }  
  78.   
  79.     public void setBuyMoney(double buyMoney) {  
  80.         this.buyMoney = buyMoney;  
  81.     }  
  82.   
  83.     public double getBackMondy() {  
  84.         return backMondy;  
  85.     }  
  86.   
  87.     public void setBackMondy(double backMondy) {  
  88.         this.backMondy = backMondy;  
  89.     }  
  90.   
  91.     public int getBillThisMonth() {  
  92.         return billThisMonth;  
  93.     }  
  94.   
  95.     public void setBillThisMonth(int billThisMonth) {  
  96.         this.billThisMonth = billThisMonth;  
  97.     }  
  98.   
  99. }  

 

addpoint.drl

 

  1. package com.drools.demo.point  
  2.   
  3. import com.drools.demo.point.PointDomain;  
  4.   
  5. rule birthdayPoint  
  6.     // 過生日,則加10分,並且將當月交易比數翻倍後再計算積分  
  7.     salience 100  
  8.     lock-on-active true  
  9.     when  
  10.         $pointDomain : PointDomain(birthDay == true)  
  11.     then  
  12.         pointDomain.setPoint(
pointDomain.getPoint()+10);          pointDomain.setBuyNums(pointDomain.getBuyNums()*2);          pointDomain.setBuyMoney(pointDomain.getBuyMoney()*2);          pointDomain.setBillThisMonth(pointDomain.getBillThisMonth()*2);                    pointDomain.recordPointLog(pointDomain.getUserName(),"birthdayPoint");  end    rule billThisMonthPoint      // 2011-01-08 - 2011-08-08每月信用卡還款3次以上,每滿3筆贈送30分      salience 99      lock-on-active true      date-effective "2011-01-08 23:59:59"      date-expires "2011-08-08 23:59:59"      when          $pointDomain : PointDomain(billThisMonth >= 3)      then          pointDomain.setPoint(pointDomain.getPoint()+$pointDomain.getBillThisMonth()/3*30);          pointDomain.recordPointLog(pointDomain.getUserName(),"billThisMonthPoint");  end    rule buyMoneyPoint      // 當月購物總金額100以上,每100元贈送10分      salience 98      lock-on-active true      when          $pointDomain : PointDomain(buyMoney >= 100)      then          pointDomain.setPoint(pointDomain.getPoint()+ (int)$pointDomain.getBuyMoney()/100 * 10);          pointDomain.recordPointLog(pointDomain.getUserName(),"buyMoneyPoint");  end    rule buyNumsPoint      // 當月購物次數5次以上,每五次贈送50分      salience 97      lock-on-active true      when          $pointDomain : PointDomain(buyNums >= 5)      then          pointDomain.setPoint(pointDomain.getPoint()+$pointDomain.getBuyNums()/5 * 50);          pointDomain.recordPointLog(pointDomain.getUserName(),"buyNumsPoint");  end    rule allFitPoint      // 特別的,如果全部滿足了要求,則額外獎勵100分      salience 96      lock-on-active true      when          $pointDomain:PointDomain(buyNums >= 5 && billThisMonth >= 3 && buyMoney >= 100)      then          pointDomain.setPoint(pointDomain.getPoint()+ 100);          pointDomain.recordPointLog(
  1. pointDomain.getUserName(),"allFitPoint");  
  2. end  

 

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

測試代碼

Test

 

  1. package com.drools.demo.point;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.InputStreamReader;  
  7.   
  8.   
  9. public class Test {  
  10.   
  11.     /** 
  12.      * @param args 
  13.      * @throws IOException  
  14.      */  
  15.     public static void main(String[] args) throws IOException {  
  16.         PointRuleEngine pointRuleEngine = new PointRuleEngineImpl();  
  17.         boolean isStart = false;  
  18.         while(true){  
  19.             InputStream is = System.in;  
  20.             BufferedReader br = new BufferedReader(new InputStreamReader(is));  
  21.             String input = br.readLine();  
  22.               
  23.             if (null != input && "s".equals(input)){  
  24.                 System.out.println("初始化規則引擎...");  
  25.                 pointRuleEngine.initEngine();  
  26.                 isStart = true;  
  27.                 System.out.println("初始化規則引擎結束.");  
  28.             } else if ("e".equals(input)){  
  29.                 if (!isStart) {  
  30.                     System.out.println("需要輸入s啓動");  
  31.                 } else {  
  32.                     final PointDomain pointDomain = new PointDomain();  
  33.                     pointDomain.setUserName("hello kity");  
  34.                     pointDomain.setBackMondy(100d);  
  35.                     pointDomain.setBuyMoney(500d);  
  36.                     pointDomain.setBackNums(1);  
  37.                     pointDomain.setBuyNums(5);  
  38.                     pointDomain.setBillThisMonth(5);  
  39.                     pointDomain.setBirthDay(true);  
  40.                     pointDomain.setPoint(0l);  
  41.                       
  42.                     pointRuleEngine.executeRuleEngine(pointDomain);  
  43.                       
  44.                     System.out.println("執行完畢BillThisMonth:"+pointDomain.getBillThisMonth());  
  45.                     System.out.println("執行完畢BuyMoney:"+pointDomain.getBuyMoney());  
  46.                     System.out.println("執行完畢BuyNums:"+pointDomain.getBuyNums());  
  47.                       
  48.                     System.out.println("執行完畢規則引擎決定發送積分:"+pointDomain.getPoint());  
  49.                 }  
  50.             } else if ("r".equals(input)){  
  51.                 System.out.println("刷新規則文件...");  
  52.                 pointRuleEngine.refreshEnginRule();  
  53.                 isStart = true;  
  54.                 System.out.println("刷新規則文件結束.");  
  55.             } else if ("q".equals(input)) {  
  56.                 System.exit(0);  
  57.             }  
  58.         }  
  59.     }  
  60.   
  61. }  
發佈了29 篇原創文章 · 獲贊 4 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章