天天看點

規則引擎:Drools與JRuleEngine

譯文:《規則引擎:Drools與JRuleEngine》

譯者:jacktom(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)

/*
  \* $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)

<?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)

/*
  * $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)

<?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)也有很大優勢。