规则引擎:Drools与JRuleEngine

 

规则引擎:Drools与JRuleEngine


译文:《规则引擎:Drools与JRuleEngine》
译者:chszs
作者:Vivek Tomar
原文:《Rule engine : Drools / JRuleEngine》
原文见http://www.naxos-software.de/blog/index.php?/archives/78-Rule-engine-Drools-JRuleEngine.html

一、规则引擎
规则引擎有助于基于存储在规则中的知识和推理来执行判断。这些规则基本上只有条件和动作,别无它物。

规则引擎的优点:
1、分隔应用程序的条件和控制流
(1) 规则都存储在单独的文件。
(2) 规则可以被技术人士和商业人士修改的。
(3) 规则改变后应用程序不必重新部署。
(4) 使用集中的规则使得应用程序更易于管理和维护。
 
2、规则替换了代码中的if else语句
(1) 规则脚本更容易提取。
(2) 即使是非技术人员也能轻易地遵循规则。
(3) 集中可以解决问题,而不是实现。
(4) 与实现代码相比,规则聚团更容易编写。 

3、为什么作决定能很容易地概念化

总结:规则有助于消除代码中大量的if else语句,使代码更易于维护。

二、Drools介绍
Drools是一个开源实现。它是一个Java库,以Apache许可证发布,其二进制代码或源码下载均有效。

推理机的核心是一个规则引擎。它执行模式匹配,在执行动作中做出决策。RETE算法用于模式匹配。 

知识被表述为规则。规则有两个主要部分:条件和动作。

例如:
如果用户(年龄>17),那么System.out.println("User is greater then 17");

在人工智能系统,主要有两种响应的方法。
1、正向链(Forward Chaining)
这是基于事实根据的。在工作区域检查中规则。当规则条件为真的规则不再有时,模式匹配结束。

2、反向链(Backward Chaining)
只检查规则的动作可以匹配目标的规则。如果满足条件,然后进行评估。

3、兼容JDK1.4,且需要下面的库。
(1) drools-all-jdk1.4.2.1.jar
(2) xercesImpl-2.6.2.jar
(3) antlr-2.7.5.jar
(4) janino-2.3.2.jar

4、代码示例(ApplyRule.java)

 

view plain copy to clipboard print ?
  1. /*  
  2.   /* $Header$  
  3.   */  
  4.   
  5.   package com.vivek.drools.example;   
  6.   
  7.   import java.io.IOException;   
  8.   import java.io.InputStream;   
  9.   import org.apache.commons.logging.Log;   
  10.   import org.apache.commons.logging.LogFactory;   
  11.   import org.drools.FactException;   
  12.   import org.drools.IntegrationException;   
  13.   import org.drools.RuleBase;   
  14.   import org.drools.WorkingMemory;   
  15.   import org.drools.io.RuleBaseLoader;   
  16.   import org.xml.sax.SAXException;   
  17.   
  18.   /**  
  19.   * Demonstration of a sample rule using Java.  
  20.   *  
  21.   * @version <tt>$Revision: 1.0 $</tt>  
  22.   * @author <a href="mailto:{[email protected]}" mce_href="mailto:{[email protected]}">{Vivek Tomar}</a>.  
  23.   */  
  24.   
  25.   public class ApplyRule {   
  26.   
  27.   // Constants -----------------------------------------------------   
  28.   
  29.   static Log log = LogFactory.getLog(ApplyRule.class.getName());   
  30.   private static final String RULE_FILE = "/rules/rules.drl";   
  31.   //private static final String RULE_IS_TURNOVER_INSURABLE = "turnoverInsurable";   
  32.   //private static final String RULE_IS_TURNOVER_NOT_INSURABLE = "turnoverNotInsurable";   
  33.   
  34.   // Attributes ----------------------------------------------------   
  35.   
  36.   private double turnover = 1000000;   
  37.   
  38.   // Constructors --------------------------------------------------   
  39.   
  40.   public ApplyRule () {   
  41.   try {   
  42.   InputStream in = this.getClass().getResourceAsStream(RULE_FILE);   
  43.   RuleBase ruleBase = RuleBaseLoader.loadFromInputStream(in);   
  44.   WorkingMemory workingMemory = ruleBase.newWorkingMemory();   
  45.   workingMemory.assertObject(this);   
  46.   // Fire specific rule   
  47.   //workingMemory.fireAllRules(new RuleNameEqualsAgendaFilter(RULE_IS_TURNOVER_INSURABLE));   
  48.   
  49.   //Fire all rules   
  50.   workingMemory.fireAllRules();   
  51.   } catch (IntegrationException e) {   
  52.   e.printStackTrace();   
  53.   } catch (FactException e) {   
  54.   e.printStackTrace();   
  55.   } catch (SAXException e) {   
  56.   e.printStackTrace();   
  57.   } catch (IOException e) {   
  58.   e.printStackTrace();   
  59.   }   
  60.   }   
  61.   
  62.   // Public --------------------------------------------------------   
  63.   
  64.   public static void main (String args[]) {   
  65.   new ApplyRule();   
  66.   }   
  67.   
  68.   public double getTurnover () {   
  69.   return turnover;   
  70.   }   
  71.   
  72.   public void setTurnover (double turnover) {   
  73.   this.turnover = turnover;   
  74.   }   
  75.   
  76.   public void printCompanyInsurable () {   
  77.   log.info("******************************");   
  78.   log.info("This company is Insurable.");   
  79.   log.info("******************************");   
  80.   }   
  81.   
  82.   public void printCompanyNotInsurable () {   
  83.   log.info("==============================");   
  84.   log.info("This company is not Insurable.");   
  85.   log.info("==============================");   
  86.   }   
  87.   }  

/* /* $Header$ */ package com.vivek.drools.example; import java.io.IOException; import java.io.InputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.drools.FactException; import org.drools.IntegrationException; import org.drools.RuleBase; import org.drools.WorkingMemory; import org.drools.io.RuleBaseLoader; import org.xml.sax.SAXException; /** * Demonstration of a sample rule using Java. * * @version <tt>$Revision: 1.0 $</tt> * @author <a href="mailto:{[email protected]}" mce_href="mailto:{[email protected]}">{Vivek Tomar}</a>. */ public class ApplyRule { // Constants ----------------------------------------------------- static Log log = LogFactory.getLog(ApplyRule.class.getName()); private static final String RULE_FILE = "/rules/rules.drl"; //private static final String RULE_IS_TURNOVER_INSURABLE = "turnoverInsurable"; //private static final String RULE_IS_TURNOVER_NOT_INSURABLE = "turnoverNotInsurable"; // Attributes ---------------------------------------------------- private double turnover = 1000000; // Constructors -------------------------------------------------- public ApplyRule () { try { InputStream in = this.getClass().getResourceAsStream(RULE_FILE); RuleBase ruleBase = RuleBaseLoader.loadFromInputStream(in); WorkingMemory workingMemory = ruleBase.newWorkingMemory(); workingMemory.assertObject(this); // Fire specific rule //workingMemory.fireAllRules(new RuleNameEqualsAgendaFilter(RULE_IS_TURNOVER_INSURABLE)); //Fire all rules workingMemory.fireAllRules(); } catch (IntegrationException e) { e.printStackTrace(); } catch (FactException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // Public -------------------------------------------------------- public static void main (String args[]) { new ApplyRule(); } public double getTurnover () { return turnover; } public void setTurnover (double turnover) { this.turnover = turnover; } public void printCompanyInsurable () { log.info("******************************"); log.info("This company is Insurable."); log.info("******************************"); } public void printCompanyNotInsurable () { log.info("=============================="); log.info("This company is not Insurable."); log.info("=============================="); } }

规则定义(rules.drl)

 

 

view plain copy to clipboard print ?
  1.   <?xml version="1.0"?>  
  2. <rule-set name="cheese rules"  
  3.   xmlns="http://drools.org/rules"  
  4.   xmlns:java="http://drools.org/semantics/java"  
  5.   xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"  
  6.   xs:schemaLocation="http://drools.org/rules rules.xsd   
  7.   http://drools.org/semantics/java java.xsd">  
  8. <rule name="turnoverInsurable">  
  9. <parameter identifier="applyRule">  
  10. <class>com.vivek.drools.example.ApplyRule</class>  
  11. </parameter>  
  12. <java:condition>applyRule.getTurnover() > 2000000 </java:condition>  
  13. <java:consequence>  
  14.   applyRule.printCompanyInsurable();   
  15. </java:consequence>  
  16. </rule>  
  17. <rule name="turnoverNotInsurable">  
  18. <parameter identifier="applyRule">  
  19. <class>com.vivek.drools.example.ApplyRule</class>  
  20. </parameter>  
  21. <java:condition>applyRule.getTurnover() < 2000000 </java:condition>  
  22. <java:consequence>  
  23.   applyRule.printCompanyNotInsurable();   
  24. </java:consequence>  
  25. </rule>  
  26. </rule-set>  

<?xml version="1.0"?> <rule-set name="cheese rules" xmlns="http://drools.org/rules" xmlns:java="http://drools.org/semantics/java" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://drools.org/rules rules.xsd http://drools.org/semantics/java java.xsd"> <rule name="turnoverInsurable"> <parameter identifier="applyRule"> <class>com.vivek.drools.example.ApplyRule</class> </parameter> <java:condition>applyRule.getTurnover() > 2000000 </java:condition> <java:consequence> applyRule.printCompanyInsurable(); </java:consequence> </rule> <rule name="turnoverNotInsurable"> <parameter identifier="applyRule"> <class>com.vivek.drools.example.ApplyRule</class> </parameter> <java:condition>applyRule.getTurnover() < 2000000 </java:condition> <java:consequence> applyRule.printCompanyNotInsurable(); </java:consequence> </rule> </rule-set>

三、JRuleEngine介绍
JRuleEngine是一个基于Java的规则引擎,遵循JSR94规范,版本1.1。

1、JRuleEngine共同特征: 
(1) 在JRuleEngine输入对象被称为事实,而输出对象被称为结论。 
(2) 一个类的方法可直接从规则中调用。 

2、JRuleEngine基于前向链算法。

3、规则是类org.jruleengine.rule.RuleImpl的对象,它们以下列方式载入:
(1) 可以从XML文件中读入
(2) 通过RuleImpl对象创建,也可以取自数据库。

4、会话是客户端和规则引擎之间的运行时的胶水。会话与单一的规则执行集相关。会话规则的类型:
(1) 一个有状态的规则会话可持续很长时间,可一次又一次地查询。
(2) 一个无状态的规则给出了实现,但只能持续一定时期。

5、JRuleEngine需要两个库:
(1) jsr94.jar
(2) jruleengine.jar

6、添加这些文件到您应用程序的类路径。

7、通过实例化一个有状态的规则会话(StatefulRuleSession)或无状态的规则会话(StatelessRuleSession)来使用这个库。

8、代码例子(ApplyRule.java)

 

 

view plain copy to clipboard print ?
  1. /*  
  2. * $Header$  
  3. */  
  4. package com.vivek.jruleengine.example;   
  5.   
  6. import java.io.InputStream;   
  7. import java.util.ArrayList;   
  8. import java.util.HashMap;   
  9. import java.util.List;   
  10.   
  11. import javax.rules.RuleRuntime;   
  12. import javax.rules.RuleServiceProvider;   
  13. import javax.rules.RuleServiceProviderManager;   
  14. import javax.rules.StatelessRuleSession;   
  15. import javax.rules.admin.RuleAdministrator;   
  16. import javax.rules.admin.RuleExecutionSet;   
  17.   
  18. import org.apache.commons.logging.Log;   
  19. import org.apache.commons.logging.LogFactory;   
  20.   
  21. /**  
  22. * Demonstration of a sample rule using Java.  
  23. *  
  24. * @version <tt>$Revision: 1.0 $</tt>  
  25. * @author <a href="mailto:{[email protected]}" mce_href="mailto:{[email protected]}">{Vivek Tomar}</a>.  
  26. */  
  27. public class ApplyRule {   
  28.   
  29. // Constants -----------------------------------------------------   
  30.   
  31. static Log log = LogFactory.getLog(ApplyRule.class.getName());   
  32. private static final String RULE_FILE = "/rules/rules.xml";   
  33.   
  34. // Attributes ----------------------------------------------------   
  35.   
  36. private double turnover = 1000000;   
  37.   
  38. // Constructors --------------------------------------------------   
  39.   
  40. public ApplyRule () {   
  41.   
  42. try {   
  43. // Load the rule service provider of the reference   
  44. // implementation.   
  45. // Loading this class will automatically register this   
  46. // provider with the provider manager.   
  47. Class.forName("org.jruleengine.RuleServiceProviderImpl");   
  48.   
  49. // Get the rule service provider from the provider manager.   
  50. RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider("org.jruleengine");   
  51.   
  52. // get the RuleAdministrator   
  53. RuleAdministrator ruleAdministrator = serviceProvider.getRuleAdministrator();   
  54. log.info("Administration API");   
  55. log.info("======================");   
  56. log.info("Acquired RuleAdministrator: ");   
  57.   
  58. // get an input stream to a test XML ruleset   
  59. // This rule execution set is part of the TCK.   
  60. InputStream inStream = this.getClass().getResourceAsStream(RULE_FILE);   
  61.     
  62.   
  63. // parse the ruleset from the XML document   
  64. RuleExecutionSet res1 =   
  65. ruleAdministrator.getLocalRuleExecutionSetProvider(null).createRuleExecutionSet(inStream, null);   
  66. inStream.close();   
  67. log.info("Loaded RuleExecutionSet: ");   
  68.   
  69. // register the RuleExecutionSet   
  70. String uri = res1.getName();   
  71. ruleAdministrator.registerRuleExecutionSet(uri, res1, null);   
  72. log.info("Bound RuleExecutionSet to URI: " + uri);   
  73.   
  74. RuleRuntime ruleRuntime = serviceProvider.getRuleRuntime();   
  75. log.info("Acquired RuleRuntime: ");   
  76.   
  77. // create a StatelessRuleSession   
  78. StatelessRuleSession statelessRuleSession = (StatelessRuleSession)   
  79. ruleRuntime.createRuleSession(uri, new HashMap(),   
  80. RuleRuntime.STATELESS_SESSION_TYPE);   
  81.   
  82. log.info("Got Stateless Rule Session: " + statelessRuleSession);   
  83.   
  84. // call executeRules with some input objects   
  85.   
  86. // Create a input list.   
  87. List input = new ArrayList();   
  88. input.add(this);   
  89.   
  90. // Print the input.   
  91. log.info("Calling rule session with the following data");   
  92. log.info("Customer turnover input: " + this.getTurnover());   
  93.   
  94. // Execute the rules without a filter.   
  95. List results = statelessRuleSession.executeRules(input);   
  96.   
  97. // Release the session.   
  98. statelessRuleSession.release();   
  99. log.info("Released Stateless Rule Session.");   
  100.   
  101. catch (NoClassDefFoundError e) {   
  102. if (e.getMessage().indexOf("Exception") != -1) {   
  103. log.error("Error: The Rule Engine Implementation could not be found.");   
  104. else {   
  105. log.error("Error: " + e.getMessage());   
  106. }   
  107. catch (Exception e) {   
  108. e.printStackTrace();   
  109. }   
  110. }   
  111.   
  112. // Public --------------------------------------------------------   
  113.   
  114. public static void main (String args[]) {   
  115. new ApplyRule();   
  116. }   
  117.   
  118. public double getTurnover () {   
  119. return turnover;   
  120. }   
  121.   
  122. public void setTurnover (double turnover) {   
  123. this.turnover = turnover;   
  124. }   
  125.   
  126. public void printCompanyInsurable () {   
  127. log.info("******************************");   
  128. log.info("This company is Insurable.");   
  129. log.info("******************************");   
  130. }   
  131.   
  132. public void printCompanyNotInsurable () {   
  133. log.info("==============================");   
  134. log.info("This company is not Insurable.");   
  135. log.info("==============================");   
  136. }   
  137. }  

/* * $Header$ */ package com.vivek.jruleengine.example; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.rules.RuleRuntime; import javax.rules.RuleServiceProvider; import javax.rules.RuleServiceProviderManager; import javax.rules.StatelessRuleSession; import javax.rules.admin.RuleAdministrator; import javax.rules.admin.RuleExecutionSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Demonstration of a sample rule using Java. * * @version <tt>$Revision: 1.0 $</tt> * @author <a href="mailto:{[email protected]}" mce_href="mailto:{[email protected]}">{Vivek Tomar}</a>. */ public class ApplyRule { // Constants ----------------------------------------------------- static Log log = LogFactory.getLog(ApplyRule.class.getName()); private static final String RULE_FILE = "/rules/rules.xml"; // Attributes ---------------------------------------------------- private double turnover = 1000000; // Constructors -------------------------------------------------- public ApplyRule () { try { // Load the rule service provider of the reference // implementation. // Loading this class will automatically register this // provider with the provider manager. Class.forName("org.jruleengine.RuleServiceProviderImpl"); // Get the rule service provider from the provider manager. RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider("org.jruleengine"); // get the RuleAdministrator RuleAdministrator ruleAdministrator = serviceProvider.getRuleAdministrator(); log.info("Administration API"); log.info("======================"); log.info("Acquired RuleAdministrator: "); // get an input stream to a test XML ruleset // This rule execution set is part of the TCK. InputStream inStream = this.getClass().getResourceAsStream(RULE_FILE); // parse the ruleset from the XML document RuleExecutionSet res1 = ruleAdministrator.getLocalRuleExecutionSetProvider(null).createRuleExecutionSet(inStream, null); inStream.close(); log.info("Loaded RuleExecutionSet: "); // register the RuleExecutionSet String uri = res1.getName(); ruleAdministrator.registerRuleExecutionSet(uri, res1, null); log.info("Bound RuleExecutionSet to URI: " + uri); RuleRuntime ruleRuntime = serviceProvider.getRuleRuntime(); log.info("Acquired RuleRuntime: "); // create a StatelessRuleSession StatelessRuleSession statelessRuleSession = (StatelessRuleSession) ruleRuntime.createRuleSession(uri, new HashMap(), RuleRuntime.STATELESS_SESSION_TYPE); log.info("Got Stateless Rule Session: " + statelessRuleSession); // call executeRules with some input objects // Create a input list. List input = new ArrayList(); input.add(this); // Print the input. log.info("Calling rule session with the following data"); log.info("Customer turnover input: " + this.getTurnover()); // Execute the rules without a filter. List results = statelessRuleSession.executeRules(input); // Release the session. statelessRuleSession.release(); log.info("Released Stateless Rule Session."); } catch (NoClassDefFoundError e) { if (e.getMessage().indexOf("Exception") != -1) { log.error("Error: The Rule Engine Implementation could not be found."); } else { log.error("Error: " + e.getMessage()); } } catch (Exception e) { e.printStackTrace(); } } // Public -------------------------------------------------------- public static void main (String args[]) { new ApplyRule(); } public double getTurnover () { return turnover; } public void setTurnover (double turnover) { this.turnover = turnover; } public void printCompanyInsurable () { log.info("******************************"); log.info("This company is Insurable."); log.info("******************************"); } public void printCompanyNotInsurable () { log.info("=============================="); log.info("This company is not Insurable."); log.info("=============================="); } }

 



规则定义(rules.xml)

 

view plain copy to clipboard print ?
  1.   <?xml version="1.0" encoding="UTF-8"?>  
  2. <rule-execution-set>  
  3. <name>RuleExecutionSet1</name>  
  4. <description>Rule Execution Set</description>  
  5. <synonymn name="applyRule" class="com.vivek.jruleengine.example.ApplyRule" />  
  6. <rule name="turnoverInsurable" description="Check if turnover insurable" >  
  7. <if leftTerm="applyRule.getTurnover" op=">" rightTerm="2000000" />  
  8. <then method="applyRule.printCompanyInsurable()" />  
  9. </rule>  
  10. <rule name="turnoverNotInsurable" description="Check if turnover not insurable" >  
  11. <if leftTerm="applyRule.getTurnover" op="<" rightTerm="2000000" />  
  12. <then method="applyRule.printCompanyNotInsurable" />  
  13. </rule>  
  14. </rule-execution-set>  

<?xml version="1.0" encoding="UTF-8"?> <rule-execution-set> <name>RuleExecutionSet1</name> <description>Rule Execution Set</description> <synonymn name="applyRule" class="com.vivek.jruleengine.example.ApplyRule" /> <rule name="turnoverInsurable" description="Check if turnover insurable" > <if leftTerm="applyRule.getTurnover" op=">" rightTerm="2000000" /> <then method="applyRule.printCompanyInsurable()" /> </rule> <rule name="turnoverNotInsurable" description="Check if turnover not insurable" > <if leftTerm="applyRule.getTurnover" op="<" rightTerm="2000000" /> <then method="applyRule.printCompanyNotInsurable" /> </rule> </rule-execution-set>

结论:
Drools除了提供正常的规则引擎的能力,还有以下额外的优点:
(1) 无论是技术人士还是商业人士,Drools都是用户友好的,它提供了一个巨大的支持工具集。
(2) Drools的Reteoo算法可加速和可扩展。
(3) Drools提供的Eclipse插件带有自动完成智能感知和调试视图、规则流GUI等。
(4) 基于Web的工具(Guvnor):是一个业务规则管理系统(BRMS),它提供了高级规则授权、版本控制和管理。

至于其他的规则引擎,我个人建议在项目中使用Drools,因为它有一个很大的支持社区。在IDE支持和基于Web的规则管理工具(Guvnor)也有很大优势。

 

你可能感兴趣的:(规则引擎:Drools与JRuleEngine)