[Drools]JAVA规则引擎 -- Drools 2

时间:2024-08-19 16:07:44

上一篇文章 http://blog.****.net/quzishen/archive/2011/01/25/6163012.aspx 描述了一些常用的drools的语法标签和一个模拟实例即发送积分的场景,这一片优化了一下代码,在此贴一下,希望有这方面使用经验的朋友多多交流沟通,指正不足。

通常而言,习惯上我们将规则放到文件系统中,比如以drl结尾的规则文件,现在我们要扩充一下,使其放到数据库中,以供多台服务器同时使用,同时依然保留文件系统的支持。

先看下一个接口:

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

实现过程没有任何难度,两种方式封装过程只在于读取规则的方式不同,代码很简单:

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

其中的getRuleFromDB() 和 getRuleDrlFile() 两个方法即可以重写以接入个人系统,现在其中编写的是测试代码。

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

RuleBaseFacatory

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

DroolsRuleDomain

  1. package com.drools.demo.point;
  2. /**
  3. * 规则内容domain
  4. *
  5. * @author quzishen
  6. */
  7. public class DroolsRuleDomain {
  8. /** 数据库记录ID */
  9. private long id;
  10. /** 规则名称 */
  11. private String ruleName;
  12. /** 规则正文  */
  13. private String ruleContext;
  14. /** 规则版本 */
  15. private int version;
  16. /** 规则脚本状态 */
  17. private int status;
  18. public long getId() {
  19. return id;
  20. }
  21. public void setId(long id) {
  22. this.id = id;
  23. }
  24. public String getRuleName() {
  25. return ruleName;
  26. }
  27. public void setRuleName(String ruleName) {
  28. this.ruleName = ruleName;
  29. }
  30. public String getRuleContext() {
  31. return ruleContext;
  32. }
  33. public void setRuleContext(String ruleContext) {
  34. this.ruleContext = ruleContext;
  35. }
  36. public int getVersion() {
  37. return version;
  38. }
  39. public void setVersion(int version) {
  40. this.version = version;
  41. }
  42. public int getStatus() {
  43. return status;
  44. }
  45. public void setStatus(int status) {
  46. this.status = status;
  47. }
  48. }

PointDomain

  1. package com.drools.demo.point;
  2. /**
  3. * 积分计算对象
  4. * @author quzishen
  5. */
  6. public class PointDomain {
  7. // 用户名
  8. private String userName;
  9. // 是否当日生日
  10. private boolean birthDay;
  11. // 增加积分数目
  12. private long point;
  13. // 当月购物次数
  14. private int buyNums;
  15. // 当月退货次数
  16. private int backNums;
  17. // 当月购物总金额
  18. private double buyMoney;
  19. // 当月退货总金额
  20. private double backMondy;
  21. // 当月信用卡还款次数
  22. private int billThisMonth;
  23. /**
  24. * 记录积分发送流水,防止重复发放
  25. * @param userName 用户名
  26. * @param type 积分发放类型
  27. */
  28. public void recordPointLog(String userName, String type){
  29. System.out.println("增加对"+userName+"的类型为"+type+"的积分操作记录.");
  30. }
  31. public String getUserName() {
  32. return userName;
  33. }
  34. public void setUserName(String userName) {
  35. this.userName = userName;
  36. }
  37. public boolean isBirthDay() {
  38. return birthDay;
  39. }
  40. public void setBirthDay(boolean birthDay) {
  41. this.birthDay = birthDay;
  42. }
  43. public long getPoint() {
  44. return point;
  45. }
  46. public void setPoint(long point) {
  47. this.point = point;
  48. }
  49. public int getBuyNums() {
  50. return buyNums;
  51. }
  52. public void setBuyNums(int buyNums) {
  53. this.buyNums = buyNums;
  54. }
  55. public int getBackNums() {
  56. return backNums;
  57. }
  58. public void setBackNums(int backNums) {
  59. this.backNums = backNums;
  60. }
  61. public double getBuyMoney() {
  62. return buyMoney;
  63. }
  64. public void setBuyMoney(double buyMoney) {
  65. this.buyMoney = buyMoney;
  66. }
  67. public double getBackMondy() {
  68. return backMondy;
  69. }
  70. public void setBackMondy(double backMondy) {
  71. this.backMondy = backMondy;
  72. }
  73. public int getBillThisMonth() {
  74. return billThisMonth;
  75. }
  76. public void setBillThisMonth(int billThisMonth) {
  77. this.billThisMonth = billThisMonth;
  78. }
  79. }

addpoint.drl

  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

  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. /**
  8. * @param args
  9. * @throws IOException
  10. */
  11. public static void main(String[] args) throws IOException {
  12. PointRuleEngine pointRuleEngine = new PointRuleEngineImpl();
  13. boolean isStart = false;
  14. while(true){
  15. InputStream is = System.in;
  16. BufferedReader br = new BufferedReader(new InputStreamReader(is));
  17. String input = br.readLine();
  18. if (null != input && "s".equals(input)){
  19. System.out.println("初始化规则引擎...");
  20. pointRuleEngine.initEngine();
  21. isStart = true;
  22. System.out.println("初始化规则引擎结束.");
  23. } else if ("e".equals(input)){
  24. if (!isStart) {
  25. System.out.println("需要输入s启动");
  26. } else {
  27. final PointDomain pointDomain = new PointDomain();
  28. pointDomain.setUserName("hello kity");
  29. pointDomain.setBackMondy(100d);
  30. pointDomain.setBuyMoney(500d);
  31. pointDomain.setBackNums(1);
  32. pointDomain.setBuyNums(5);
  33. pointDomain.setBillThisMonth(5);
  34. pointDomain.setBirthDay(true);
  35. pointDomain.setPoint(0l);
  36. pointRuleEngine.executeRuleEngine(pointDomain);
  37. System.out.println("执行完毕BillThisMonth:"+pointDomain.getBillThisMonth());
  38. System.out.println("执行完毕BuyMoney:"+pointDomain.getBuyMoney());
  39. System.out.println("执行完毕BuyNums:"+pointDomain.getBuyNums());
  40. System.out.println("执行完毕规则引擎决定发送积分:"+pointDomain.getPoint());
  41. }
  42. } else if ("r".equals(input)){
  43. System.out.println("刷新规则文件...");
  44. pointRuleEngine.refreshEnginRule();
  45. isStart = true;
  46. System.out.println("刷新规则文件结束.");
  47. } else if ("q".equals(input)) {
  48. System.exit(0);
  49. }
  50. }
  51. }
  52. }