【www.shanpow.com--在线新华字典】
drools规则引擎篇1:[Drools]JAVA规则引擎(非常好的一篇教程)
Drools是一个基于java的规则引擎,开源的,可以将复杂多变的规则从硬编码中解放出来,以规则脚本的形式存放在文件中,使得规则的变更不需要修正代码重启机器就可以立即在线上环境生效。
本文所使用的demo已上传 http://download.csdn.net/source/3002213
1、Drools语法
开始语法之前首先要了解一下drools的基本工作过程,通常而言我们使用一个接口来做事情,首先要穿进去参数,其次要获取到接口的实现执行完毕后的结果,而drools也是一样的,我们需要传递进去数据,用于规则的检查,调用外部接口,同时还可能需要获取到规则执行完毕后得到的结果。在drools中,这个传递数据进去的对象,术语叫 Fact对象。Fact对象是一个普通的java bean,规则中可以对当前的对象进行任何的读写操作,调用该对象提供的方法,当一个java bean插入到workingMemory中,规则使用的是原有对象的引用,规则通过对fact对象的读写,实现对应用数据的读写,对于其中的属性,需要提供getter setter访问器,规则中,可以动态的往当前workingMemory中插入删除新的fact对象。
规则文件可以使用 .drl文件,也可以是xml文件,这里我们使用drl文件。
规则语法:
package:对一个规则文件而言,package是必须定义的,必须放在规则文件第一行。特别的是,package的名字是随意的,不必必须对应物理路径,跟java的package的概念不同,这里只是逻辑上的一种区分。同样的package下定义的function和query等可以直接使用。
比如:package com.drools.demo.point
import:导入规则文件需要使用到的外部变量,这里的使用方法跟java相同,但是不同于java的是,这里的import导入的不仅仅可以是一个类,也可以是这个类中的某一个可访问的静态方法。
比如:
import com.drools.demo.point.PointDomain;
import com.drools.demo.point.PointDomain.getById;
rule:定义一个规则。rule "ruleName"。一个规则可以包含三个部分:
属性部分:定义当前规则执行的一些属性等,比如是否可被重复执行、过期时间、生效时间等。
条件部分,即LHS,定义当前规则的条件,如 when Message(); 判断当前workingMemory中是否存在Message对象。
结果部分,即RHS,这里可以写普通java代码,即当前规则条件满足后执行的操作,可以直接调用Fact对象的方法来操作应用。
规则事例:
rule "name"
no-loop true
when
$message:Message(status == 0)
then
System.out.println("fit");
$message.setStatus(1);
update($message);
end
上述的属性中:
no-loop : 定义当前的规则是否不允许多次循环执行,默认是false,也就是当前的规则只要满足条件,可以无限次执行。什么情况下会出现一条规则执行过一次又被多次重复执行呢?drools提供了一些api,可以对当前传入workingMemory中的Fact对象进行修改或者个数的增减,比如上述的update方法,就是将当前的workingMemory中的Message类型的Fact对象进行属性更新,这种操作会触发规则的重新匹配执行,可以理解为Fact对象更新了,所以规则需要重新匹配一遍,那么疑问是之前规则执行过并且修改过的那些Fact对象的属性的数据会不会被重置?结果是不会,已经修改过了就不会被重置,update之后,之前的修改都会生效。当然对Fact对象数据的修改并不是一定需要调用update才可以生效,简单的使用set方法设置就可以完成,这里类似于java的引用调用,所以何时使用update是一个需要仔细考虑的问题,一旦不慎,极有可能会造成规则的死循环。上述的no-loop true,即设置当前的规则,只执行一次,如果本身的RHS部分有update等触发规则重新执行的操作,也不要再次执行当前规则。
但是其他的规则会被重新执行,岂不是也会有可能造成多次重复执行,数据紊乱甚至死循环?答案是使用其他的标签限制,也是可以控制的:lock-on-active true
lock-on-active true:通过这个标签,可以控制当前的规则只会被执行一次,因为一个规则的重复执行不一定是本身触发的,也可能是其他规则触发的,所以这个是no-loop的加强版。当然该标签正规的用法会有其他的标签的配合,后续提及。
date-expires:设置规则的过期时间,默认的时间格式:“日-月-年”,中英文格式相同,但是写法要用各自对应的语言,比如中文:"29-七月-2010",但是还是推荐使用更为精确和习惯的格式,这需要手动在java代码中设置当前系统的时间格式,后续提及。属性用法举例:date-expires "2011-01-31 23:59:59" // 这里我们使用了更为习惯的时间格式
date-effective:设置规则的生效时间,时间格式同上。
duration:规则定时,duration 3000 3秒后执行规则
salience:优先级,数值越大越先执行,这个可以控制规则的执行顺序。
其他的属性可以参照相关的api文档查看具体用法,此处略。
规则的条件部分,即LHS部分:
when:规则条件开始。条件可以单个,也可以多个,多个条件一次排列,比如
when
eval(true)
$customer:Customer()
$message:Message(status==0)
上述罗列了三个条件,当前规则只有在这三个条件都匹配的时候才会执行RHS部分,三个条件中第一个
eval(true):是一个默认的api,true 无条件执行,类似于 while(true)
$message:Message(status==0) 这句话标示的:当前的workingMemory存在Message类型并且status属性的值为0的Fact对象,这个对象通常是通过外部java代码插入或者自己在前面已经执行的规则的RHS部分中insert进去的。
前面的$message代表着当前条件的引用变量,在后续的条件部分和RHS部分中,可以使用当前的变量去引用符合条件的FACT对象,修改属性或者调用方法等。可选,如果不需要使用,则可以不写。
条件可以有组合,比如:
Message(status==0 || (status > 1 && status <=100))
RHS中对Fact对象private属性的操作必须使用getter和setter方法,而RHS中则必须要直接用.的方法去使用,比如
$order:Order(name=="qu") $message:Message(status==0 && orders contains $order && $order.name=="qu")
特别的是,如果条件全部是 &&关系,可以使用“,”来替代,但是两者不能混用
如果现在Fact对象中有一个List,需要判断条件,如何判断呢?
看一个例子:
Message {
int status;
List<String> names;
}
$message:Message(status==0 && names contains "网易" && names.size >= 1)
上述的条件中,status必须是0,并且names列表中含有“网易”并且列表长度大于等于1
contains:对比是否包含操作,操作的被包含目标可以是一个复杂对象也可以是一个简单的值。
Drools提供了十二中类型比较操作符:
> >= < <= == != contains / not contains / memberOf / not memberOf /matches/ not matches
not contains:与contains相反。
memberOf:判断某个Fact属性值是否在某个集合中,与contains不同的是他被比较的对象是一个集合,而contains被比较的对象是单个值或者对象。
not memberOf:正好相反。
matches:正则表达式匹配,与java不同的是,不用考虑"/"的转义问题
not matches:正好相反。
规则的结果部分
当规则条件满足,则进入规则结果部分执行,结果部分可以是纯java代码,比如:
then
System.out.println("OK"); //会在控制台打印出ok
end
当然也可以调用Fact的方法,比如 $message.execute();操作数据库等等一切操作。
结果部分也有drools提供的方法:
insert:往当前workingMemory中插入一个新的Fact对象,会触发规则的再次执行,除非使用no-loop限定;
update:更新
modify:修改,与update语法不同,结果都是更新操作
retract:删除
RHS部分除了调用Drools提供的api和Fact对象的方法,也可以调用规则文件中定义的方法,方法的定义使用 function 关键字
function void console {
System.out.println();
StringUtils.getId();// 调用外部静态方法,StringUtils必须使用import导入,getId()必须是静态方法
}
Drools还有一个可以定义类的关键字:
declare 可以再规则文件中定义一个class,使用起来跟普通java对象相似,你可以在RHS部分中new一个并且使用getter和setter方法去操作其属性。
declare Address @author(quzishen) // 元数据,仅用于描述信息
@createTime(2011-1-24) city : String @maxLengh(100) postno : intend
上述的"@"是什么呢?是元数据定义,用于描述数据的数据~,没什么执行含义
你可以在RHS部分中使用Address address = new Address()的方法来定义一个对象。
更多的规则语法,可以参考其他互联网资料,推荐:
http://wenku.baidu.com/view/a6516373f242336c1eb95e7c.html
(写的很基础,但是部分语法写的有些简单,含糊不好理解)
2、Drools应用实例:
现在我们模拟一个应用场景:网站伴随业务产生而进行的积分发放操作。比如支付宝信用卡还款奖励积分等。
发放积分可能伴随不同的运营策略和季节性调整,发放数目和规则完全不同,如果使用硬编码的方式去伴随业务调整而修改,代码的修改、管理、优化、测试、上线将是一件非常麻烦的事情,所以,将发放规则部分提取出来,交给Drools管理,可以极大程度的解决这个问题。
(注意一点的是,并非所有的规则相关内容都建议使用Drools,这其中要考虑系统会运行多久,规则变更频率等一系列条件,如果你的系统只会在线上运行一周,那根本没必要选择Drools来加重你的开发成本,java硬编码的方式则将是首选)
我们定义一下发放规则:
积分的发放参考因素有:交易笔数、交易金额数目、信用卡还款次数、生日特别优惠等。
定义规则:
// 过生日,则加10分,并且将当月交易比数翻倍后再计算积分
// 2011-01-08 - 2011-08-08每月信用卡还款3次以上,每满3笔赠送30分
// 当月购物总金额100以上,每100元赠送10分
// 当月购物次数5次以上,每五次赠送50分
// 特别的,如果全部满足了要求,则额外奖励100分
// 发生退货,扣减10分
// 退货金额大于100,扣减100分
在事先分析过程中,我们需要全面的考虑对于积分所需要的因素,以此整理抽象Fact对象,通过上述的假设条件,我们假设积分计算对象如下:
[java] view plaincopy
/**
* 积分计算对象
* @author quzishen
*/
public class PointDomain {
// 用户名
private String userName;
// 是否当日生日
private boolean birthDay;
// 增加积分数目
private long point;
// 当月购物次数
private int buyNums;
// 当月退货次数
private int backNums;
// 当月购物总金额
private double buyMoney;
// 当月退货总金额
private double backMondy;
// 当月信用卡还款次数
private int billThisMonth;
/**
* 记录积分发送流水,防止重复发放
* @param userName 用户名
* @param type 积分发放类型
*/
public void recordPointLog(String userName, String type){
System.out.println("增加对"+userName+"的类型为"+type+"的积分操作记录.");
}
public String getUserName() {
return userName;
}
// 其他getter setter方法省略
}
定义积分规则接口
[java] view plaincopy
/**
* 规则接口
* @author quzishen
*/
public interface PointRuleEngine {
/**
* 初始化规则引擎
*/
public void initEngine();
/**
* 刷新规则引擎中的规则
*/
public void refreshEnginRule();
/**
* 执行规则引擎
* @param pointDomain 积分Fact
*/
public void executeRuleEngine(final PointDomain pointDomain);
}
规则接口实现,Drools的API很简单,可以参考相关API文档查看具体用法:
[java] view plaincopy
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import org.drools.RuleBase;
import org.drools.StatefulSession;
import org.drools.compiler.DroolsParserException;
import org.drools.compiler.PackageBuilder;
import org.drools.spi.Activation;
/**
* 规则接口实现类
* @author quzishen
*/
public class PointRuleEngineImpl implements PointRuleEngine {
private RuleBase ruleBase;
/* (non-Javadoc)
* @see com.drools.demo.point.PointRuleEngine#initEngine()
*/
public void initEngine() {
// 设置时间格式
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
ruleBase = RuleBaseFacatory.getRuleBase();
try {
PackageBuilder backageBuilder = getPackageBuilderFromDrlFile();
ruleBase.addPackages(backageBuilder.getPackages());
} catch (DroolsParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/* (non-Javadoc)
* @see com.drools.demo.point.PointRuleEngine#refreshEnginRule()
*/
public void refreshEnginRule() {
ruleBase = RuleBaseFacatory.getRuleBase();
org.drools.rule.Package[] packages = ruleBase.getPackages();
for(org.drools.rule.Package pg : packages) {
ruleBase.removePackage(pg.getName());
}
initEngine();
}
/* (non-Javadoc)
* @see com.drools.demo.point.PointRuleEngine#executeRuleEngine(com.drools.demo.point.PointDomain)
*/
public void executeRuleEngine(final PointDomain pointDomain) {
if(null == ruleBase.getPackages() || 0 == ruleBase.getPackages().length) {
return;
}
StatefulSession statefulSession = ruleBase.newStatefulSession();
statefulSession.insert(pointDomain);
// fire
statefulSession.fireAllRules(new org.drools.spi.AgendaFilter() {
public boolean accept(Activation activation) {
return !activation.getRule().getName().contains("_test");
}
});
statefulSession.dispose();
}
/**
* 从Drl规则文件中读取规则
* @return
* @throws Exception
*/
private PackageBuilder getPackageBuilderFromDrlFile() throws Exception {
// 获取测试脚本文件
List<String> drlFilePath = getTestDrlFile();
// 装载测试脚本文件
List<Reader> readers = readRuleFromDrlFile(drlFilePath);
PackageBuilder backageBuilder = new PackageBuilder();
for (Reader r : readers) {
backageBuilder.addPackageFromDrl(r);
}
// 检查脚本是否有问题
if(backageBuilder.hasErrors()) {
throw new Exception(backageBuilder.getErrors().toString());
}
return backageBuilder;
}
/**
* @param drlFilePath 脚本文件路径
* @return
* @throws FileNotFoundException
*/
private List<Reader> readRuleFromDrlFile(List<String> drlFilePath) throws FileNotFoundException {
if (null == drlFilePath || 0 == drlFilePath.size()) {
return null;
}
List<Reader> readers = new ArrayList<Reader>();
for (String ruleFilePath : drlFilePath) {
readers.add(new FileReader(new File(ruleFilePath)));
}
return readers;
}
/**
* 获取测试规则文件
*
* @return
*/
private List<String> getTestDrlFile() {
List<String> drlFilePath = new ArrayList<String>();
drlFilePath
.add("D:/workspace2/DroolsDemo/src/com/drools/demo/point/addpoint.drl");
drlFilePath
.add("D:/workspace2/DroolsDemo/src/com/drools/demo/point/subpoint.drl");
return drlFilePath;
}
}
为了获取单实例的RuleBase,我们定义一个工厂类
[java] view plaincopy
import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
/**
* RuleBaseFacatory 单实例RuleBase生成工具
* @author quzishen
*/
public class RuleBaseFacatory {
private static RuleBase ruleBase;
public static RuleBase getRuleBase(){
return null != ruleBase ? ruleBase : RuleBaseFactory.newRuleBase();
}
}
剩下的就是定义两个规则文件,分别用于积分发放和积分扣减
addpoint.drl
[java] view plaincopy
package com.drools.demo.point
import com.drools.demo.point.PointDomain;
rule birthdayPoint
// 过生日,则加10分,并且将当月交易比数翻倍后再计算积分
salience 100
lock-on-active true
when
$pointDomain : PointDomain(birthDay == true)
then
$pointDomain.setPoint($pointDomain.getPoint()+10);
$pointDomain.setBuyNums($pointDomain.getBuyNums()*2);
$pointDomain.setBuyMoney($pointDomain.getBuyMoney()*2);
$pointDomain.setBillThisMonth($pointDomain.getBillThisMonth()*2);
$pointDomain.recordPointLog($pointDomain.getUserName(),"birthdayPoint");
end
rule billThisMonthPoint
// 2011-01-08 - 2011-08-08每月信用卡还款3次以上,每满3笔赠送30分
salience 99
lock-on-active true
date-effective "2011-01-08 23:59:59"
date-expires "2011-08-08 23:59:59"
when
$pointDomain : PointDomain(billThisMonth >= 3)
then
$pointDomain.setPoint($pointDomain.getPoint()+$pointDomain.getBillThisMonth()/3*30);
$pointDomain.recordPointLog($pointDomain.getUserName(),"billThisMonthPoint");
end
rule buyMoneyPoint
// 当月购物总金额100以上,每100元赠送10分
salience 98
lock-on-active true
when
$pointDomain : PointDomain(buyMoney >= 100)
then
$pointDomain.setPoint($pointDomain.getPoint()+ (int)$pointDomain.getBuyMoney()/100 * 10);
$pointDomain.recordPointLog($pointDomain.getUserName(),"buyMoneyPoint");
end
rule buyNumsPoint
// 当月购物次数5次以上,每五次赠送50分
salience 97
lock-on-active true
when
$pointDomain : PointDomain(buyNums >= 5)
then
$pointDomain.setPoint($pointDomain.getPoint()+$pointDomain.getBuyNums()/5 * 50);
$pointDomain.recordPointLog($pointDomain.getUserName(),"buyNumsPoint");
end
rule allFitPoint
// 特别的,如果全部满足了要求,则额外奖励100分
salience 96
lock-on-active true
when
$pointDomain:PointDomain(buyNums >= 5 && billThisMonth >= 3 && buyMoney >= 100)
then
$pointDomain.setPoint($pointDomain.getPoint()+ 100);
$pointDomain.recordPointLog($pointDomain.getUserName(),"allFitPoint");
end
subpoint.drl
[java] view plaincopy
package com.drools.demo.point
import com.drools.demo.point.PointDomain;
rule subBackNumsPoint
// 发生退货,扣减10分
salience 10
lock-on-active true
when
$pointDomain : PointDomain(backNums >= 1)
then
$pointDomain.setPoint($pointDomain.getPoint()-10);
$pointDomain.recordPointLog($pointDomain.getUserName(),"subBackNumsPoint");
end
rule subBackMondyPoint
// 退货金额大于100,扣减100分
salience 9
lock-on-active true
when
$pointDomain : PointDomain(backMondy >= 100)
then
$pointDomain.setPoint($pointDomain.getPoint()-10);
$pointDomain.recordPointLog($pointDomain.getUserName(),"subBackMondyPoint");
end
测试方法:
[java] view plaincopy
public static void main(String[] args) throws IOException {
PointRuleEngine pointRuleEngine = new PointRuleEngineImpl();
while(true){
InputStream is = System.in;
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String input = br.readLine();
if(null != input && "s".equals(input)){
System.out.println("初始化规则引擎...");
pointRuleEngine.initEngine();
System.out.println("初始化规则引擎结束.");
}else if("e".equals(input)){
final PointDomain pointDomain = new PointDomain();
pointDomain.setUserName("hello kity");
pointDomain.setBackMondy(100d);
pointDomain.setBuyMoney(500d);
pointDomain.setBackNums(1);
pointDomain.setBuyNums(5);
pointDomain.setBillThisMonth(5);
pointDomain.setBirthDay(true);
pointDomain.setPoint(0l);
pointRuleEngine.executeRuleEngine(pointDomain);
System.out.println("执行完毕BillThisMonth:"+pointDomain.getBillThisMonth());
System.out.println("执行完毕BuyMoney:"+pointDomain.getBuyMoney());
System.out.println("执行完毕BuyNums:"+pointDomain.getBuyNums());
System.out.println("执行完毕规则引擎决定发送积分:"+pointDomain.getPoint());
} else if("r".equals(input)){
System.out.println("刷新规则文件...");
pointRuleEngine.refreshEnginRule();
System.out.println("刷新规则文件结束.");
}
}
}
执行结果:
-----------------
增加对hello kity的类型为birthdayPoint的积分操作记录.增加对hello kity的类型为billThisMonthPoint的积分操作记录.增加对hello kity的类型为buyMoneyPoint的积分操作记录.增加对hello kity的类型为buyNumsPoint的积分操作记录.增加对hello kity的类型为allFitPoint的积分操作记录.增加对hello kity的类型为subBackNumsPoint的积分操作记录.增加对hello kity的类型为subBackMondyPoint的积分操作记录.执行完毕BillThisMonth:10执行完毕BuyMoney:1000.0执行完毕BuyNums:10执行完毕规则引擎决定发送积分:380
版权声明:本文为博主原创文章,未经博主允许不得转载。
drools规则引擎篇2: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
Example 4.3. kmodule.xml中配置KieBase示例
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
因为KSession2_1 和 KSession2_2 是不同的类型,一个是stateful,一个是stateless。所以他们使用的方法是不同的,如果用错了会抛出RunTimeException。
使用Maven来构建
KIE插件使得artifact会被校验和预编译,所以建议一直使用这个插件。如下图
Example 4.7. 在pom.xml中增加KIE plugin
用程序来定义KieModule
其实也可以用程序来定义KieModule里面的KieBase和KieSession。要做到这一点需要先创建一个KieFileSystem。它是一个虚拟的文件系统,然后把项目中的所有资源添加进去。
像其他的核心组件一样,你也可以从KieServices中获取KieFileSystem。
Example 4.8. 用编程方式达到 kmodule.xml 同样效果的例子
下面还要给KieFileSystem加上其他必须的artifacts:
Example 4.9. Adding Kie artifacts to a KieFileSystem
Example 4.10. Creating and adding a Resource with an explicit type
Example 4.11. Building the contents of a KieFileSystem and creating a KieContainer
Example 4.12. Checking that a compilation didn"t produce any error
Example 4.13. Setting the severity using properties
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
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
在上面的例子里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
下面是一个例子:
显示申明一个版本(除非版本冲突,否则总是这个版本):
(废话不详说)
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
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
待续......
drools规则引擎篇3:Drools学习笔记1-规则引擎介绍
最近做一个项目可能会用到规则引擎。就学习了一下。寄了些笔记。从最开始的一无所知,到慢慢熟悉,在这里记录一下啊。
规则引擎的概念:规则引擎起源于基于规则的专家系统,而基于规则的专家系统又是专家系统的其中一个分支。专家系统属于人工智能的范畴,它模仿人类的推理方式,使用试探性的方法进行推理,并使用人类能理解的术语解释和证明它的推理结论。规则引擎一般由3部分组成:规则库(Knowledge
base)+Working Memory(Fact base)+推理机(规则引擎)。
规则引擎的工作:模式匹配、冲突消解、执行引擎。
模式匹配算法:将事实与规则进行匹配的算法。常见的模式匹配算法有RETE,LFA,TREAI,LEAPS。Drools采用的匹配算法就是RETE,该算法被认为是目前世界上最先进的模式匹配算法。
补充:规则引擎(rule engine)是有很多种的。依据规则的执行方式来划分。商业上采用的大部分规则引擎都是 forward chaining(汉语咋解释不知道)类型的。forward chaining类型有分为2种:(1)production/inference rules(汉语咋解释不知道)它是通过IFconditionTHEN
action这种条件判断的形式来驱动规则引擎的。(2)reaction/Event
Condition
Action rules,这种规则引擎是通过事件机制来驱动规则引擎的。这是我从维基百科上了解的。有兴趣的可以去看http://en.wikipedia.org/wiki/Business_rules_engine。
到此,我们对规则引擎是个啥东西已经有总体的了解了。至于里边提到的各个细节,我们可以放到以后慢慢了解,现在不是深究它们的时候。毕竟我们是要学习Drools,并运用到实际项目中。要是想都搞明白了在做项目,那项目经理是要骂娘的。
下面将看看啥时rete算法。


![[延边大学招生官网]延边大学官网:http://www.ybu.edu.cn/](https://m.51test.net/img.wykw.com/uploadfile/images/template/36.jpg)
![[我的未来谁做主观后感]我的未来谁做主评价](http://img.dagaqi.com/uploadfile/2019/0905/20190905052949913.png)



