天天看點

drools實作動态加載更新指定規則檔案

背景:

    需要實作規則的動态釋出,更新,删除等動作。且規則檔案有很多個,希望根據檔案名稱觸發指定規則。

實作:

    研究了一下drools,網上結合網上找的資料和自己研究,實作如下:

    定義drools的bean

package cn.mime.rule.platform.engine.config;

import org.kie.api.KieServices;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.model.KieModuleModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DroolsConfig {

    @Bean
    KieServices getKieService() {
        KieServices kieServices = KieServices.Factory.get();
        return kieServices;
    }

    @Bean
    KieRepository getKieRepository() {
        KieServices kieServices = KieServices.Factory.get();
        return kieServices.getRepository();
    }

    @Bean
    KieModuleModel getKieModule() {
        KieServices kieServices = KieServices.Factory.get();
        return kieServices.newKieModuleModel();
    }

    @Bean
    KieFileSystem getKieFileSystem() {
        KieServices kieServices = KieServices.Factory.get();
        return kieServices.newKieFileSystem();
    }

}
           

當然你也可以定義在你需要的地方,隻要保證能夠使用相同的對象就行了,不能每次都kieServices.newKieFileSystem() 這樣出來是不同的對象。會覆寫之前的規則檔案等。

package cn.mime.rule.platform.engine.business;


import cn.mime.platform.regular.RuleResult;
import cn.mime.rule.platform.common.model.engine.EventParam;
import cn.mime.rule.platform.common.model.rpm.EventRd;

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

public interface DroolsBusiness {

    /**
     * build所有規則
     */
    void buildAllRules();

    /**
     * build規則
     * @param
     */
    void buildRules(String fileName, String code);

    /**
     * 删除規則
     * @param fileName
     */
    void deleteRules(String fileName);

    /**
     * 觸發規則
     * @param eventParam
     */
    List<RuleResult> fireRules(Map<String, Object> eventParam, String packageName);

}
           

實作

package cn.mime.rule.platform.engine.business.impl;


import cn.mime.platform.regular.RuleFunc;
import cn.mime.platform.regular.RuleParam;
import cn.mime.platform.regular.RuleResult;
import cn.mime.rule.platform.common.Constant;
import cn.mime.rule.platform.engine.business.DroolsBusiness;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.kie.api.KieBaseConfiguration;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.io.KieResources;
import org.kie.api.io.Resource;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.ObjectFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

@Component
public class DroolsBusinessImpl implements DroolsBusiness {

    private static final Logger LOGGER = LoggerFactory.getLogger(DroolsBusinessImpl.class);


    @Value("${drl.path}")
    private String drlPath;

    /**
     * 加載規則目錄
     */
    private final String BUILD_PATH = "src/main/resources/";

    /**
     * Kiebase
     */
    private final String KEI_BASE = "kieBase";

    /**
     * Kiebase
     */
    private final String SESSION = "session_";

    @Autowired
    private KieServices kieServices;

    @Autowired
    private KieFileSystem kieFileSystem;

    @Autowired
    private KieModuleModel kieModuleModel;

    @Autowired
    private KieRepository kieRepository;

    @Override
    public void buildAllRules() {
        LOGGER.info("build rule start");
        File filePath = new File(drlPath);
        if(!filePath.exists()) {
            filePath.mkdir();
        }
        Iterator<File> files = FileUtils.iterateFiles(filePath, null, false);
        while(files.hasNext()) {
            File file = files.next();
            if(file.getName().endsWith(".drl")) {
                LOGGER.info("build file with filename {}", file.getName());
                String packageName = file.getName().split("\\.")[0];
                KieBaseModel kieBaseModel = kieModuleModel.newKieBaseModel(KEI_BASE + packageName);
                kieBaseModel.addPackage(packageName);
                kieBaseModel.newKieSessionModel(SESSION + packageName);
                try {
                    kieFileSystem.write(BUILD_PATH+"/"+packageName+"/"+file.getName(), FileUtils.readFileToByteArray(file));
                } catch (IOException e) {
                    LOGGER.error("read file error with file name {}", file.getName());
                }
            }
        }
        //這邊主要目的是定義keyModel讓不同的package對應不同的session,這樣可以隻觸發某個session下的規則
        String xml = kieModuleModel.toXML();
        kieFileSystem.writeKModuleXML(xml);
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        //裝載至庫
        kieBuilder.buildAll();
        if (kieBuilder.getResults().hasMessages(Message.Level.ERROR)) {
            LOGGER.error("build rules error {}", kieBuilder.getResults().toString());
        }
        LOGGER.info("build rule end");
    }

    @Override
    public void buildRules(String fileName, String code) {
        if(StringUtils.isEmpty(fileName) || StringUtils.isEmpty(code)) {
            return;
        }
        LOGGER.info("build file with filename {}", fileName);
        KieBaseModel kieBaseModel = kieModuleModel.newKieBaseModel(KEI_BASE + fileName);
        kieBaseModel.addPackage(fileName);
        kieBaseModel.newKieSessionModel(SESSION + fileName);
        kieFileSystem.write(BUILD_PATH+"/"+fileName+"/"+fileName+".drl", code);
        //這邊主要目的是定義keyModel讓不同的package對應不同的session,這樣可以隻觸發某個session下的規則
        String xml = kieModuleModel.toXML();
        kieFileSystem.writeKModuleXML(xml);
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        //裝載至庫
        kieBuilder.buildAll();
        if (kieBuilder.getResults().hasMessages(Message.Level.ERROR)) {
            LOGGER.error("build rules error {}", kieBuilder.getResults().toString());
        }
    }

    @Override
    public void deleteRules(String fileName) {
        kieModuleModel.removeKieBaseModel(KEI_BASE + fileName);
        String xml = kieModuleModel.toXML();
        kieFileSystem.writeKModuleXML(xml);
        kieFileSystem.delete(BUILD_PATH+"/"+fileName+"/"+fileName+".drl");
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        //裝載至庫
        kieBuilder.buildAll();
        if (kieBuilder.getResults().hasMessages(Message.Level.ERROR)) {
            LOGGER.error("build rules error {}", kieBuilder.getResults().toString());
        }
    }

    @Override
    public List<RuleResult> fireRules(Map<String, Object> eventParam, String packageName) {
        LOGGER.info("fire rule start with packageName {}  param {} ", packageName, eventParam);
        KieContainer kContainer = kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
        KieSession kSession = kContainer.newKieSession(SESSION + packageName);
        kSession.setGlobal("ruleFunc", new RuleFunc());
        kSession.insert(getRuleParam(eventParam, packageName));
        kSession.fireAllRules();
        Collection c = kSession.getObjects(new ObjectFilter() {
            public boolean accept(Object object) {
                if(object instanceof RuleResult) {
                    return true;
                } else {
                    return false;
                }
            }
        });
        kSession.dispose();
        return new ArrayList<RuleResult>(c);
    }


    /**
     * 生成規則入參
     * @param eventParam
     * @return
     */
    private RuleParam getRuleParam(Map<String, Object> eventParam, String packageName) {
        RuleParam ruleParam = new RuleParam();
        ruleParam.putAll(eventParam);
        ruleParam.put(Constant.Field.RPM_CATEGORY_NAME, packageName);
        return ruleParam;
    }

}
           

幾點說明的是:

    buildAllRules 方法是我這邊容器啟動的時候會去加載所有路徑下的檔案

    buildRules 方法是加載某個指定檔案

    kieModuleModel 和KieBaseModel 的作用是生成類似這樣的xml,并加載,其目的是實作不同的package對應不用的ksession,這樣我在觸發的時候隻要指定不同的ksession就能觸發不同的規則包下面的規則。不同的規則不會影響。

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://www.drools.org/xsd/kmodule">
  <kbase name="kieBase規則0" default="false" eventProcessingMode="cloud" equalsBehavior="identity" declarativeAgenda="disabled" scope="javax.enterprise.context.ApplicationScoped" packages="規則0">
    <ksession name="session_規則0" type="stateful" default="false" clockType="realtime" beliefSystem="simple" scope="javax.enterprise.context.ApplicationScoped"/>
  </kbase>
  <kbase name="kieBase規則1" default="false" eventProcessingMode="cloud" equalsBehavior="identity" declarativeAgenda="disabled" scope="javax.enterprise.context.ApplicationScoped" packages="規則1">
    <ksession name="session_規則1" type="stateful" default="false" clockType="realtime" beliefSystem="simple" scope="javax.enterprise.context.ApplicationScoped"/>
  </kbase>
</kmodule>
           

希望能幫助到大家。

繼續閱讀