天天看点

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>
           

希望能帮助到大家。

继续阅读