JBoss Rules Study

JBoss Rules 学习(一): 什么是Rule

        学习JBoss Rules有几天了,因为这方面的中文资料较少,所以这几天都在看官网上的manual。这是一份不错的教程,我把我看的一些重要的东西翻译整理了一下,希望可以对想学习JBoss Rules的同学们提供一点帮助。
      
在开始这份教程之前,我先简要介绍一下JBoss Rules
       JBoss Rules
的前身是Codehaus的一个开源项目叫Drools。最近被纳入JBoss门下,更名为JBoss Rules,成为了JBoss应用服务器的规则引擎。
       Drools
是为Java量身定制的基于Charles  ForgyRETE算法的规则引擎的实现。具有了OO接口的RETE,使得商业规则有了更自然的表达。
 
      
既然JBoss Rules是一个商业规则引擎,那我们就要先知道到底什么是Rules,即规则。在JBoss Rules中,规则是如何被表示的

Rules

一条规则是对商业知识的编码。一条规则有 attributes ,一个 Left Hand Side LHS )和一个 Right Hand Side RHS )。 Drools 允许下列几种 attributes salience agenda-group no-loop auto-focus duration activation-group

rule “ < name > ”    
    
< attribute >   < value >
    
    when        
        
< LHS >
    
    then        
        
< RHS >

end


规则的 LHS 由一个或多个条件( Conditions )组成。当所有的条件( Conditions )都满足并为真时, RHS 将被执行。 RHS 被称为结果( Consequence )。 LHS RHS 类似于:

if  (  < LHS >  ) {
    
< RHS >

}

 

规则可以通过 package 关键字同一个命名空间( namespace )相关联;其他的规则引擎可能称此为规则集( Rule Set )。一个 package 声明了 imports global 变量, functions rules

 

package  com.sample

import
 java.util.List
import
 com.sample.Cheese

global List cheeses

function 
void
 exampleFunction(Cheese cheese) {
    System.out.println( cheese );
}

rule 
A Cheesy Rule
    when
        cheese : Cheese( type 
==   " stilton "  )
    then
        exampleFunction( cheese );
        cheeses.add( cheese );
end

对新的数据和被修改的数据进行规则的匹配称为模式匹配( Pattern Matching )。进行匹配的引擎称为推理机( Inference Engine )。被访问的规则称为 ProductionMemory ,被推理机进行匹配的数据称为 WorkingMemory Agenda 管理被匹配规则的执行。推理机所采用的模式匹配算法有下列几种: Linear RETE Treat Leaps

Drools 采用了 RETE Leaps 的实现。 Drools RETE 实现被称为 ReteOO ,表示 Drools Rete 算法进行了加强和优化的实现。

一条规则的 LHS Conditional Element 和域约束( Field Constraints )。下面的例子显示了对一个 Cheese Fact 使用了字面域约束( Literal Field Constraint

rule  " Cheddar Cheese "
    when
        Cheese( type 
==   " cheddar "  )
    then
        System.out.println( 
" cheddar "
 );
end

上面的这个例子类似于:

public   void  cheddarCheese(Cheese cheese) {
    
if  ( cheese.getType().equals( " cheddar "
) {
        System.out.println( 
" cheddar "
 );
    }
}

<!--[if !vml]--> <!--[endif]-->

规则引擎实现了数据同逻辑的完全解耦。规则并不能被直接调用,因为它们不是方法或函数,规则的激发是对 WorkingMemory 中数据变化的响应。结果( Consequence ,即 RHS )作为 LHS events 完全匹配的 Listener

rules 被加入 Productioin Memory 后, rules 被规则引擎用 RETE 算法分解成一个图:

Facts assert 进入 WorkingMemory 中后,规则引擎找到匹配的 ObjectTypeNode ,然后将此 Fact 传播到下一个节点。 ObjectTypeNode 拥有一块内存来保存所有匹配的 facts 。在我们的例子中,下一个节点是一个域约束( Field Constraint ), type = = “cheddar” 。如果某个 Cheese 对象的类型不是“ cheddar ”,这个 fact 将不会被传播到网络的下一个节点。如果是“ cheddar ”类型,它将被记录到 AlphaNode 的内存中,并传播到网络的下一个节点。 AlphaNode 是古典 RETE 术语,它是一个单输入 / 单输出的节点。最后通过 AlphaNode fact 被传播到 Terminal Node Terminal Node 是最终节点,到此我们说这条规则被完全匹配,并准备激发。

当一条规则被完全匹配,它并没有立刻被激发(在 RETE 中是这样,但在 Leaps 中它会立刻被激发)。这条规则和与其匹配的 facts 将激活被放入 Agenda ,由 Agenda 来负责安排激发 Activations (指的是 rule + the matched facts )。

下面的图很清楚的说明了 Drools 规则引擎的执行过程:

 

数据被 assert WorkingMemory 后,和 RuleBase 中的 rule 进行匹配(确切的说应该是 rule LHS ),如果匹配成功这条 rule 连同和它匹配的数据(此时就叫做 Activation )一起被放入 Agenda ,等待 Agenda 来负责安排激发 Activation (其实就是执行 rule RHS ),上图中的菱形部分就是在 Agenda 中来执行的, Agenda 就会根据冲突解决策略来安排 Activation 的执行顺序。

posted on 2006-06-01 16:52 guangnian 阅读(3200) 评论(8)  编辑 收藏 引用 所属分类: JBoss Rules(Drools)

<script type="text/javascript"> // </script>

评论

# re: JBoss Rules 学习(一): 什么是Rule 2006-06-01 18:06 江南白衣

好啊,又有人开始学习和中文化总结Drools了,密切关注中。  回复  更多评论   

# re: JBoss Rules 学习(一): 什么是Rule 2006-06-02 10: 19 C[ETI]O@quaffsoft

cheeses不需要new出来?  回复  更多评论   

# re: JBoss Rules 学习(一): 什么是Rule 2006-06-02 10:49 guangnian

@C[ETI]O@quaffsoft
你指的是global List cheeses 吗?对于Drools rulesglobal属性的用法我还不太清楚,但是有一点就是,上面的Drools rules例子是写在.drl文件中的,而不是写在.java中,所有不要太用java的观点来看那段例子。上面的package也是跟java不太一样的概念,用package关键字并不会创建相应的目录层次。  回复  更多评论   

# re: JBoss Rules 学习(一): 什么是Rule 2006-06-02 11:22 Water Ye

关于Globals的用法

java.util.Vector globalVector = new java.util.Vector( );
java.util.Map map = new java.util.HashMap( );
map.put( "vector", globalVector );
//Open a stateless Session StatelessRuleSession srs = (StatelessRuleSession) runtime.createRuleSession( "SistersRules", map, RuleRuntime.STATELESS_SESSION_TYPE );
...
// Persons added to List
// call executeRules( ) giving a List of Objects as parameter
// There are rules which will put Objects in the Vector
// fetch the vector from the map
v = (java.util.Vector)map.get("vector");  
回复  更多评论   

JBoss Rules 学习(二): RETE算法

JBoss Rules 学习(一):什么是Rule中,我们介绍了JBoss Rules中对Rule的表示,其中提到了JBoss Rule中主要采用的RETE算法来进行规则匹配。下面将详细的介绍一下RETE算法在JBoss Rule中的实现,最后随便提一下JBoss Rules中也可以使用的另一种规则匹配算法Leaps

1.Rete 算法

Rete 在拉丁语中是 ”net” ,有网络的意思。 RETE 算法可以分为两部分:规则编译( rule compilation )和运行时执行( runtime execution )。

编译算法描述了规则如何在 Production Memory 中产生一个有效的辨别网络。用一个非技术性的词来说,一个辨别网络就是用来过滤数据。方法是通过数据在网络中的传播来过滤数据。在顶端节点将会有很多匹配的数据。当我们顺着网络向下走,匹配的数据将会越来越少。在网络的最底部是终端节点( terminal nodes )。在 Dr Forgy 1982 年的论文中,他描述了 4 种基本节点: root , 1-input, 2-input and terminal 。下图是 Drools 中的 RETE 节点类型:

 

Figure 1. Rete Nodes

根节点( RootNode )是所有的对象进入网络的入口。然后,从根节点立即进入到 ObjectTypeNode ObjectTypeNode 的作用是使引擎只做它需要做的事情。例如,我们有两个对象集: Account Order 。如果规则引擎需要对每个对象都进行一个周期的评估,那会浪费很多的时间。为了提高效率,引擎将只让匹配 object type 的对象通过到达节点。通过这种方法,如果一个应用 assert 一个新的 account ,它不会将 Order 对象传递到节点中。很多现代 RETE 实现都有专门的 ObjectTypeNode 。在一些情况下, ObjectTypeNode 被用散列法进一步优化。

Figure 2 . ObjectTypeNodes

ObjectTypeNode 能够传播到 AlphaNodes, LeftInputAdapterNodes BetaNodes

1-input 节点通常被称为 AlphaNode AlphaNodes 被用来评估字面条件( literal conditions )。虽然, 1982 年的论文只提到了相等条件(指的字面上相等),很多 RETE 实现支持其他的操作。例如, Account.name = = “Mr Trout” 是一个字面条件。当一条规则对于一种 object type 有多条的字面条件,这些字面条件将被链接在一起。这是说,如果一个应用 assert 一个 account 对象,在它能到达下一个 AlphaNode 之前,它必须先满足第一个字面条件。在 Dr. Forgy 的论文中,他用 IntraElement conditions 来表述。下面的图说明了 Cheese AlphaNode 组合( name = = “cheddar” strength = = “strong” ):

Figure 3. AlphaNodes

Drools 通过散列法优化了从 ObjectTypeNode AlphaNode 的传播。每次一个 AlphaNode 被加到一个 ObjectTypeNode 的时候,就以字面值( literal value )作为 key ,以 AlphaNode 作为 value 加入 HashMap 。当一个新的实例进入 ObjectTypeNode 的时候,不用传递到每一个 AlphaNode ,它可以直接从 HashMap 中获得正确的 AlphaNode ,避免了不必要的字面检查。

<!--[if !supportEmptyParas]-->

2-input 节点通常被称为 BetaNode Drools 中有两种 BetaNode JoinNode NotNode BetaNodes 被用来对 2 个对象进行对比。这两个对象可以是同种类型,也可以是不同类型。

我们约定 BetaNodes 2 个输入称为左边( left )和右边( right )。一个 BetaNode 的左边输入通常是 a list of objects 。在 Drools 中,这是一个数组。右边输入是 a single object 。两个 NotNode 可以完成‘ exists ’检查。 Drools 通过将索引应用在 BetaNodes 上扩展了 RETE 算法。下图展示了一个 JoinNode 的使用:

Figure 4 . JoinNode

 

注意到图中的左边输入用到了一个 LeftInputAdapterNode ,这个节点的作用是将一个 single Object 转化为一个单对象数组( single Object Tuple ),传播到 JoinNode 节点。因为我们上面提到过左边输入通常是 a list of objects

<!--[if !supportEmptyParas]-->

Terminal nodes 被用来表明一条规则已经匹配了它的所有条件( conditions )。 在这点,我们说这条规则有了一个完全匹配( full match )。在一些情况下,一条带有“或”条件的规则可以有超过一个的 terminal node

Drools 通过节点的共享来提高规则引擎的性能。因为很多的规则可能存在部分相同的模式,节点的共享允许我们对内存中的节点数量进行压缩,以提供遍历节点的过程。下面的两个规则就共享了部分节点:

rule
    when
        Cheese( $chedddar : name 
==   " cheddar "  )
        $person : Person( favouriteCheese 
==
 $cheddar )
    then
        System.out.println( $person.getName() 
+   "  likes cheddar "
 );
end

 

rule
    when
        Cheese( $chedddar : name 
==   " cheddar "  )
        $person : Person( favouriteCheese 
!=
 $cheddar )
    then
        System.out.println( $person.getName() 
+   "  does likes cheddar "
 );
end

这里我们先不探讨这两条 rule 到的是什么意思,单从一个直观的感觉,这两条 rule 在它们的 LHS 中基本都是一样的,只是最后的 favouriteCheese ,一条规则是等于 $cheddar ,而另一条规则是不等于 $cheddar 。下面是这两条规则的节点图:

Figure 5 . Node Sharing

从图上可以看到,编译后的 RETE 网络中, AlphaNode 是共享的,而 BetaNode 不是共享的。上面说的相等和不相等就体现在 BetaNode 的不同。然后这两条规则有各自的 Terminal Node

<!--[if !supportEmptyParas]-->

RETE 算法的第二个部分是运行时( runtime )。当一个应用 assert 一个对象,引擎将数据传递到 root node 。从那里,它进入 ObjectTypeNode 并沿着网络向下传播。当数据匹配一个节点的条件,节点就将它记录到相应的内存中。这样做的原因有以下几点:主要的原因是可以带来更快的性能。虽然记住完全或部分匹配的对象需要内存,它提供了速度和可伸缩性的特点。当一条规则的所有条件都满足,这就是完全匹配。而只有部分条件满足,就是部分匹配。(我觉得引擎在每个节点都有其对应的内存来储存满足该节点条件的对象,这就造成了如果一个对象是完全匹配,那这个对象就会在每个节点的对应内存中都存有其映象。)

2. Leaps 算法:

Production systems Leaps 算法使用了一种“ lazy ”方法来评估条件( conditions )。一种 Leaps 算法的修改版本的实现,作为 Drools v3 的一部分,尝试结合 Leaps RETE 方法的最好的特点来处理 Working Memory 中的 facts

古典的 Leaps 方法将所有的 asserted facts ,按照其被 asserted Working Memory 中的顺序( FIFO ),放在主堆栈中。它一个个的检查 facts ,通过迭代匹配 data type facts 集合来找出每一个相关规则的匹配。当一个匹配的数据被发现时,系统记住此时的迭代位置以备待会的继续迭代,并且激发规则结果( consequence )。当结果( consequence )执行完成以后,系统就会继续处理处于主堆栈顶部的 fact 。如此反复。

JBoss Rules 学习(三): Drools规则引擎 (上)

从今天开始,我们将分两期来详细的介绍Drools规则引擎的原理,和各关键类的使用方法。

Drools 规则引擎()


1. 概述 :
Drools 分为两个主要部分:构建( Authoring )和运行时( Runtime
)。

构建的过程涉及到
.drl .xml 规则文件的创建,它们被读入一个解析器,使用 ANTLR 3 语法进行解析。解析器对语法进行正确性的检查,然后产生一种中间结构“ descr ”, descr AST 来描述规则。 AST 然后被传到 PackageBuilder ,由 PackagBuilder 来产生 Packaged 对象。 PackageBuilder 还承担着一些代码产生和编译的工作,这些对于产生 Package 对象都时必需的。 Package 对象是一个可以配置的,可序列化的,由一个或多个规则组成的对象。下图阐明了上述过程:

 
Figure 1.1
Authoring Components

 

RuleBase 是一个运行时组件,它包含了一个或多个 Package 对象。可以在任何时刻将一个 Package 对象加入或移出 RuleBase 对象。一个 RuleBase 对象可以在任意时刻实例化一个或多个 WorkingMemory 对象,在它的内部保持对这些 WorkingMemory 的弱引用。 WorkingMemory 由一系列子组件组成。当应用程序中的对象被 assert WorkingMemory ,可能会导致一个或多个 Activation 的产生,然后由 Agenda 负责安排这些 Activation 的执行。下图说明了上述过程:

 

Figure 1.2 . Runtime Components

 

2.构建(Authoring):

主要有三个类用来完成构建过程:DrlParser, XmlParser PackageBuilder。两个解析器类从传入的Reader实例产生descr AST模型。PackageBuilder提供了简便的API,使你可以忽略那两个类的存在。这两个简单的方法是:“addPackageFromDrl”和“addPackageFromXml”,两个都只要传入一个Reader实例作为参数。下面的例子说明了如何从classpath中的xmldrl文件创建一个Package对象。注意:所有传入同一个PackageBuilder实例的规则源,都必须是在相同的package 命名空间(namespace)中。

PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( 
new
 InputStreamReader( getClass().getResourceAsStream( "package1.drl" ) ) );
builder.addPackageFromXml( 
new
 InputStreamReader( getClass().getResourceAsStream( "package2.drl" ) ) );
Package pkg = builder.getPackage();


Figure 2.1 PackageBuilder

PackageBuilder是可以配置的,使用PackageBuilderConfiguration。通常,你可以指定另一个parent ClassLoader和用什么编译器(compiler),默认是Eclipse JDT。下面显示了如何指定JANINO编译器:

PackageBuilderConfiguration conf = new PackageBuilderConfiguration();
conf.setCompiler( PackageBuilderConfiguration.JANINO );
PackageBuilder builder = 
new PackageBuilder( conf );


Figure 2.2 . PackageBuilderConfiguration


3
RuleBase:


Figure 3.1 . RuleBase

一个RuleBase包含了多个将被使用的规则包(packages of rules)。一个RuleBase是可以序列化的,所以它可以被配置到JNDI或其他类似的服务。通常,第一次使用时,一个RuleBase被创建并缓存。RuleBaseRuleBaseFactory来实例化,默认返回一个ReteOO RuleBase。可以传入参数来指定采用ReteOOLeaps。然后,用addPackage方法加入Package实例。你可以加入有相同命名空间(namespace)的多个Package

RuleBase ruleBase  = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(pkg);


Figure 3.2. RuleBaseFactory

一个 rulebase instance 是线程安全的,所有你可以在你的应用中,让一个 rulebase instance 在多个线程中共享。对于一个 rulebase 的最通常的操作是产生一个新的 WorkingMemory

这个 rulebase 保持着到它所产生的 WorkingMemoryd 的弱引用,所以在长时间运行的 WorkingMemory 中,如果 rules 发生改变,这些 WorkingMemory 可以即使的根据最新的 rules 进行更新,而不必重启 WorkingMemory 。你也可以指定 RuleBase 不必保持一个弱引用,但是你要保证 RuleBase 不用更新。

ruleBase.newWorkingMemory();   //  maintains a weak reference.
ruleBase.newWorkingMemory(  false  );  //  do not maintain a weak reference

任何时候, Package 可以被加入或移除;所有的改变都会被反映到现存的 WorkingMemory 中。不要忘了调用 fireAllRules() Activations 激发。

ruleBase.addPackage( pkg  );   //  Add a package instance
ruleBase.removePackage(  " org.com.sample "   );   //  remove a package, and all its parts, by it's namespace
ruleBase.removeRule(  " org.com.sample " " my rule "  );  //  remove a specific rule from a namespace

虽然有删除一个单独规则的方法,但是却没有加入一个单独规则的方法(要达到这个目的只有加入一个只有一条规则的 package )。

<!--[if !supportEmptyParas]-->

RuleBaseConfigurator 可以指定 RuleBase 的附加行为。在加入 RuleBase 后, RuleBaseConfiguration 就变成不可变对象。

RuleBaseConfiguration conf  =   new  RuleBaseConfiguration();

conf.setProperty( RuleBaseConfiguration.PROPERTY_ASSERT_BEHAVIOR,
                  RuleBaseConfiguration.WM_BEHAVIOR_EQUALITY );

RuleBase ruleBase 
=   new  ReteooRuleBase( conf );

两个主要的属性是: PROPERT_ASSERT_BEHAVIOR PROPERTY_LOGICAL_OVERRIDE_BEHAVIOR (在以后的部分中会解释)。所有的属性值都是 RuleBaseConfiguration 类中的静态域常量。


Figure 3.3 RuleBaseConfiguration


 

Drools 规则引擎()

4 WorkingMemory:


Figure 4.1 WorkingMemory

WorkingMemory 是运行时规则引擎的主要类。它保持了所有被 asserted WorkingMemory 的数据的引用,直到取消( retracted )。 WorkingMemory 是有状态对象。它们的生命周期可长可短。如果从一个短生命周期的角度来同一个引擎进行交互,意味着你可以使用 RuleBase 对象来为每个 session 产生一个新的 WorkingMemory ,然后在结束 session discard 这个 WorkingMemory (产生一个 WorkingMemory 是一个廉价的操作)。另一种形式,就是在一个相当长的时间中(例如一个 conversation ),保持一个 WorkingMemory ,并且对于新的 facts 保持持续的更新。当你希望 dispose 一个 WorkingMemory 的时候,最好的实践就是调用 dispose() 方法,此时 RuleBase 中对它的引用将会被移除(尽管这是一个弱引用)。不管怎样最后它将会被当成垃圾收集掉。术语 WorkingMemory Actions 代表了对 WorkingMemory assertions retractions modifications

4.1 Facts

Facts 是从你的应用中,被 assert WorkingMemory 中的对象( beans )。 Facts 是规则可以访问的任意的 java 对象。规则引擎中的 facts 并不是“ clone facts ,它只是持有到你的应用中数据的引用。 Facts 是你的应用数据。 String 和其他没有 getter setter 的类不是有效的 Fact 。这样的类不能使用域约束( Field Constraints ),因为使用域约束要依靠 JavaBean 标准的 getter setter 来同对象交互。

4.2 Assertion

“Assertion” 是将 facts 告诉 WorkingMemory 的动作,例如 WorkingMemory.assertObject (yourObject) 。当你 assert 一个 fact ,它将被检查是否匹配规则。这意味着所有的匹配工作将会在 assert 的过程中完成。尽管如此,当你完成 assert facts 之后,你还要调用“ fireAllRules() ”方法来执行规则。

当一个对象被 assert 后,会返回一个 FactHandle 。这个 FactHandle 是一个代表在 Working Memory 中你的 asserted Object 的令牌( token )。当你希望 retract 或者 modify 一个对象的时候,这个令牌让你用来同 WorkingMemory 进行交互。

Cheese stilton  =   new  Cheese( " stilton " );
FactHandle stiltonHandle 
=  workingMemory.assertObject( stilton );

 

WorkingMeomry 有两种 assertion 模式: Equality Identity (默认是 Identity )。

Identity 模式下 WorkingMemory 使用一个 IdentityHashMap 来存储所有的 asserted Objects 。这个模式下,当 asserted Object 是同一个实例时,它返回同一个 FactHandle

Equality 模式下 WorkingMemory 使用一个 HashMap 来存储所有的 asserted Objects 。这个模式下,当 asserted Object 相等时,它返回同一个 FactHandle

WorkingMemory.assertObject(yourObjcet) 只是进行 assertion 的一种 regular 方法,还存在有一种称为 logical assertion 的动作)。

4.3 Retraction

基本上就是 assert 的逆操作。当你 retract 一个 fact WorkingMemory 将不再跟踪那个 fact 。任何被 activated 并依赖那个 fact 的规则将被取消。注意:完全有可能存在某条规则是依赖于一个 fact 的“不存在”( non existence )。在这种情况下, retract 一个 fact 将导致一条规则被激活。对一个 Fact 进行 Retraction ,必须用 assert 时返回的那个 FactHandle 做为参数。

Cheese stilton  =   new  Cheese( " stilton " );
FactHandle stiltonHandle 
=
 workingMemory.assertObject( stilton );
.
workingMemory.retractObject( stiltonHandle );

 

4.4 Modification

当一个 Fact 被修改了,会通知规则引擎进行重新处理。在规则引擎内部实际上是对旧的 Fact 进行 retract ,然后对新的 Object 再进行 assert 。要使用 modifyObject() 方法来通知 Working Memory ,被改变的 Object 并不会自己通知规则引擎。注意: modifyObject() 方法总是要把被修改的 Object 做为第二参数,这就允许你把一个不可变对象替换为另一个新对象。

Cheese stilton  =   new  Cheese( " stilton " );
FactHandle stiltonHandle 
=
 workingMemory.assertObject( stilton );
.
stilton.setPrice( 
100
 );
workingMemory.modifyObject( stiltonHandle, stilton );

 

4.5 Globals

Global 是一个能够被传进 WorkingMemory 但不需要 assert 的命名对象。大多数这些对象被用来作为静态信息或服务。这些服务被用在一条规则的 RHS ,或者可能是从规则引擎返回对象的一种方法。

List list  =   new  ArrayList();
workingMemory.setGlobal(
" list " , list);

 

setGlobal() 方法传进去的命名对象必须同 RuleBase 中所定义的具有相同的类型(就是要同你的规则文件中用 Global 关键字所定义的类型相同),否则会抛出一个 RuntimeException 。如果一条规则在你 setGlobal 之前调用了定义的 Global ,会抛出一个 NullPointerException

4.6 Property Change Listener

如果你的 fact 对象是 JavaBean ,你可以为它们实现一个 property change listener ,然后把它告诉规则引擎。这意味着,当一个 fact 改变时,规则引擎将会自动知道,并进行响应的动作(你不需要调用 modifyObject() 方法来通知 WorkingMemory )。 Proxy libraries 将会帮助实现这一切。要让 Property Change Listener 生效,还要将 fact 设置为动态( dynamic )模式,通过将 true 做为 assertObject() 方法的第二个参数来实现:

Cheese stilton  =   new  Cheese( " stilton " );
FactHandle stiltonHandle 
=  workingMemory.assertObject( stilton,  true  );   // specifies t hat this is a dynamic fact

 

然后要在 JavaBean 中加入一个 PropertyChangeSupport 实例,和两个方法: addPropertyChangeListener() removePropertyChangeListener() 。最后要在 JavaBean setter 方法中通知 PropertyChangeSupport 所发生的变化。示例代码如下:

private   final  PropertyChangeSupport changes  =   new  PropertyChangeSupport(  this  );

public   void  addPropertyChangeListener( final
 PropertyChangeListener l) {
    
this
.changes.addPropertyChangeListener( l );
}

public   void  removePropertyChangeListener( final
 PropertyChangeListener l) {
    
this
.changes.removePropertyChangeListener( l );
}

public   void  setState( final
 String newState) {
    String oldState 
=   this
.state;
    
this .state  =
 newState;
this .changes.firePropertyChange(  " state " , oldState, newState );


5. Agenda:


Figure 5.1 . Agenda

Agenda RETE 的一个特点。在一个 WorkingMemory Action 发生时,可能会有多条规则发生完全匹配。当一条规则完全匹配的时候,一个 Activation 就被创建(引用了这条规则和与其匹配的 facts ),然后放进 Agenda 中。 Agenda 通过使用冲突解决策略( Conflict Resolution Strategy )来安排这些 Activations 的执行。

引擎工作在一个“ 2 阶段”模式下:

<!--[if !supportLists]--> 1  <!--[endif]--> WorkingMemory Actions assert 新的 facts ,修改存在的 facts retract facts 都是 WorkingMemory Actions 。通过在应用程序中调用 fireAllRules() 方法,会使引擎转换到 Agenda Evaluatioin 阶段。

<!--[if !supportLists]--> 2  <!--[endif]--> Agenda Evaluation :尝试选择一条规则进行激发( fire )。如果规则没有找到就退出,否则它就尝试激发这条规则,然后转换到 WorkingMemory Actions 阶段,直到 Agenda 中为空。

这个过程一直重复,直到 Agenda 是空的,此时控制权就回到应用程序中。。当 WorkingMemory Actions 发生时,没有规则正在被激发。

下图说明了这个循环的过程:

Figure  5.2 . Two Phase Execution

5 1 Conflict Resultion

当有多条 rules agenda 中,就需要解决冲突。当激发一条规则时,会对 WorkingMemory 产生副作用。规则引擎需要知道规则要以什么顺序来激发(例如,激发 rule A 可能会引起 rule B 被从 agenda 中移除。)

Drools 采取的冲突解决策略有 2 种,按照优先级排列如下: Salience LIFO (后进先出)。最易懂的策略是“ Salience ”,即优先级, user 可以为某个 rule 指定一个高一点的优先级(通过附给它一个比较大的数字)。高 Salience rule 将会被优先激发。

5 2 Agenda Groups

Agenda Groups 是划分 Agenda rules (其实是“ activations ”)的一种方法。在任意一个时刻,只有一个 group 拥有“ focus ”,这意味着只有在那个 group 中的 activations 才是有效的。

Agenda Groups 是在 grouped rules 之间创建一个“流”( flow )的简便的方法。你可以在规则引擎中,或是用 API 来切换具有焦点的组。如果你的规则有很明确的多“阶段”( phases )或多“序列”( sequences )的处理,可以考虑用 Agenda Groups 来达到这个目的。

每次调用 setFocus() 方法的时候,那个 Agenda Group 就会被压入一个堆栈,当这个有焦点的组为空时,它就会被弹出,然后下一个组就会被执行。一个 Agenda Group 可以出现在堆栈的多个位置。默认的 Agenda Group 是“ MAIN ”,所有没有被指定 Agenda Group Activations 都被放到那个组中,这个组总是被放在堆栈的第一个组,并默认给予焦点。

5 3  Agenda Filters


Figure 5.3. Agenda Filter

Filter 必须实现 AgendaFilter 接口,用来允许或禁止一个 activation 能够被激发。 Drools 提供了下面几种方便的默认实现:

<!--[if !supportLists]--> ·         <!--[endif]--> RuleNameEndWithAgendaFilter

<!--[if !supportLists]--> ·         <!--[endif]--> RuleNameEqualsAgendaFilter

<!--[if !supportLists]--> ·         <!--[endif]--> RuleNameStartsWithAgendaFilter

要使用一个 filter 就要在调用 fireAllRules() 方法的时候指定它。下面的例子将对所有名字以“ Test ”结尾的规则进行过滤:

workingMemory.fireAllRules(  new  RuleNameEndsWithAgendaFilter(  " Test "  ) );

 

6.事件模型( Event Model

Event 包里提供了规则引擎的事件机制,包括规则激发,对象被 asserted 等等。你可以使用事件机制来进行 AOP 编程。

有两种类型的 Event Listener WorkingMemoryEventListener AgendaEventListener


Figure 6.1. WorkingMemoryEventListener


Figure 6.2  AgendaEventListener

 

对两个 EventListener 接口都提供了默认实现,但在方法中并没有做任何事。你可以继承这两个默认实现来完成你自己的实现- DefaultAgendaEventListener DefaultWorkingMemoryEventListener 。下面代码说明了如何扩展一个 DefaultAgendaEventListner 并把它加到 WorkingMemory 中,例子中只完成了 afterActivationFired() 方法:

workingMemory.addEventListener(  new  DefaultAgendaEventListener() {                            
   
public   void
 afterActivationFired(AfterActivationFiredEvent event) {
       
super
.afterActivationFired( event );
       System.out.println( event );
   }
   
});

Drools 也提供了 DebugWorkingMemoryEventListener DebugAgendaEventListener 两个实现类,在这两个类的方法中实现了 debug 信息的输出:

workingMemory.addEventListener(  new  DebugWorkingMemoryEventListener() );



JBoss Rules 学习(五): JBoss Rules 3.0.1 类库介绍

下载地址:

http://labs.jboss.com/portal/index.html?ctrl:id=page.default.downloads&project=jbossrules

下载文件说明:

JBoss Rules 3.0.1 Binaries (includes javadocs)  13MB )― 仅仅包含 JBoss Rules 的四个核心类库:

<!--[if !supportLists]--> l         <!--[endif]--> drools-core.jar - 核心引擎,运行时组件。包含了 RETE 引擎和 LEAPS 引擎;

<!--[if !supportLists]--> l         <!--[endif]--> drools-compiler.jar - 规则文件的编译组件,构建可执行的 RuleBase

<!--[if !supportLists]--> l         <!--[endif]--> drools-jsr94.jar - 提供了 JSR-94 的兼容实现,本质上是 drools- compiler 组件的包裹层。注意:由于 JSR94 规约的限制,不是所有的特点都可以通过此接口暴露。

<!--[if !supportLists]--> l         <!--[endif]--> drools-decisiontables.jar - 决策表的“编译”组件(使用了 drools- compiler 组件)。支持 excel CSV 输入格式。

JBoss Rules 3.0.1 Binaries with dependencies (includes javadocs) 23 MB )- 包含了 JBoss Rules 的核心类库和它们的 dependencies

<!--[if !supportLists]--> l         <!--[endif]--> antlr- 2.7.6 .jar

<!--[if !supportLists]--> l         <!--[endif]--> antlr-3.0ea8.jar

<!--[if !supportLists]--> l         <!--[endif]--> colt- 1.2.0 .jar

<!--[if !supportLists]--> l         <!--[endif]--> commons-collections-3.1.jar

<!--[if !supportLists]--> l         <!--[endif]--> commons-io-1.1.jar

<!--[if !supportLists]--> l         <!--[endif]--> commons-jci-core-1.0-406301.jar

<!--[if !supportLists]--> l         <!--[endif]--> commons-jci-eclipse- 3.2.0 .666.jar

<!--[if !supportLists]--> l         <!--[endif]--> commons-jci-janino- 2.4.3 .jar

<!--[if !supportLists]--> l         <!--[endif]--> commons-lang-2.1.jar

<!--[if !supportLists]--> l         <!--[endif]--> commons-logging-api- 1.0.4 .jar

<!--[if !supportLists]--> l         <!--[endif]--> concurrent- 1.3.4 .jar

<!--[if !supportLists]--> l         <!--[endif]--> core- 3.2.0 .666.jar

<!--[if !supportLists]--> l         <!--[endif]--> janino- 2.4.3 .jar

<!--[if !supportLists]--> l         <!--[endif]--> jsr94-1.1.jar

<!--[if !supportLists]--> l         <!--[endif]--> jung- 1.7.2 .jar

<!--[if !supportLists]--> l         <!--[endif]--> junit- 3.8.1 .jar

<!--[if !supportLists]--> l         <!--[endif]--> poi- 2.5.1 -final-20040804.jar

<!--[if !supportLists]--> l         <!--[endif]--> stringtemplate-2.3b6.jar

<!--[if !supportLists]--> l         <!--[endif]--> xercesImpl- 2.6.2 .jar

<!--[if !supportLists]--> l         <!--[endif]--> xml-apis-1.0.b2.jar

<!--[if !supportLists]--> l         <!--[endif]--> xpp3- 1.1.3 .4.0.jar

<!--[if !supportLists]--> l         <!--[endif]--> xstream- 1.1.3 .jar

如果你运行在 Java 1.5 环境下,有一些类库,例如 XML libraries ,可以不需要。需要注意的类库有:

JCI ”-这是 Apache Java Compiler Interface , 提供了运行时编译能力。可以通过 PackageBuilderConfiguration 实例来设定采用 eclipse janino 编译器,默认是 eclipse

POI ”-提供了解析 Excel 文件的能力;

antlr ”-提供了解析规则语言的能力。

JBoss Rules IDE 3.0.1 13 MB )- 这是 JBoss Rules Eclipse 插件,只支持 Eclipse 3.2 或以上版本。它提供了运行 JBoss Rules 的所有 dependencies 。你可以创建一个 Rule Project ,它能够为你编写规则文件提供自动完成的功能,并且它为你提供了 Agenda view WorkingMemory view Global Data view ,使你可以通过 eclipse 视图很清楚的看到 Agenda WorkingMemory Global Data 的情况。

你还可以通过 update site 来自动安装这个插件 ,URL 是:

http://anonsvn.labs.jboss.com/labs/jbossrules/updates/drools-ide-update/

<!--[if !supportEmptyParas]--> <!--[endif]-->

JBoss Rules 学习(六): Drools规则语言详解(上)

Drools 规则语言详解(上)

<!--[if !supportLists]--> 1  <!--[endif]--> 概述:

Drools 3 采用了原生的规则语言,那是一种非 XML 文本格式。在符号方面,这种格式是非常轻量的,并且通过“ expanders ”支持符合你问题域的 Domain Specific Language DSL )。这一章把焦点放在了 Drools 原生的规则格式。如果你想从技术上了解规则语言的机制,可以参考“ drl.g ”源文件,这是用 Antlr3 语法来描述规则语言。如果你使用 Rule Workbench ,内容助手将会为你完成大量的规则结构,例如输入“ ru ”,然后按 ctrl space ,会为你建立规则结构。

<!--[if !supportLists]--> 1.1    <!--[endif]--> 规则文件

一个规则文件通常是一个以 .drl 扩展名结尾的文件。在一个 drl 文件中,你可以有多条 rules functions 等等。尽管如此,你也可以将你的规则分布在多个文件中,这有利于管理大量的规则。一个 DRL 文件是一个简单的文本文件。

1.2 规则的结构

一个规则结构大致如下:

rule  " name "
    ATTRIBUTES
    when
        LHS
    then
        RHS
end

可以看到,这是非常简单的。通常的标点符号都是不需要的,甚至连“ name ”的双引号都是不需要的。 ATTRIBUTES 是简单的,也是可选的,来提示规则的行为方式。 LHS 是规则的条件部分,需要按照一定的语法来写。 RHS 基本上是一个允许执行 Java 语法的代码的块(以后将会支持 groovy C #)。任何在 LHS 中使用的变量都可以在 RHS 中使用。

注意:每行开始的空格是不重要的,除非在 DSL Domain Specific Language )语言中有特别的指明。

<!--[if !supportLists]--> 1.3   <!--[endif]--> Domain Specific Language

Domain Specific Language 是对原生规则语言的加强。它们使用“ expander ”机制。 Expander 机制是一种可扩展的 API 。你可以使用 .dsl 文件,来提供从域或自然语言到规则语言和你的域对象的映射。你可以将 .dsl 文件看成是对你的域模型的映射。 DSL 提供了更高的规则可读性,你可以选择使用你自己创建的 DSL ,或者是原生的规则语言。

1.4 保留字

在规则语言中存在一些保留字。你应该避免使用这些保留字,来命名规则文本中的域对象,属性,方法,功能。保留字如下: when then rule end contains matches and or modify retract assert salience function query exists eval agenda-group no-loop duration -> not auto-focus

<!--[if !supportLists]--> 2.   <!--[endif]--> 注释

2.1 单行注释:

Figure 2.1. Single line comment

2.2 多行注释:


Figure 2.2. Multi line comment

<!--[if !supportLists]-->

3.     <!--[endif]--> Package

一个包是 rule 和其他相关结构,像 import global 的集合。 Package 的成员之间通常都是相关联的。一个 Package 代表了一个命名空间( namespace ),用来使给定的规则组之间保持唯一性。 Package 的名字本身就是命名空间,并且与文件或文件夹并无关联。

<!--[if !supportEmptyParas]-->

可以将来自不同规则源的规则装配在一起,前提是这些规则必须处在同一个命名空间中。尽管如此,一个通常的结构是将处于同一个命名空间中的所有规则都放在同一个相同的文件中。

下面的 rail-road 图显示了组成一个 Package 的所有组件。注意:一个 package 必须有一个命名空间,并且采用 Java 包名的约定。在一个规则文件中,各组件出现的位置是任意的,除了“ package ”和“ expander ”语句必须出现在任何一个规则之前,放在文件的顶部。在任何情况下,分号都是可选的。

Figure 3.1. package

3.1 import


Figure 3.2. import

Import 语句的使用很像 Java 中的 import 语句。你需要为你要在规则中使用的对象,指定完整的路径和类名。 Drools 自动从相同命名的 java 包中引入所需的类。

3.2 expander

Figure 3.3. expander

expander 语句是可选的,是用来指定 Domain Specific Language 的配置(通常是一个 .dsl 文件)。这使得解析器可以理解用你自己的 DSL 语言所写的规则。

3.3 global

Figure 3.4. global

Global 就是全局变量。如果多个 package 声明了具有相同标识符的 global ,那么它们必需是相同的类型,并且所有的引用都是相同的。它们通常用来返回数据,比如 actions 的日志,或者为 rules 提供所需的数据或服务。 global 并不是通过 assert 动作放入 WorkingMemory 的,所有当 global 发生改变时,引擎将不会知道。所以, global 不能作为约束条件,除非它们的值是 final 的。将 global 错误的使用在约束条件中,会产生令人惊讶的错误结果。

<!--[if !supportEmptyParas]-->

注意: global 只是从你的 application 中传入 WorkingMemory 的对象的命名实例。这意味着你可以传入任何你想要的对象。你可以传入一个 service locator ,或者是一个 service 本身。

<!--[if !supportEmptyParas]-->

下面的例子中,有一个 EmailService 的实例。在你调用规则引擎的代码中,你有一个 EmailService 对象,然后把它放入 WorkingMemory 。在 DRL 文件中,你声明了一个类型为 EmailService global ,然后将它命名为“ email ”,像这样: global EmailService email ;。然后在你的规则的 RHS 中,你可以使用它,像这样: email.sendSMS(number,message) 等等。

4. Function


Figure 4.1. function

Function 是将代码放到你的规则源中的一种方法。它们只能做类似 Helper 类做的事(实际上编译器在背后帮你生成了 Helper 类)。在一个 rule 中使用 function 的主要优势是,你可以保持所有的逻辑都在一个地方,并且你可以根据需要来改变 function (这可能是好事也可能是坏事)。 Function 最有用的就是在规则的 RHS 调用 actions ,特别是当那个 action 需要反复调用的时候。

<!--[if !supportEmptyParas]-->

一个典型的 function 声明如下:

function String calcSomething(String arg) {
return
  " hola ! " ;
}

<!--[if !supportEmptyParas]-->

注意:“ function ”关键字的使用,它并不真正是 Java 的一部分。而 function 的参数就像是一个普通的 method (如果不需要参数就不用写)。返回类型也跟普通的 method 一样。在一条规则(在它的 RHS 中,或可能是一个 eval )中调用 function ,就像调用一个 method 一样,只需要 function 的名字,并传给它参数。

<!--[if !supportEmptyParas]-->

function 的替代品,可以使用一个 Helper 类中的静态方法: Foo.doSomething() ,或者以 global 的方式传入一个 Helper 类或服务的实例: foo.doSomething() foo 是一个命名的 global 变量)。

<!--[if !supportEmptyParas]-->

JBoss Rules 学习(七): Drools规则语言详解(下)


Drools
规则语言详解(下)


5. Rule

Figure 5.1. rule

Rule 结构是最重要的结构。 Rule 使用了形如“ IF something THEN action (当然,我们的关键字是“ when ”和“ then ”)的形式。

一个规则在一个 package 中必须要有唯一的名字。如果一个名字中含有空格,那就需要将名字放在双引号中(最好总是使用双引号)。

Attribute 是可选的(最好是每行只有一个 Attribute )。

规则的 LHS 跟在“ when ”关键字的后面(最好是另起一行),同样 RHS 要跟在“ then ”关键字后面(最好也另起一行)。规则以关键字“ end ”结束。规则不能嵌套。

5.1 Left Hand Side

Left Hand Side 其实就是规则的条件部分。 LHS 对应的 rail-road 图如下,我们在后面会做进一步解释:

Figure 5.2. Left Hand Side

Figure 5.3. pattern

 

5.2 Right Hand Side

Right Hand Side RHS )就是规则的结果( consequence )或者动作( action )部分。 RHS 的目的是 retract add facts WorkingMemory 中,还有针对你的 application 的动作。实际上, RHS 是当规则激发( fire )时执行的代码块。

RHS 中,你可以使用几个方便的 method 来改变 WorkingMemory

modify(obj) ”:告诉引擎一个对象已经发生变化,规则必须重新匹配( obj 对象必须是出现在 LHS 中的对象);

assert(new Something()) ”:将一个新的 Something 对象加入 WorkingMemory

assertLogical(new Something()) ”:与 assert 方法类似。但是,当没有 fact 支持当前激发规则的真实性的时候,这个新对象会自动被 retract

retract(obj) ”:从 WorkingMemory 中移除一个对象。

这些方法都是宏指令,提供了到 KnowledgeHelper 实例的快捷方式(参考 KnowledgeHelper 接口)。 KnowledgeHelper 接口可以在 RHS 代码块中调用,通过变量“ drools ”。如果你在 assert 进引擎的 JavaBean 中加入“ Property Change Listener ”,在对象发生变化的时候,你就不用调用“ modify ”方法。

5.3 Rule Attributes

Figure 5.4. rule attributes


5.3.1 no-loop

默认值: false

类型: boolean

当在 rule RHS 中修改了一个 fact ,这可能引起这个 rule 再次被 activate ,引起递归。将 no-loop 设为 true ,就可以防止这个 rule Activation 的再次被创建。

5.3.2 salience

默认值: 0

类型: int

每个 rule 都可以设置一个 salience 整数值,默认为 0 ,可以设为正整数或负整数。 Salience 是优先级的一种形式。当处于 Activation 队列中时,拥有高 salience 值的 rule 将具有更高的优先级。

5.3.3 a genda-group

默认值: MAIN

类型: String

Agenda group 允许用户对 Agenda 进行分组,以提供更多的执行控制。只有具有焦点的组中的 Activation 才会被激发( fire )。

5.3.4 a uto-focus

默认值: false

类型: boolean

当一个规则被 activate (即 Activation 被创建了),如果这个 rule auto-focus 值为 true 并且这个 rule agenda-group 没有焦点,此时这个 Activation 会被给予焦点,允许这个 Activation fire 的潜在可能。

5.3.5 a ctivation-group

默认值: N/A

类型: String

当处于同一个 activation-group 中的第一个 Activation fire 后,这个 activation-group 中其余剩下的 Activation 都不会被 fire

5.3.6 duration

默认值:没有默认值

类型: long

5.4 Column

Figure 5.5. Column


Example 5.1. Column

Cheese( )
Cheese( type 
==   " stilton " , price  <   10  )

 

一个 Column 由一个类的一个或多个域约束构成。第一个例子没有约束,它将匹配 WorkingMemory 中所有的 Cheese 实例。第二个例子对于一个 Cheese 对象有两个字面约束( Literal Constraints ),它们被用“,”号隔开,意味着“ and ”。

Figure 5.6. Bound Column


Example 5.2. Bound Column

cheapStilton : Cheese( type  ==   " stilton " , price  <   10  )

<!--[if !supportEmptyParas]--> <!--[endif]-->

这个例子同前一个例子有点类似。但是在这个例子中,我们将一个变量绑定到匹配规则引擎的 Cheese 实例上。这意味着,你可以在另一个条件中使用 cheapStilton ,或者在 rule RHS 中。

5.4.1 Field Constraints

Field Constraints 使规则引擎可以从 WorkingMemory 中挑选出合适的 Fact 对象。一个 Fact 的“ Field ”必须符合 JavaBean 规范,提供了访问 field getter 方法。你可以使用 field 的名字直接访问 field ,或者使用完整的方法名(省略括号)。

例如,以我们的 Chess 类为例,下面是等价的: Cheese(type = = …) Cheese(getType = = …) 。这意味着,你可以使用不太严格遵守 JavaBean 规范对象。尽管如此,你要保证 accessor 方法是不带参数的,以保证它不会改变对象的状态。

注意:如果一个 field 使用原始类型( primitive type ), Drools 将会把它们自动装箱成相应的对象(即使你使用 java 1.4 ),但是在 java 1.4 下却不能自动拆箱。总的来说,尽量在 rule 所使用的类中,使用非原始类型的域。如果是使用 java 5 ,就可以比较随意了,因为编译器会帮你执行自动装拆箱。

5.4.1 .1 Operators

Figure 5.7. Operators

有效的操作符是同域类型相关的。例如,对于日期域,“ < ”意味着“之前”。“ matches ”只适用于 String 域,“ contains ”和“ excludes ”只适用于 Collection 类型域。

5.4.1 .2 字面值约束( Literal Constraints

最简单的域约束就是字面值约束,允许用户将一个 field 约束于一个已知值。

注意:你可以检查域是否为 null ,使用 = = != 操作符和字面值‘ null ’关键字。如, Cheese(type != null) 。字面值约束,特别是“ = = ”操作符,提供了非常快的执行速度,因为可以使用散列法来提高性能。

Figure 5.8. Literal Constraints


Numeric

所有标准的 Java 数字基本类型都可以用。

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

<!--[if !supportLists]--> ·         <!--[endif]--> >

<!--[if !supportLists]--> ·         <!--[endif]--> <

<!--[if !supportLists]--> ·         <!--[endif]--> >=

<!--[if !supportLists]--> ·         <!--[endif]--> <=

Example 5.3 . Numeric Literal Constraint

Cheese( quantity  ==   5  )


Date

当前只对“ dd-mm-yyyy ”的日期格式提供默认支持。你可以通过指定 drools.dateformat 系统属性,来改变默认的日期格式。如果需要更多的控制,要用谓词约束( Predicate Constraint )。

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

<!--[if !supportLists]--> ·         <!--[endif]--> >

<!--[if !supportLists]--> ·         <!--[endif]--> <

<!--[if !supportLists]--> ·         <!--[endif]--> >=

<!--[if !supportLists]--> ·         <!--[endif]--> <=

Example 5.4. Date Literal Constraint

Cheese( bestBefore  <   " 27-Oct-2007 "  )


String

可以使用任何有效的 Java String

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

Example 5.5. String Literal Constraint

Cheese( type  ==   " stilton "  )


Boolean

只能用 “ true ”或“ false ”。 0 1 不能被识别,而且 Cheese(smelly) 也是不被允许的。

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

Example 5.6 Boolean Literal Constraint

Cheese( smelly  =   =   true  )


Matches Operator

Matches 操作符后面可以跟任何有效的 Java 正则表达式。

Example 5.7. Regular Expression Constraint

Cheese( type matches  " (Buffulo)?//S*Mozerella "  )


Contains Operator and Excludes Operator

contains ”和“ excludes ”可以用来检查一个 Collection 域是否含有一个对象。

Example 5.8. Literal Cosntraints with Collections

CheeseCounter( cheeses contains  " stilton "  )
CheeseCounter( cheeses excludes 
" chedder "  )


5.4.1 .3 Bound Variable Constraint

可以将 Facts 和它们的 Fields 附给一个 Bound Variable ,然后在后续的 Field Constraints 中使用这些 Bound Variable 。一个 Bound Variable 被称为声明( Declaration )。 Declaration 并不能和“ matches ”操作符合用,但却可以和“ contains ”操作符合用。

Example 5.9. Bound Field using '==' operator

Person( likes : favouriteCheese )
Cheese( type 
==  likes )

 

在上面的例子中,“ likes ”就是我们的 Bound Variable ,即 Declaration 。它被绑定到了任何正在匹配的 Person 实例的 favouriteCheese 域上,并且用来在下一个 Column 中约束 Cheese type 域。可以使用所有有效的 Java 变量名,包括字符“ $ ”。“ $ ”经常可以帮助你区分 Declaration field 。下面的例子将一个 Declaration 绑定到匹配的实例上,并且使用了“ contains ”操作符。注意: Declaratino 的第一个字符用了“ $ ”:

Example 5.10 Bound Fact using 'contains' operator

$stilton : Cheese( type  ==   " stilton "  )
Cheesery( cheeses contains $stilton )


5.4.1 .4 Predicate Constraints

 

Figure 5.9. Predicate expression

Predicate 表达式可以使用任何有效的 Java 逻辑表达式。先前的 Bound Declaration 可以用在表达式中。

下面的例子将会找出所有男性比女性大 2 岁的 pairs of male/femal people

Example 5.11. Predicate Constraints

Person( girlAge : age, sex  =   =   " F "  )
Person( boyAge : age 
->  ( girlAge.intValue()  +   2   ==  boyAge.intValue() ), sex  =   =   " M "  )

 

5.4.1 .5 Return Value Constraints

 

Figure 5.10. Return Value expression

一个 Retrurn Value 表达式可以使用任何有效的 Java 表达式,只要它返回一个对象,不能返回原始数据类型。如果返回值是原始数据类型,要先进行装箱。先前的 Bound Declaration 也可以使用在表达式中。

下面的例子跟上一节的例子一样,也将会找出所有男性比女性大 2 岁的 pairs of male/femal people 。注意:这里我们不用绑定 boyAge ,增加了可读性:

Example 5.12. Return Value Constraints

Person( girlAge : age, sex  =   =   " F "  )
Person( age 
=   =  (  new  Integer(girlAge.intValue()  +   2 ) ), sex  =   =   " M "  )

 

5.5 Conditional Elements

Conditional Elements 用来连接一个或多个 Columns

5.5.1 and

Figure 5.11. and


Example 5.13.  And

Cheese( cheeseType : type )  &&  Person( favouriteCheese  ==  cheeseType )
Cheese( cheeseType : type ) and Person( favouriteCheese 
==  cheeseType )


5.5.2 or

Figure 5.12. or

Example 5.14. or

Person( sex  ==   " f " , age  >   60  )  ||  Person( sex  ==   " m " , age  >   65  )
Person( sex 
==   " f " , age  >   60  ) or Person( sex  ==   " m " , age  >   65  )

 

Figure 5.13. or with binding


Example 5.15. or with binding

pensioner : Person( sex  ==   " f " , age  >   60  )  ||  pensioner : Person( sex  ==   " m " , age  >   65  ) 
pensioner : ( Person( sex 
==   " f " , age  >   60  ) or Person( sex  ==   " m " , age  >   65  ) )

or Conditional Element 的使用会导致多条 rule 的产生,称为 sub rules 。上面的例子将在内部产生两条规则。这两条规则会在 WorkingMemory 中各自独立的工作,也就是它们都能进行 match activate fire 。当对一个“ or Conditional Element 使用变量绑定时,要特别小心,错误的使用将产生完全不可预期的结果。

可以将“ OR Conditional Element 理解成产生两条规则的快捷方式。因此可以很容易理解,当“ OR Conditional Element 两边都为真时,这样的一条规则将可能产生多个 activation

5.5.3 eval

Figure 5.14 . eval

Eval is essentially a catch all which allows any semantic code (that returns a primitive boolean) to be executed. 在表达式中可以引用在 LHS 中出现的变量,和在 rule package 中的 Functions 。一个 Eval 应该是 LHS 中的最后一个 Conditional Element 。在一个 rule 中,你可以有多个 eval

Eval 不能被索引,因此不能像 Field Constraints 那样被优化。尽管如此,当 Functions 的返回值一直在变化时,应该使用 Eval ,因为这在 Field Constraints 中时不允许的。如果规则中的其他条件都匹配,一个 eval 每次都要被检查。(现在还不理解到底 eval 要怎么用?)

Example 5.16. eval

p1 : Parameter() 
p2 : Parameter()
eval( p1.getList().containsKey(p2.getItem()) )
eval( isValid(p1, p2) ) 
// this is how you call a function in the LHS - a function called  // "isValid"


5.5.4 not

Figure 5.15. not

not ”是一阶逻辑的存在量词( first order logic’s Existential Quantifier , 用来检查 WorkingMemory 中某对象的非存在性。现在,只有 Columns 可以放在 not 中,但是将来的版本会支持“ and ”和“ or ”。

Example 5.17. No Buses

not Bus()


Example 5.18. No red Buses

not Bus(color  ==   " red " )
not ( Bus(color 
==   " red " , number  =   =   42 ) )  // brackets are optional


5.5.5 exists

Figure 5.16. exists

exists ” 是一阶逻辑的存在量词( first order logic’s Existential Quantifier ),用来检查 WorkingMemory 中某对象的存在性。可以将“ exists ”理解为“至少有一个”( at least one… )。它的意义不同于只有 Column 本身,“ Column ”本身可以理解为“对于每一个”( for each of … )。如果你对一个 Column 使用了“ exists ”,那么规则将只 activate 一次,而不管 WorkingMeomry 中有多少个数据匹配了那个条件。

现在,只有 Columns 可以放在“ exists ”中,但是将来的版本会支持“ and ”和“ or ”。

Example 5.19. At least one Bus

exists Bus()


Example 5.20. At least one red Bus

exists Bus(color  ==   " red " )


5.5.6 group

Figure 5.17. group

Group 的作用相当于代数学中的“()”,显式的指明操作的顺序。

5.6 再谈自动装箱和原始类型

Java 5 支持在原始类型与其对应包装类之间的装拆箱。尽管如此,因为要让 drools 能够在 J2SE 1.4 下运行,我们不能依靠 J2SE 。因此, drools 自己实现了自动装箱。被引用(即被 Bound Variable 绑定)的 Field 将被自动进行装箱(如果它们本身就是 object ,就不会有任何变化)。尽管如此,必须要注意的是,他们并不会被自动拆箱。

还有一点要注意的,就是对于 ReturnValue Constraints ,返回值的代码必须返回一个对象,而不能是一个原始类型。

6 Query

Figure 6.1 . query

一个 query 只包含了一个 rule LHS 结构(你不用指定“ when ”或“ then ”)。这是查询 WorkingMemory 中匹配条件的 Facts 的简单的方法。

要得到结果,要使用 WorkingMemory.getQueryResults(“name”) 方法,其中“ name ”就是 query 的名字。 Query 的名字在 RuleBase 中是全局的,所以, do not add queries of the same name to different packages for the same RuleBase

下面的例子创建了一个简单的 query 来查询所有年龄大于 30 的人:

Example 6.1. Query People over the age of 30
 

query "people over the age of 30" 
    person : Person( age > 30 )
end

 

我们通过一个标准的循环来迭代一个返回的QueryResults对象。每一次的iterate将返回一个QueryResult对象。我们可以用QueryResult对象的get()方法来访问每个Column,通过传入Bound Declarationindex position

Example 6.2. Query People over the age of 30

QueryResults results = workingMemory.getQueryResults( "people over the age of 30" );
System.out.println( "we have " + results.size() + " people over the age  of 30" );

System.out.println( "These people are are over 30:" );

for
 ( Iterator it = results.iterator; it.hasNext(); ) {
    QueryResult result = ( QueryResult ) it.next();
    Person person = ( Person ) result.get( "person" );
    System.out.println( person.getName() + "/n" );
}

 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值