您的位置:首页 > 编程语言 > Java开发

[Drools]JAVA规则引擎2 -- Drools实例

2011-01-26 14:17 253 查看
上一篇文章 http://blog.csdn.net/quzishen/archive/2011/01/25/6163012.aspx 描述了一些常用的drools的语法标签和一个模拟实例即发送积分的场景,这一片优化了一下代码,在此贴一下,希望有这方面使用经验的朋友多多交流沟通,指正不足。

通常而言,习惯上我们将规则放到文件系统中,比如以drl结尾的规则文件,现在我们要扩充一下,使其放到数据库中,以供多台服务器同时使用,同时依然保留文件系统的支持。

先看下一个接口:

/**
* 规则接口
* @author quzishen
*/
public interface PointRuleEngine {

/**
* 初始化规则引擎
*/
public void initEngine();

/**
* 刷新规则引擎中的规则
*/
public void refreshEnginRule();

/**
* 执行规则引擎
* @param pointDomain 积分Fact
*/
public void executeRuleEngine(final PointDomain pointDomain);
}


实现过程没有任何难度,两种方式封装过程只在于读取规则的方式不同,代码很简单:

package com.drools.demo.point;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.drools.RuleBase;
import org.drools.StatefulSession;
import org.drools.compiler.PackageBuilder;
import org.drools.spi.Activation;

/**
* 规则接口实现类
*
* @author quzishen
*/
public class PointRuleEngineImpl implements PointRuleEngine {
// ~~~ instance filed begin
/** RuleBase */
private RuleBase ruleBase;
// ~~~ instance filed end

/*
* (non-Javadoc)
* @see com.drools.demo.point.PointRuleEngine#initEngine()
*/
public void initEngine() {
// 设置时间格式
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
try {
synchronized (this) {
ruleBase = RuleBaseFacatory.getRuleBase();
// 优先从DB加载规则,如果没有加载到或者加载错误,则从文件系统加载
PackageBuilder backageBuilder = getPackBuilderFromDrlDB();
backageBuilder = null == backageBuilder ? getPackageBuilderFromDrlFile()
: backageBuilder;

ruleBase.addPackages(backageBuilder.getPackages());
}
} catch (Exception e) {
e.printStackTrace();
}
}

/*
* (non-Javadoc)
* @see com.drools.demo.point.PointRuleEngine#refreshEnginRule()
*/
public void refreshEnginRule() {
ruleBase = RuleBaseFacatory.getRuleBase();
synchronized (ruleBase) {
// 删除所有的添加的Package
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() {
// 装载规则文件
List<Reader> readers;
try {
readers = buildReadersFromDrlFile();
// 装载PackageBuilder
return buildPackageBuilder(readers);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 从Drl规则DB中读取规则
*
* @return
* @throws Exception
*/
private PackageBuilder getPackBuilderFromDrlDB() {
// 装载规则
List<Reader> readers = buildReadersFromDrlDB();

// 装载PackageBuilder
try {
return buildPackageBuilder(readers);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 装载db中的规则到List<Reader>
*
* @return
*/
private List<Reader> buildReadersFromDrlDB() {
List<Reader> readers = new ArrayList<Reader>();
// 获取脚本
List<DroolsRuleDomain> drlRuleDomains = getRuleFromDB();

if (null == drlRuleDomains) {
return readers;
}

for (DroolsRuleDomain droolsRuleDomain : drlRuleDomains) {
String ruleContext = droolsRuleDomain.getRuleContext();

Reader br = new StringReader(ruleContext);
readers.add(br);
}
return readers;
}

/**
* 装载PackageBuilder
*
* @param readers
* @return
* @throws Exception
*/
private PackageBuilder buildPackageBuilder(List<Reader> readers)
throws Exception {
if (null == readers || 0 == readers.size()) {
return null;
}

PackageBuilder backageBuilder = new PackageBuilder();
for (Reader r : readers) {
backageBuilder.addPackageFromDrl(r);
}

// 检查脚本是否有问题
if (backageBuilder.hasErrors()) {
throw new Exception(backageBuilder.getErrors().toString());
}

return backageBuilder;
}

/**
* 装载规则文件到Reader中
*
* @return
* @throws FileNotFoundException
*/
private List<Reader> buildReadersFromDrlFile() throws FileNotFoundException {
// 获取脚本文件
List<String> drlFilePath = getRuleDrlFile();
// 装载脚本文件
return readRuleFromDrlFile(drlFilePath);
}

/**
* 从规则文件中读取规则
*
* @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<DroolsRuleDomain> getRuleFromDB() {
// 测试代码
List<DroolsRuleDomain> droolsRuleDomains = new ArrayList<DroolsRuleDomain>();

DroolsRuleDomain d1 = new DroolsRuleDomain();
d1.setId(1);
d1.setRuleContext("package com.drools.demo.point" + "/n" +
"import com.drools.demo.point.PointDomain;" + "/n" +
"rule birthdayPoint" + "/n" +
"// 过生日,则加10分,并且将当月交易比数翻倍后再计算积分" + "/n" +
"salience 100" + "/n" +
"lock-on-active true" + "/n" +
"when" + "/n" +
"$pointDomain : PointDomain(birthDay == true)" + "/n" +
"then" + "/n" +
"$pointDomain.setPoint($pointDomain.getPoint()+10);" + "/n" +
"$pointDomain.recordPointLog($pointDomain.getUserName(),/"birthdayPoint/");" + "/n" +
"end");

d1.setRuleName("testRule");
d1.setVersion(1);

droolsRuleDomains.add(d1);

return droolsRuleDomains;
}

/**
* 获取规则文件
*
* @return
*/
private List<String> getRuleDrlFile() {
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;
}
}


其中的getRuleFromDB() 和 getRuleDrlFile() 两个方法即可以重写以接入个人系统,现在其中编写的是测试代码。

其他的文件与上篇文章相同:

RuleBaseFacatory

package com.drools.demo.point;

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();
}
}


DroolsRuleDomain

package com.drools.demo.point;

/**
* 规则内容domain
*
* @author quzishen
*/
public class DroolsRuleDomain {
/** 数据库记录ID */
private long id;
/** 规则名称 */
private String ruleName;
/** 规则正文  */
private String ruleContext;
/** 规则版本 */
private int version;
/** 规则脚本状态 */
private int status;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getRuleName() {
return ruleName;
}

public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}

public String getRuleContext() {
return ruleContext;
}

public void setRuleContext(String ruleContext) {
this.ruleContext = ruleContext;
}

public int getVersion() {
return version;
}

public void setVersion(int version) {
this.version = version;
}

public int getStatus() {
return status;
}

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

}


PointDomain

package com.drools.demo.point;

/**
* 积分计算对象
* @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;
}

public void setUserName(String userName) {
this.userName = userName;
}

public boolean isBirthDay() {
return birthDay;
}

public void setBirthDay(boolean birthDay) {
this.birthDay = birthDay;
}

public long getPoint() {
return point;
}

public void setPoint(long point) {
this.point = point;
}

public int getBuyNums() {
return buyNums;
}

public void setBuyNums(int buyNums) {
this.buyNums = buyNums;
}

public int getBackNums() {
return backNums;
}

public void setBackNums(int backNums) {
this.backNums = backNums;
}

public double getBuyMoney() {
return buyMoney;
}

public void setBuyMoney(double buyMoney) {
this.buyMoney = buyMoney;
}

public double getBackMondy() {
return backMondy;
}

public void setBackMondy(double backMondy) {
this.backMondy = backMondy;
}

public int getBillThisMonth() {
return billThisMonth;
}

public void setBillThisMonth(int billThisMonth) {
this.billThisMonth = billThisMonth;
}

}


addpoint.drl

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 与上一篇相同,请参见上一篇,此处省略篇幅略

测试代码

Test

package com.drools.demo.point;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class Test {

/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
PointRuleEngine pointRuleEngine = new PointRuleEngineImpl();
boolean isStart = false;
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();
isStart = true;
System.out.println("初始化规则引擎结束.");
} else if ("e".equals(input)){
if (!isStart) {
System.out.println("需要输入s启动");
} else {
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();
isStart = true;
System.out.println("刷新规则文件结束.");
} else if ("q".equals(input)) {
System.exit(0);
}
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: