欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Drools 学习笔记(二)----stateful session (有状态会话)的使用

程序员文章站 2022-05-07 21:54:58
...

有状态会话长期存在,并允许随着时间的推移进行迭代更改。 有状态会话的一些常见用例包括但不限于:
1. 监测
半自动买入股票市场监控与分析。
2. 诊断
故障查找,医疗诊断
3. 物流
包裹跟踪和送货配置
4. 合规
验证市场交易的合法性。

我们举例说明了用于提高火灾报警器的监控用例。 只使用四个类,我们假设Room代表房子里的房间,每个Room都有一个喷头Sprinkler。 如果在房间里发生火灾,我们用一个Fire实例来表示,用Alarm代表警报 。

public class Room {
    private String name
    // getter and setter methods here
}

public class Sprinkler {
    private Room room;
    private boolean on;
    // getter and setter methods here
}

public class Fire {
    private Room room;
    // getter and setter methods here
}

public class Alarm {
}

当发生火灾时,会为该类别创建Fire类的实例,并将其插入到会话中。 该规则使用Fire对象的房间字段上的绑定来约束与当前关闭的房间的喷水灭火器的匹配。 当此规则触发并且执行结果时,喷头被打开。

rule "火灾发生时,打开洒水器"
when 
    Fire($room : room)
    $sprinkler : Sprinkler( room == $room, on == false )
then 
    modify( $sprinkler ){ setOn(true) };
    System.out.println("Turn on the sprinkler for room "+ $room.getName() );
end

而无状态会话使用标准Java语法来修改字段,在上述规则中,我们使用modify语句,它作为一种“with”语句。 它可以包含一系列逗号分隔的Java表达式,即对由modify语句的控制表达式选择的对象的setter的调用。 这将修改数据,并使引擎意识到这些更改,以便它可以再次对其进行推理。 这个过程被称为推理,对于有状态会话的工作至关重要。 无状态会话通常不使用推理,因此引擎不需要意识到数据的更改。 也可以通过使用顺序模式显式地关闭推理。

到目前为止,我们有规则告诉我们匹配数据是否存在,但是当它不存在时呢? 我们如何确定火已经熄灭了,即没有Fire对象呢? 以前的约束是根据命题逻辑的句子,其中引擎限制个别的实例。 Drools还支持First Order Logic,允许您查看数据集。 当某个不存在时,关键字下的模式不匹配。 一旦这个房间的火灾消失,下面给出的规则会使喷水灭火。

rule "火灾结束时,关闭洒水器"
when 
    $room : Room()
    $sprinkler : Sprinkler( room == $room, on == true)
    not Fire( room == $room )
then 
    modify( $sprinkler) { setOn (false) };
    System.out.println( "Turn off the sprinkler from room "+ $room.getName() );
end

每个room有一个喷水灭火器,house只有一个警报。 当发生火灾时,会创建一个alrm对象,而不管发生多少火灾,整个建筑物都只需要一个警报alrm。

rule "发生火灾时,打开报警器"
when 
    exists Fire()
then 
    insert( new Alarm() );
    System.out.println( "Raise the alarm");
end

同样,当没有火灾时,我们想要删除警报,所以可以再次使用not关键字。

rule "火灾结束,关闭报警器"
when 
    not Fire()
    $alarm : Alarm()
then 
    delete( $alarm );
    System.out.println( "Cancel the alarm");
end

最后,当应用程序首次启动并且在报警消除并且所有喷头已关闭后,都会打印Everything is ok。

rule "一切正常"
when 
    not Alarm()
    not Sprinkler( on == true)
then 
    System.out.println("Everything is ok");
end

正如我们在无状态会话示例中所做的那样,上述规则应放在单个DRL文件中,并保存到Maven项目或其任何子文件夹的资源文件夹中。 如前所述,我们可以从KieContainer获得KieSession。 唯一的区别是,这次我们创建一个有状态会话,而之前我们创建的是一个无状态会话。

KieServices kieServices = KieServices.Factory.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession();

创建会话后,现在可以随着时间的推移迭代地使用它。 创建和插入四个房间对象,每个房间的对应一个Sprinkler对象。 此时,规则引擎已经完成了所有的匹配,但并没有触发。 调用ksession.fireAllRules()使得匹配的规则触发,但因为没有火灾,所以输出结果是Everything is ok。

String[] names = new String[]{ "kitchen","bedroom","office","livingroom"};
Map<String,Room> name2room = new HashMap<>();
for (String name : names) {
    Room room = new Room( name );
    name2room.put(name, room);
    kieSession.insert(room);
    Sprinkler sprinkler = new Sprinkler( room );
    kieSession.insert(sprinkler);
}
kieSession.fireAllRules();

输出结果:

Everything is ok

我们现在创造两个Fire并插入它们, 随着火灾的发生,一旦调用了fireAllRules(),报警器就会响起,并且相应的喷水灭火器打开。

Fire kitchenFire = new Fire(name2room.get("kitchen"));
Fire officeFire = new Fire(name2room.get("office"));
FactHandle kitchenFireHandle = kieSession.insert(kitchenFire);
FactHandle officeFireHandle = kieSession.insert(officeFire);

kieSession.fireAllRules();

输出结果:

Raise the alarm
Turn on the sprinkler for room kitchen
Turn on the sprinkler for room office

一段时间之后,火灾将熄灭,并且Fire实例被撤回。 这导致喷头关闭,报警被取消,最后再次打印Everything is ok。

kieSession.delete(kitchenFireHandle);
kieSession.delete(officeFireHandle);
kieSession.fireAllRules();

输出结果:

Cancel the alarm
Turn off the sprinkler from room kitchen
Turn off the sprinkler from room office
Everything is ok
相关标签: Drools