Drools入門(四)——?jiǎng)討B(tài)規(guī)則實(shí)現(xiàn)

概述

前面我們實(shí)現(xiàn)了對規(guī)則文件的讀取和執(zhí)行,也知道了規(guī)則文件的基本結(jié)構(gòu)與相關(guān)函數(shù)及應(yīng)用,同時(shí)也學(xué)習(xí)了規(guī)則引擎的組件及規(guī)則的加載解析過程?,F(xiàn)在將解決最后一個(gè)問題,我們開發(fā)中的需求是不會(huì)事先寫好規(guī)則文件文件在啟動(dòng)項(xiàng)目的,如果是這樣當(dāng)規(guī)則文件修改新增或刪除時(shí)就必須重啟服務(wù)器,這樣才能使規(guī)則生效,這顯然是不合理的,接下來我們將在《Drools入門(一)——環(huán)境搭建》的基礎(chǔ)上進(jìn)行修改,使規(guī)則引擎動(dòng)態(tài)加載外部規(guī)則,并實(shí)現(xiàn)新增和刪除功能

添加KieModuleBuilder類

新建一個(gè)包命名為builder,并在builder包下新建KieModuleBuilder.java,并將下面內(nèi)容復(fù)制到該類中

package builder;

import entity.Rule;
import entity.RuleGroup;
import org.drools.compiler.compiler.io.memory.MemoryFileSystem;
import org.drools.compiler.kie.builder.impl.KieContainerImpl;
import org.drools.compiler.kie.builder.impl.KieProject;
import org.drools.compiler.kie.builder.impl.MemoryKieModule;
import org.drools.compiler.kie.builder.impl.ResultsImpl;
import org.drools.compiler.kproject.models.KieBaseModelImpl;
import org.drools.compiler.kproject.models.KieModuleModelImpl;
import org.kie.api.KieServices;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.ReleaseId;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.builder.model.KieSessionModel;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.StatelessKieSession;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class KieModuleBuilder {

    private static final String RESOURCES_ROOT = "src/main/resources/";

    private static final String FILE_SEPARATOR = "/";

    private static final String PACKAGE_NAME_PREFIX = "com.dome.rules.id_";

    private static final String PACKAGE_PATH_PREFIX = PACKAGE_NAME_PREFIX.replaceAll("\\.", FILE_SEPARATOR);

    private static final String KIE_BASE_MODEL_NAME_PREFIX = "kieBaseModelName_";

    private static final String KIE_SESSION_MODEL_NAME_PREFIX = "kieSessionModelName_";

    private static final String PACKAGE = "package ";

    private static final String FILE_SUFFIX = ".drl";

    /**
     * 創(chuàng)建模塊化組件并添加到初始化文件系統(tǒng)中,初始化文件系統(tǒng)在完成加載后將摒棄
     * @param kieServices
     * @param kieFileSystem
     * @param ruleGroups
     * @return
     */
    public static KieModuleModel initAndBuildKieModuleModel(KieServices kieServices, KieFileSystem kieFileSystem, List<RuleGroup> ruleGroups){
        boolean isDefault = true;
        KieModuleModel kieModuleModel = kieServices.newKieModuleModel();
        for (RuleGroup ruleGroup : ruleGroups) {
            if (ruleGroup.getRules() != null && ruleGroup.getRules().size() > 0){
                buildKieBaseModel(kieModuleModel, ruleGroup, isDefault);
                isDefault = false;
            }
        }
        kieFileSystem.write(KieModuleModelImpl.KMODULE_SRC_PATH,kieModuleModel.toXML());
        return kieModuleModel;
    }

    /**
     * 添加規(guī)則文件到初始化文件系統(tǒng)中,初始化文件系統(tǒng)在完成加載后將摒棄
     * @param kieFileSystem
     * @param ruleGroups
     * @return
     */
    public static List<String> initAndBuildRules(KieFileSystem kieFileSystem, List<RuleGroup> ruleGroups){
        List<String> rules = new ArrayList<>();
        for (RuleGroup ruleGroup : ruleGroups) {
            for (Rule rule : ruleGroup.getRules()) {
                String ruleContent = buildRule(ruleGroup, rule);
                rules.add(ruleContent);
                kieFileSystem.write(RESOURCES_ROOT + getRuleFilePath(ruleGroup.getId(), rule.getId()), ruleContent);
            }
        }
        return rules;
    }

    /**
     * 初始化session組件,用于規(guī)則引擎使用入口
     * @param kieContainer
     * @param ruleGroups
     * @return
     */
    public static Map<RuleGroup, StatelessKieSession> initStatelessKieSession(KieContainer kieContainer, List<RuleGroup> ruleGroups){
        Map<RuleGroup, StatelessKieSession> statelessKieSessionMap = new LinkedHashMap<>();
        for (RuleGroup promotionRule : ruleGroups) {
            statelessKieSessionMap.put(promotionRule, buildKieSession(kieContainer, promotionRule));
        }
        return statelessKieSessionMap;
    }

    /**
     * 檢查規(guī)則庫中是否已經(jīng)存在該規(guī)則模塊
     * @param kieRepository
     * @param ruleGroup
     * @param releaseId
     * @return
     */
    public static Boolean checkBaseModule(KieRepository kieRepository, RuleGroup ruleGroup, ReleaseId releaseId){
        MemoryKieModule kieModule = (MemoryKieModule)kieRepository.getKieModule(releaseId);
        return kieModule.getKieModuleModel().getKieBaseModels().containsKey(getKieBaseModelName(ruleGroup.getId()));
    }

    /**
     * 添加規(guī)則文件及創(chuàng)建模塊化組件并添加到文件系統(tǒng)中,此處為后期動(dòng)態(tài)添加,該文件系統(tǒng)與初始化時(shí)的文件系統(tǒng)不是同一個(gè)
     * @param kieContainer
     * @param kieRepository
     * @param ruleGroup
     * @return
     */
    public static StatelessKieSession addBaseModuleAndRule(KieContainer kieContainer, KieRepository kieRepository, RuleGroup ruleGroup){
        MemoryKieModule kieModule = (MemoryKieModule)kieRepository.getKieModule(kieContainer.getReleaseId());
        MemoryFileSystem memoryFileSystem = kieModule.getMemoryFileSystem();
        for (Rule rule : ruleGroup.getRules()) {
            String ruleContent = buildRule(ruleGroup, rule);
            memoryFileSystem.write(getRuleFilePath(ruleGroup.getId(), rule.getId()), ruleContent.getBytes(Charset.defaultCharset()));
        }
        KieBaseModel kieBaseModel = buildKieBaseModel(kieModule.getKieModuleModel(), ruleGroup, false);
        KieProject kieProject = ((KieContainerImpl) kieContainer).getKieProject();
        ((KieContainerImpl) kieContainer).updateToKieModule(kieModule);
        kieProject.buildKnowledgePackages((KieBaseModelImpl) kieBaseModel,new ResultsImpl());
        return buildKieSession(kieContainer, ruleGroup);
    }

    /**
     * 從文件系統(tǒng)中刪除規(guī)則文件及刪除模塊化組件,此處為后期動(dòng)態(tài)添加,該文件系統(tǒng)與初始化時(shí)的文件系統(tǒng)不是同一個(gè)
     * @param kieContainer
     * @param kieRepository
     * @param ruleGroup
     */
    public static void removeBaseModuleAndRule(KieContainer kieContainer, KieRepository kieRepository, RuleGroup ruleGroup){
        MemoryKieModule kieModule = (MemoryKieModule)kieRepository.getKieModule(kieContainer.getReleaseId());
        MemoryFileSystem memoryFileSystem = kieModule.getMemoryFileSystem();
        for (Rule rule : ruleGroup.getRules()) {
            memoryFileSystem.remove(getRuleFilePath(ruleGroup.getId(), rule.getId()));
        }
        String kieBaseModelName = getKieBaseModelName(ruleGroup.getId());
        String kieSessionModelName = getKieSessionModelName(ruleGroup.getId());
        KieModuleModel kieModuleModel = kieModule.getKieModuleModel();
        kieModuleModel.getKieBaseModels().get(kieBaseModelName).removeKieSessionModel(kieSessionModelName);
        kieModuleModel.removeKieBaseModel(kieBaseModelName);
        //偽刪除builder,其實(shí)刪不刪都一樣,同名時(shí)會(huì)覆蓋,源碼也沒有提供刪除方法
        kieModule.cacheKnowledgeBuilderForKieBase(kieBaseModelName,null);
        kieModule.getKnowledgeResultsCache().remove(kieBaseModelName);
        ((KieContainerImpl) kieContainer).updateToKieModule(kieModule);
    }

    /**
     * 規(guī)則構(gòu)建
     * @param ruleGroup
     * @param rule
     * @return
     */
    private static String buildRule(RuleGroup ruleGroup, Rule rule){
        StringBuilder ruleContent = new StringBuilder(PACKAGE);
        ruleContent.append(getPackageName(ruleGroup.getId())).append("\n");
        ruleContent.append(rule.getContent());
        return ruleContent.toString();
    }

    /**
     * Base模塊構(gòu)建
     * @param kieModuleModel
     * @param ruleGroup
     * @param isDefault
     * @return
     */
    private static KieBaseModel buildKieBaseModel(KieModuleModel kieModuleModel, RuleGroup ruleGroup, Boolean isDefault){
        KieBaseModel kieBaseModel = kieModuleModel.newKieBaseModel(getKieBaseModelName(ruleGroup.getId()));
        kieBaseModel.setDefault(isDefault);
        kieBaseModel.addPackage(getPackageName(ruleGroup.getId()));
        //只要無狀態(tài)session
        KieSessionModel kieSessionModel = kieBaseModel.newKieSessionModel(getKieSessionModelName(ruleGroup.getId()));
        kieSessionModel.setDefault(isDefault);
        kieSessionModel.setType(KieSessionModel.KieSessionType.STATELESS);
        return kieBaseModel;
    }

    private static StatelessKieSession buildKieSession(KieContainer kieContainer, RuleGroup ruleGroup){
        return kieContainer.newStatelessKieSession(getKieSessionModelName(ruleGroup.getId()));
    }

    private static String getPackageName(String id){
        return PACKAGE_NAME_PREFIX + id;
    }

    private static String getRuleFilePath(String id, String ruleId){
        return PACKAGE_PATH_PREFIX + id + FILE_SEPARATOR + ruleId + FILE_SUFFIX;
    }

    private static String getKieBaseModelName(String id){
        return KIE_BASE_MODEL_NAME_PREFIX + id;
    }

    private static String getKieSessionModelName(String id){
        return KIE_SESSION_MODEL_NAME_PREFIX + id;
    }

}

添加DroolsManager類

新建manager包,并在manager包下新建DroolsManager.java,并將下面內(nèi)容復(fù)制到該類中

package manager;

import static builder.KieModuleBuilder.*;

import entity.Rule;
import entity.RuleGroup;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.StatelessKieSession;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class DroolsManager {

    public static Map<RuleGroup, StatelessKieSession> statelessKieSessionMap;

    private static KieContainer kieContainer;

    private static List<RuleGroup> ruleGroups;

    /**
     * 初始化
     */
    public static void init() {
        initData();
        initSystemProperties();
        KieServices kieServices = KieServices.Factory.get();
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        if (ruleGroups != null && ruleGroups.size() > 0){
            initAndBuildKieModuleModel(kieServices, kieFileSystem, ruleGroups);
            initAndBuildRules(kieFileSystem, ruleGroups);
            KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
            kieBuilder.buildAll();
            kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
            statelessKieSessionMap = initStatelessKieSession(kieContainer, ruleGroups);
        }
    }

    /**
     * 添加組規(guī)則
     * @param ruleGroup
     */
    public static void addModuleAndRule(RuleGroup ruleGroup){
        if (kieContainer == null){ //規(guī)則引擎未被初始化
            init();
            return;
        }
        if (ruleGroup != null && !checkBaseModule(KieServices.Factory.get().getRepository(), ruleGroup, kieContainer.getReleaseId())){
            statelessKieSessionMap.put(ruleGroup,addBaseModuleAndRule(kieContainer, KieServices.Factory.get().getRepository(), ruleGroup));
        }
    }

    /**
     * 刪除組規(guī)則
     * @param id
     * @return
     */
    public static RuleGroup removeModuleAndRule(String id){
        if (kieContainer == null) { //規(guī)則引擎未被初始化
            init();
            return getRuleGroupById(id);
        }
        RuleGroup ruleGroup = getRuleGroupById(id);
        if (ruleGroup != null && checkBaseModule(KieServices.Factory.get().getRepository(), ruleGroup, kieContainer.getReleaseId())){
            removeBaseModuleAndRule(kieContainer, KieServices.Factory.get().getRepository(), ruleGroup);
            statelessKieSessionMap.remove(ruleGroup);
        }
        return ruleGroup;
    }

    /**
     * 初始化drools時(shí)間格式化格式
     */
    private static void initSystemProperties(){
        System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 初始化數(shù)據(jù)
     */
    private static void initData(){
        ruleGroups = new ArrayList<>();
        //組一
        RuleGroup ruleGroup1 = new RuleGroup("1","ruleGroupOne");
        Rule rule1_1 = new Rule("1_1","ruleOne");
        rule1_1.setContent("rule \"test1_1\"\n    when\n        eval(true)\n    then\n        System.out.println(\"規(guī)則中打印日志:校驗(yàn)通過!1_1\");\nend");
        ruleGroup1.getRules().add(rule1_1);
        Rule rule1_2 = new Rule("1_2","ruleOne");
        rule1_2.setContent("rule \"test1_2\"\n    when\n        eval(true)\n    then\n        System.out.println(\"規(guī)則中打印日志:校驗(yàn)通過!1_2\");\nend");
        ruleGroup1.getRules().add(rule1_2);
        ruleGroups.add(ruleGroup1);
        //組二
        RuleGroup ruleGroup2 = new RuleGroup("2","ruleGroupTwo");
        Rule rule2_1 = new Rule("2_1","ruleTwo");
        rule2_1.setContent("rule \"test2_1\"\n    when\n        eval(true)\n    then\n        System.out.println(\"規(guī)則中打印日志:校驗(yàn)通過!2_1\");\nend");
        ruleGroup2.getRules().add(rule2_1);
        ruleGroups.add(ruleGroup2);
    }

    /**
     * 根據(jù)組id獲取組信息
     * @param id
     * @return
     */
    private static RuleGroup getRuleGroupById(String id){
        for (Map.Entry<RuleGroup, StatelessKieSession> kieSessionEntry : statelessKieSessionMap.entrySet()) {
            if (kieSessionEntry.getKey().getId().equals(id)) {
                return kieSessionEntry.getKey();
            }
        }
        return null;
    }

}

創(chuàng)建Main方法運(yùn)行

新建一個(gè)類,并將下面內(nèi)容復(fù)制到該類中

public static void main(String[] args) {
    //初始化規(guī)則引擎
    DroolsManager.init();
    execute();
    System.out.println("------------------------------");
    //移除組1
    RuleGroup ruleGroup = DroolsManager.removeModuleAndRule("1");
    execute();
    System.out.println("------------------------------");
    //添加組1
    DroolsManager.addModuleAndRule(ruleGroup);
    execute();
    System.out.println("------------------------------");
}

/**
 * 執(zhí)行規(guī)則引擎
 */
private static void execute(){
    for (Map.Entry<RuleGroup, StatelessKieSession> kieSessionEntry : DroolsManager.statelessKieSessionMap.entrySet()) {
        kieSessionEntry.getValue().execute(CommandFactory.newInsert(""));
    }
}

運(yùn)行結(jié)果如下

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.
規(guī)則中打印日志:校驗(yàn)通過!1_2
規(guī)則中打印日志:校驗(yàn)通過!1_1
規(guī)則中打印日志:校驗(yàn)通過!2_1
------------------------------
規(guī)則中打印日志:校驗(yàn)通過!2_1
------------------------------
規(guī)則中打印日志:校驗(yàn)通過!2_1
規(guī)則中打印日志:校驗(yàn)通過!1_2
規(guī)則中打印日志:校驗(yàn)通過!1_1
------------------------------

Process finished with exit code 0

注意:當(dāng)前只實(shí)現(xiàn)了將整個(gè)KieBaseModel移除,前面章節(jié)說過一個(gè)KieBaseModel下面是包含多條規(guī)則的,也就是說當(dāng)我們移除組1的時(shí)候會(huì)把組1下面的兩條規(guī)則都進(jìn)行移除,所以移除組1后只會(huì)有一條規(guī)則被輸出。

針對一組里面單一規(guī)則的增刪改功能已經(jīng)實(shí)現(xiàn)但代碼目前沒有整理,后續(xù)再更新吧

場景

由于規(guī)則引擎的特性when里面是判斷邏輯,then里面可以寫業(yè)務(wù)處理邏輯,因此規(guī)則引擎的使用場景目前個(gè)人認(rèn)為有兩種(僅為個(gè)人觀點(diǎn))

第一種是判斷條件簡單但存在多種可能性的且隨時(shí)都會(huì)進(jìn)行調(diào)整的場景,比如有5個(gè)屬性ABCDE,只要滿足條件(A=B || B=C || C=D || D=E || A=B=C || B=C=E....)的就返回結(jié)果,甚至有可能不同的條件搭配會(huì)生成特定的結(jié)果需要把他們的所有結(jié)果都收集起來,這時(shí)如果使用代碼是不現(xiàn)實(shí)的

第二種數(shù)據(jù)庫表邏輯查詢,現(xiàn)在有一張表里面有10個(gè)字段,且數(shù)據(jù)很少發(fā)生變化基本只有大量查詢,要求入?yún)BC去查這張表,表中數(shù)據(jù)類型為1的符合A=字段1即可,類型為2的要符合A=字段1且B=字段2,類型為3的要符合A=字段1且B=字段2且C=字段3....等等,SQL的查詢也是不現(xiàn)實(shí)的,這時(shí)可以選擇規(guī)則引擎,將表中的數(shù)據(jù)拼接成drl,when的條件判斷語句就是每條數(shù)據(jù)各自符合的條件,then邏輯則返回該數(shù)據(jù)的主鍵或數(shù)據(jù)本身,這時(shí)填充入?yún)⒈隳艿玫椒蠗l件的所有數(shù)據(jù)

總結(jié)

Drools是一個(gè)強(qiáng)大規(guī)范的規(guī)則引擎,以上只是它的部分功能應(yīng)用,它還支持工作流等功能,在使用前建議還是要對其有一定的了解,因?yàn)橛玫娜吮容^少網(wǎng)上資料并不多且大多數(shù)是幾年前的,否則只會(huì)寸步難行,為了配合規(guī)則引擎的使用而使用是一件令人蛋疼的事

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容