欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

drools实现动态加载更新指定规则文件

程序员文章站 2022-03-03 15:54:48
...

背景:

    需要实现规则的动态发布,更新,删除等动作。且规则文件有很多个,希望根据文件名称触发指定规则。

实现:

    研究了一下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>
希望能帮助到大家。