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

2022-10-19,,,

上一篇文章 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 <a href="http://lib.csdn.net/base/17" class='replace_word' title="Java EE知识库" target='_blank' style='color:#df3434; font-weight:bold;'>Java</a>.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);
    }
    }
    }
    }

 
 

[Drools]JAVA规则引擎 -- Drools 2的相关教程结束。

《[Drools]JAVA规则引擎 -- Drools 2.doc》

下载本文的Word格式文档,以方便收藏与打印。