Drools規則引擎介紹一

原文地址:http://docs.jboss.org/drools/release/6.2.0.Final/drools-docs/html_single/index.html 

原文前面所有的步驟都可以省略,直接從安裝eclipse插件開始,安裝地址是:http://docs.jboss.org/drools/release/6.2.0.Final/drools-docs/html_single/index.html

在國內現在是可以直接update,所以不需要用zip安裝之類的方法。

在eclipse的Preferences中出現了一個菜單Drools,在installed Drools裏面add一個Runtime(選擇官網下載後解壓縮的binaries目錄)。

新建一個Drools Project

src/main/java新建類DroolsTest:

package com.sample;


import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;


/**
 * This is a sample class to launch a rule.
 */
public class DroolsTest {


    public static final void main(String[] args) {
        try {
            // load up the knowledge base
       KieServices ks = KieServices.Factory.get();
       KieContainer kContainer = ks.getKieClasspathContainer();
        KieSession kSession = kContainer.newKieSession("ksession-rules");


            // go !
            Message message = new Message();
            message.setMessage("Hello World");
            message.setStatus(Message.HELLO);
            kSession.insert(message);
            kSession.fireAllRules();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }


    public static class Message {


        public static final int HELLO = 0;
        public static final int GOODBYE = 1;


        private String message;


        private int status;


        public String getMessage() {
            return this.message;
        }


        public void setMessage(String message) {
            this.message = message;
        }


        public int getStatus() {
            return this.status;
        }


        public void setStatus(int status) {
            this.status = status;
        }


    }


}



src/main/resources/rules新建規則文件

package com.sample
 
import com.sample.DroolsTest.Message;
 
rule "Hello World"
    when
        m : Message( status == Message.HELLO, myMessage : message )
    then
        System.out.println( myMessage );
        m.setMessage( "Goodbye cruel world" );
        m.setStatus( Message.GOODBYE );
        update( m );
end


rule "GoodBye"
    when
        Message( status == Message.GOODBYE, myMessage : message )
    then
        System.out.println( myMessage );
end


src/main/resources/META-INF新建配置文件kmodule.xml:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="rules" packages="rules">
        <ksession name="ksession-rules"/>
    </kbase>
</kmodule>


點擊run可以看到結果:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Hello World
Goodbye cruel world

講解一下這個例子的邏輯:main函數前三行是固定寫法,它加載"ksession-rules"。這個名字在kmodule.xml裏面配置,它會在resources目錄下的rules目錄下去找規則。

main函數先寫一個類型爲Message.HELLO類型的消息到對象中,規則文件中規定如果類型爲Message.HELLO則打印出消息並且更新對象類型Message.GOODBYE,

因爲update(m)這時候規則引擎將會被再次觸發,因爲類型更新爲Message.GOODBYE將會觸發規則2,打印出新類型的消息。


怎麼樣在Maven中使用Drools:

drools在maven中央倉庫中就有,所以不需要配置額外的maven倉庫,配置如下:

    <dependencies>
      <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-bom</artifactId>
        <type>pom</type>
        <version>...</version>
        <scope>import</scope>
      </dependency>
      ...
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-api</artifactId>
    </dependency>
    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-compiler</artifactId>
      <scope>runtime</scope>
    </dependency>
    ...
  <dependencies>



運行時:

這裏說的運行時是指:如果你部署二進制形式的規則(KnowledgePackage對象或者KnowledgeBase對象),這樣可以讓你的運行時非常輕量化。可以用drools-compiler來生成規則包然後把它們部署到運行時環境。運行時環境只需要drool-core.jar和knowledge-api.jar來運行。

Rule Workbench(規則工作臺)

需要Eclipse3.4以上(GEF插件3.4以上)

要不然就是用JBoss的IDE,集成好了。

通過http://www.jboss.org/drools/downloads.html 鏈接找到對應的Drools plug-in安裝地址。

Drools運行時:

這裏的運行時表示的是jar包集合,其實就是下載的不同版本的Drools。Eclipse需要一個默認的的Drools運行時。

從源代碼構建Drools:

Drools和jBPM使用Git來做版本控制。鏈接爲:https://github.com/droolsjbpm

比如guvnor子項目,build方法如下:

$ git clone [email protected]:droolsjbpm/guvnor.git
...
$ cd guvnor
$ mvn clean install -DskipTests -Dfull
...
從6.0開始KIE,Drools(包括工作臺),jBPM(包括設計器和控制檯),OptaPlanner將共享相同的版本號。

KIE是什麼?

KIE是一個被Drools和jBPM共享的底層庫,它提供一個統一的基本方法、編程模型來構建,部署且提供了工具集。

KIE的結構:



OptaPlanner是一個本地搜索和優化工具,它現在是一個和Drools和jBPM同樣的頂級項目。

Dashboard Builder是一個強大的報表工具。它獨立於Drools和jBPM。

UberFire是工作臺項目的基礎組件,他提供了類似Eclipse樣式的工作臺能力(比如插件)。它也是獨立於Drools和jBPM。

Guvnor在5.0裏面承擔了太多的職責。在6.0裏面它將專注於封裝UberFire插件用戶構建web的IDE。

Drools和jBPM工作臺的發行版本使用UberFire作爲基礎然後加上一些插件Guvnor以及Drools、jBPM自己的插件像decision table,guided editor,BPMN2 designer,human task。


KIE的生命週期

編輯

用可視化工具例如DRL BPMN2,decision table, class models等 編輯知識庫(knowledge)

構建

把上一步編輯的知識庫構建爲部署單元,對於KIE這個部署單元就是jar。

測試

把jar部署到應用前請測試

部署

把jar部署到一個應用可以使用的位置

KIE使用maven風格的倉庫

使用(Utilize)

加載jar然後提供一個KieSession對象,這樣應用就可以和它交互了。

運行

系統通過KieSession的API和它交互

工作

用戶通過UI或者命令行調用到它

管理

管理所有的KIESession或者KIEContainer


構建,部署,使用和運行

6.0引入了一個新配合和方法來build知識庫,而5.0是用編程的方式,當然這個編程的方式爲了向後兼容還是可用的。

KIE項目或者模塊其實就是一個Maven的項目或者模塊,僅僅在META-INF目錄下面多了一個kmodule.xml。這個文件是用來描述選擇那些知識庫和配置知識庫的session。它可以通過spring或者OSGI BluePrints來提供xml支持。


雖然maven可以構建和部署KIE項目,但有個插件,它會生成很多類文件,可以提供校驗功能並且運行速度會更快。

示例圖:



org.kie.api.core.builder內容


KieContainer


Example 4.2. 創建KieContainer

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();

KieService


kmodule.xml是用來聲明KieBase和KIESession定義的地方。

KieBase包括rules,processes,functions和 type models。

KieBase中不包含數據

從KieBase中創建的KieSession則包含數據,KieBase的創建是重量級的東西,而KIESession是輕量級的東西。

KieContainer有機制會自動緩存住KieBase,最終用戶不用擔心這個問題


KieBase


KieSession也可以直接從kmodule.xml中定義的KieContainer中直接創建。



Example 4.3. kmodule.xml中配置KieBase示例

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://jboss.org/kie/6.0.0/kmodule">
  <configuration>
    <property key="drools.evaluator.supersetOf" value="org.mycompany.SupersetOfEvaluatorDefinition"/>
  </configuration>
  <kbase name="KBase1" default="true" eventProcessingMode="cloud" equalsBehavior="equality" declarativeAgenda="enabled" packages="org.domain.pkg1">
    <ksession name="KSession2_1" type="stateful" default="true"/>
    <ksession name="KSession2_2" type="stateless" default="false" beliefSystem="jtms"/>
  </kbase>
  <kbase name="KBase2" default="false" eventProcessingMode="stream" equalsBehavior="equality" declarativeAgenda="enabled" packages="org.domain.pkg2, org.domain.pkg3" includes="KBase1">
    <ksession name="KSession3_1" type="stateful" default="false" clockType="realtime">
      <fileLogger file="drools.log" threaded="true" interval="10"/>
      <workItemHandlers>
        <workItemHandler name="name" type="org.domain.WorkItemHandler"/>
      </workItemHandlers>
      <listeners>
        <ruleRuntimeEventListener type="org.domain.RuleRuntimeListener"/>
        <agendaEventListener type="org.domain.FirstAgendaListener"/>
        <agendaEventListener type="org.domain.SecondAgendaListener"/>
        <processEventListener type="org.domain.ProcessListener"/>
      </listeners>
    </ksession>
  </kbase>
</kmodule>



Table 4.1. kbase 屬性

Attribute name Default value Admitted values Meaning
name none any The name with which retrieve this KieBase from the KieContainer. This is the only mandatory attribute.
includes none any comma separated list A comma separated list of other KieBases contained in this kmodule. The artifacts of all these KieBases will be also included in this one.
packages all any comma separated list By default all the Drools artifacts under the resources folder, at any level, are included into the KieBase. This attribute allows to limit the artifacts that will be compiled in this KieBase to only the ones belonging to the list of packages.
default false true, false Defines if this KieBase is the default one for this module, so it can be created from the KieContainer without passing any name to it. There can be at most one default KieBase in each module.
equalsBehavior identity identity, equality Defines the behavior of Drools when a new fact is inserted into the Working Memory. With identity it always create a new FactHandle unless the same object isn't already present in the Working Memory, while with equality only if the newly inserted object is not equal (according to its equal method) to an already existing fact.
eventProcessingMode cloud cloud, stream When compiled in cloud mode the KieBase treats events as normal facts, while in stream mode allow temporal reasoning on them.
declarativeAgenda disabled disabled, enabled Defines if the Declarative Agenda is enabled or not.

Example 4.4. 從KieContainer中解析出KieBases 和 KieSessions 

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();

KieBase kBase1 = kContainer.getKieBase("KBase1");
KieSession kieSession1 = kContainer.newKieSession("KSession2_1");
StatelessKieSession kieSession2 = kContainer.newStatelessKieSession("KSession2_2");



因爲KSession2_1 和 KSession2_2 是不同的類型,一個是stateful,一個是stateless。所以他們使用的方法是不同的,如果用錯了會拋出RunTimeException。


使用Maven來構建

KIE插件使得artifact會被校驗和預編譯,所以建議一直使用這個插件。如下圖

Example 4.7. 在pom.xml中增加KIE plugin 


  <build>
    <plugins>
      <plugin>
        <groupId>org.kie</groupId>
        <artifactId>kie-maven-plugin</artifactId>
        <version>${project.version}</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>
  </build>        
      

用程序來定義KieModule

其實也可以用程序來定義KieModule裏面的KieBase和KieSession。要做到這一點需要先創建一個KieFileSystem。它是一個虛擬的文件系統,然後把項目中的所有資源添加進去。


像其他的核心組件一樣,你也可以從KieServices中獲取KieFileSystem。



Example 4.8. 用編程方式達到 kmodule.xml 同樣效果的例子

KieServices kieServices = KieServices.Factory.get();
KieModuleModel kieModuleModel = kieServices.newKieModuleModel();

KieBaseModel kieBaseModel1 = kieModuleModel.newKieBaseModel( "KBase1 ")
        .setDefault( true )
        .setEqualsBehavior( EqualityBehaviorOption.EQUALITY )
        .setEventProcessingMode( EventProcessingOption.STREAM );

KieSessionModel ksessionModel1 = kieBaseModel1.newKieSessionModel( "KSession1" )
        .setDefault( true )
        .setType( KieSessionModel.KieSessionType.STATEFUL )
        .setClockType( ClockTypeOption.get("realtime") );

KieFileSystem kfs = kieServices.newKieFileSystem();


下面還要給KieFileSystem加上其他必須的artifacts:

Example 4.9. Adding Kie artifacts to a KieFileSystem

KieFileSystem kfs = ...
kfs.write( "src/main/resources/KBase1/ruleSet1.drl", stringContainingAValidDRL )
        .write( "src/main/resources/dtable.xls",
                kieServices.getResources().newInputStreamResource( dtableFileStream ) );
例子裏面顯示增加Kie artifact可以通過普通的String也可以通過Resources。


KieResources


Resources的類型可以通過擴展名來推斷,也可以通過ResourceType來指定,如下:

Example 4.10. Creating and adding a Resource with an explicit type

KieFileSystem kfs = ...
kfs.write( "src/main/resources/myDrl.txt",
           kieServices.getResources().newInputStreamResource( drlStream )
                      .setResourceType(ResourceType.DRL) );

將所有資源都加載到KieFileSystem,然後把KieFileSystem傳遞給KieBuilder來構建。

KieBuilder


當KieFileSystem裏面的內容被構建好了之後,運行結果KieModule會自動被加入KieRepository,KieRepository是一個單例。


也可以在這之後用ReleaseId通過KieServices來獲得KieContainer。例如:

Example 4.11. Building the contents of a KieFileSystem and creating a KieContainer

KieServices kieServices = KieServices.Factory.get();
KieFileSystem kfs = ...
kieServices.newKieBuilder( kfs ).buildAll();
KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());

在這時候就可以獲得KieBase和創建KieSession了,跟之前的方法類似。

最佳實踐顯示,最好是檢查一下KieBuilder的編譯結果。

Example 4.12. Checking that a compilation didn't produce any error

KieBuilder kieBuilder = kieServices.newKieBuilder( kfs ).buildAll();
assertEquals( 0, kieBuilder.getResults().getMessages( Message.Level.ERROR ).size() );
修改默認的構建行爲

默認情況當有一個新的同名規則被加入的時候,它會替代老的規則,並打印INFO日誌。大部分情況是OK的,但是有些用戶想阻止這種行爲並報ERROR日誌。例子如下:

Example 4.13. Setting the severity using properties

// sets the severity of rule updates
drools.kbuilder.severity.duplicateRule = <INFO|WARNING|ERROR>
// sets the severity of function updates
drools.kbuilder.severity.duplicateFunction = <INFO|WARNING|ERROR>

4.2.3 部署

4.2.3.1 KieBase

KieBase是一個應用的知識定義集合的倉庫。它包含Rules,processes,functions和type models。KieBase本身不包含任何數據。由KieBase創建的Session可以用來插入數據,然後用Session來啓動進程實例。KieBase可以從KieContainer(包含了KieModule)中取到。


有時候,在OSGI環境中,KieBase需要解析不在默認類加載器中定義的類型。這時候就需要KieBaseConfiguration,它有一個額外的類加載器,當KieContainer創建KieBase的時候可以傳遞給它。

Example 4.14. Creating a new KieBase with a custom ClassLoader

KieServices kieServices = KieServices.Factory.get();
KieBaseConfiguration kbaseConf = kieServices.newKieBaseConfiguration( null, MyType.class.getClassLoader() );
KieBase kbase = kieContainer.newKieBase( kbaseConf );

4.2.3.2 KieSession和 KieBase修改

KieSession會在“運行”章節詳細討論。KieBase創建了KieSession對象並且保持了引用。當修改KieBase的時候這些改動也會被應用到KieSession中。這個引用默認是一個Java弱引用。當然有一個可選的boolean參數可以控制這個引用。

4.2.3.3 KieScanner

KieScanner 可以持續監控你的Maven Repository(不同於普通的maven)看看有沒有一個新的Kie項目被安裝。一個包含這個Kie項目的新的發佈版本會被髮布KieContainer中。KieScanner需要依賴kie-ci.jar包。


下面的例子顯示KieScanner怎麼註冊到KieContainer的:

Example 4.15. Registering and starting a KieScanner on a KieContainer

KieServices kieServices = KieServices.Factory.get();
ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "myartifact", "1.0-SNAPSHOT" );
KieContainer kContainer = kieServices.newKieContainer( releaseId );
KieScanner kScanner = kieServices.newKieScanner( kContainer );

// Start the KieScanner polling the Maven repository every 10 seconds
kScanner.start( 10000L );
在上面的例子裏KieScanner被配置爲按固定時間間隔啓動。也可以調用scanNow()函數啓動。如果KieScanner發現maven倉庫中有KieContainer用到的新版本項目,它會自動下載新版本並觸發一個增量構建。這時候KieContainer中的所有KieBase和KieSession都更新爲新版本了。

KieScanner只會選擇SNAPSHOT,版本範圍,或者LATEST設置中的修改。運行時指定了版本號,則不會被更新。


4.2.3.4 Maven的版本號和依賴

Maven有一些機制可以來管理版本號和應用。應用可以指定版本發佈。或則使用SNAPSHOT發佈。可以通過提供特定範圍的版本號或者使用SNAPSHOT機制。

StackOverFlow網站提供了一個非常好的解釋:

http://stackoverflow.com/questions/30571/how-do-i-tell-maven-to-use-the-latest-version-of-a-dependency 

如果你總是要使用最新版本,Maven有兩個重點可以用而不用使用版本範圍這個方法。你需要小心使用這些參數因爲你已經不再控制這些插件和依賴關係。

當你依賴一個插件或者依賴,你可以再version字段使用LATEST或者RELEASE。LATEST表示最新的發佈版本或者SNAPSHOT版本。RELEASE表示最新的RELEASE版本,不包含SNAPSHOT版本。總而言之,最好不要不設定具體的版本。廢話省略。

詳情請看Maven的POM的語法描述:

http://books.sonatype.com/mvnref-book/reference/pom-relationships-sect-pom-syntax.html

http://books.sonatype.com/mvnref-book/reference/pom-relationships-sect-project-dependencies.html


下面是一個例子:

<metadata>
  <groupId>com.foo</groupId>
  <artifactId>my-foo</artifactId>
  <version>2.0.0</version>
  <versioning>
    <release>1.1.1</release>
    <versions>
      <version>1.0</version>
      <version>1.0.1</version>
      <version>1.1</version>
      <version>1.1.1</version>
      <version>2.0.0</version>
    </versions>
    <lastUpdated>20090722140000</lastUpdated>
  </versioning>
</metadata>
如果一個版本是被要求依賴的話,如下寫:



<version>[1.0.1]</version>

顯示申明一個版本(除非版本衝突,否則總是這個版本):

<version>1.0.1</version>

(廢話不詳說)

4.2.3.5 Settings.xml和遠程倉庫安裝:

(廢話不詳說)


4.2.4 運行

4.2.4.1 KieBase

KieBase是應用知識庫。它包含規則,processes,函數,和類型模型。KieBase本身不包含任何數據。從KieBase中創建的session可以被插入數據,可以從session中創建processes。當KieBase定義的時候,KieBase可以從KieContainer中包含的KieModels中獲取。


Example 4.16. Getting a KieBase from a KieContainer

KieBase kBase = kContainer.getKieBase();
4.2.4.2 KieSession

KieSession保存和執行運行時數據。它是從KieBase中創建。


4.2.4.3 Kie運行時

kie運行時給Rules,processes提供全局設置、註冊channel等方法。


4.2.4.3.1.1 Globals(全局對象)

規則引擎可以看到Globals命名對象。但是改變Globals裏面的值不會重算Rules。Globals再提供靜態信息方面很有效。比如在規則的RHS中提供服務。或者接收從規則引擎返回的平均值。當你在LHS中使用Globals的時候,保證它不可變,或者至少改變不會影響規則的行爲。

global應該如下定義:

global java.util.List list

廢話省略

4.2.4.4 事件模型

event包提供了規則引擎的事件,比如規則被觸發或者對象被斷言。這樣就可以從應用的主流程中分離日誌或者監聽事件。

KieRuntimeEventManager


RuleRuntimeEventManager




續......

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章