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>
希望能帮助到大家。