Drools規則引擎接入詳解

Drools是Jboss開源的一個規則引擎,簡單來說就是一種運算速度極快且配置非常靈活的計算工具,這篇博客不分析底層實現了,因爲互聯網上面隨便一搜就能發現很多介紹,但是真正去使用的博客卻太少了,也許真正用的很深的不多大家都只是抄來抄去那些原理

公司最近正好需要這麼一個服務我們便開始研究起來並最終上線,其實在實踐過程中遇到很多的坑,並且由於網上解決資料非常少導致耽誤了不少時間,在這裏寫出來也是爲了幫助後續的小夥伴排雷,Jboss官方文檔中提到得接入方式其實分爲2種:

1.離線接入(下載Drools官方jar使用內部jar得API進行)

2.Kie-Server接入(搭建官方集羣,作爲client接入Drools服務)

 

兩種做法都有優缺點:

離線/Kie接入對比
  離線 Kie
接入難度
分佈式集羣 官方jar支持很弱,需要自己二次開發 支持
性能 極高,因爲本地內存直接運算 較高,有了網絡交互得消耗,損失部分性能
擴展性 低,基本都需要自己二次開發

 

從上面這個表格可以看出初學者入門使用離線方式是最好得,不過即使使用這種方式由於中文文檔得缺失其實也是比較複雜得,我們公司當時前期使用得是第一種方式

那麼開始進入主題,如何快速搭建Drools並且使用,公司使用的是Drools 7.7版本

POM文件配置加入:

        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-core</artifactId>
            <version>7.7.0.Final</version>
            <exclusions>
                <exclusion>
                    <artifactId>mvel2</artifactId>
                    <groupId>org.mvel</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>7.7.0.Final</version>
            <exclusions>
                <exclusion>
                    <artifactId>protobuf-java</artifactId>
                    <groupId>com.google.protobuf</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-decisiontables</artifactId>
            <version>7.7.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-templates</artifactId>
            <version>7.7.0.Final</version>
        </dependency>

Drools有動態和靜態規則兩種,我們公司採用的是動態規則,利用Drools的API進行開發將規則原始數據存儲到Mysql中,然後動態加入Drools的工作內存並計算,其實原理是一樣得,都是採用DRL規則語法,只是一個是靜態加載DRL文件一個是動態內存中生成DRL規範得字符串

Drools 中文文檔網站:http://www.drools.org.cn/category/use   (版本比較老了,但是是爲數不多得中文文檔)

Drools 官方網站:https://www.drools.org/    (可以下載最新版官方接入document,但是是全英語版本得)

 

公司存放規則得數據庫sql如下:

/*
 Navicat Premium Data Transfer

 Source Server         : wms4-test-shard
 Source Server Type    : MySQL
 Source Server Version : 50718
 Source Host           : 10.88.27.117:3306
 Source Schema         : drools

 Target Server Type    : MySQL
 Target Server Version : 50718
 File Encoding         : 65001

 Date: 18/06/2019 15:14:41
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for factremarks
-- ----------------------------
DROP TABLE IF EXISTS `factremarks`;
CREATE TABLE `factremarks` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `attribute` varchar(200) NOT NULL,
  `remarks` varchar(200) NOT NULL,
  `type` varchar(200) NOT NULL,
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for rulecondition
-- ----------------------------
DROP TABLE IF EXISTS `rulecondition`;
CREATE TABLE `rulecondition` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `ruleInfo_id` bigint(20) NOT NULL,
  `conditionKey` varchar(200) DEFAULT NULL,
  `op` varchar(200) NOT NULL,
  `conditionValue` varchar(200) DEFAULT NULL,
  `associationType` varchar(200) DEFAULT NULL,
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  `paramType` varchar(200) DEFAULT NULL,
  `type` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `ruleInfo_id` (`ruleInfo_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=404 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for ruleglobal
-- ----------------------------
DROP TABLE IF EXISTS `ruleglobal`;
CREATE TABLE `ruleglobal` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `rule_id` bigint(20) NOT NULL,
  `globalName` varchar(40) NOT NULL,
  `globalType` varchar(40) NOT NULL,
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for rulegroup
-- ----------------------------
DROP TABLE IF EXISTS `rulegroup`;
CREATE TABLE `rulegroup` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `groupCode` varchar(40) NOT NULL,
  `groupName` varchar(40) DEFAULT '',
  `description` varchar(200) DEFAULT '',
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  `ou_id` varchar(30) DEFAULT NULL,
  `type` int(11) DEFAULT NULL COMMENT '規則組屬於什麼模塊  1-上架',
  `groupType` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `groupCode` (`groupCode`) USING BTREE,
  KEY `ouid` (`ou_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for rulegroupref
-- ----------------------------
DROP TABLE IF EXISTS `rulegroupref`;
CREATE TABLE `rulegroupref` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `rule_id` bigint(20) NOT NULL,
  `ruleGroup_id` bigint(20) NOT NULL,
  `orderNo` int(11) DEFAULT NULL,
  `state` int(11) DEFAULT NULL,
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `ruleGroup_id` (`ruleGroup_id`),
  KEY `rule_Id` (`rule_id`),
  KEY `state` (`state`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=151 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for rulehead
-- ----------------------------
DROP TABLE IF EXISTS `rulehead`;
CREATE TABLE `rulehead` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `ruleName` varchar(100) NOT NULL,
  `packageName` varchar(40) NOT NULL,
  `remarks` varchar(200) DEFAULT NULL,
  `ruleString` varchar(60) DEFAULT NULL,
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  `rule_str` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `ruleString` (`ruleString`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=122 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for ruleinfo
-- ----------------------------
DROP TABLE IF EXISTS `ruleinfo`;
CREATE TABLE `ruleinfo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `rule_id` bigint(20) NOT NULL,
  `type` varchar(40) NOT NULL,
  `parent` varchar(200) DEFAULT NULL,
  `obj` varchar(200) NOT NULL,
  `calculation` varchar(200) DEFAULT NULL,
  `attribute` varchar(200) DEFAULT NULL,
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `rule_id` (`rule_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=374 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for ruleop
-- ----------------------------
DROP TABLE IF EXISTS `ruleop`;
CREATE TABLE `ruleop` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `rule_id` bigint(20) NOT NULL,
  `objName` varchar(200) NOT NULL,
  `attribute` varchar(200) NOT NULL,
  `value` varchar(200) NOT NULL,
  `create_time` date DEFAULT NULL,
  `create_by` varchar(50) DEFAULT NULL,
  `update_by` varchar(50) DEFAULT NULL,
  `type` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `rule_id` (`rule_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=223 DEFAULT CHARSET=utf8;


ALTER TABLE `drools`.`rulecondition`
ADD COLUMN `brackets` varchar(200) NULL AFTER `type`;

ALTER TABLE `drools`.`ruleinfo`
ADD COLUMN `associationType` varchar(30) NULL AFTER `update_by`;


ALTER TABLE `drools`.`rulehead`
ADD COLUMN `frontEndData` text NULL AFTER `rule_str`;
SET FOREIGN_KEY_CHECKS = 1;

表結構說明文檔下載地址:https://download.csdn.net/download/wmq880204/11665077    

 

公司離線方式流程圖如下:

 

整個流程還是比較簡單易懂得,核心加載代碼如下

@Autowired
private RuleLoadManager ruleLoadManager;

@Autowired
private KnowLedgeBaseManger knowLedgeBaseManger;

//查出數據庫所有啓用狀態得規則並且裝載到Map結構中
Map<String, List<BaseResource>> map = ruleLoadManager.loadAllRules();
//開始循環所有規則數據加載至工作內存中
map.forEach((group, resourceList) -> knowLedgeBaseManger.createKnowledgeBase(resourceList, group));


KnowLedgeBaseManager 類實現方法

@Autowired
    private DroolsServicesBuilderFactory droolsServicesBuilderFactory;

    @Autowired
    private RuleLoadManager ruleLoadManager;

    @Autowired
    private KnowledgeBaseLib knowledgeBaseLib;

    /**
     * 根據傳入規則創建知識庫
     *
     * @param resources         規則
     * @param knowLedgeBaseName 知識庫名稱
     * @return
     */
    @Override
    public InternalKnowledgeBase createKnowledgeBase(List<BaseResource> resources, String knowLedgeBaseName) {
        KnowledgeBuilder knowledgeBuilder = droolsServicesBuilderFactory.createKnowledgeBuilder();
        InternalKnowledgeBase internalKnowledgeBase = droolsServicesBuilderFactory.createInternalKnowledgeBase();
        droolsServicesBuilderFactory.addRules( knowledgeBuilder, resources, ResourceType.DRL );
        BiConsumer<InternalKnowledgeBase, KnowledgeBuilder> baseBuilder = droolsServicesBuilderFactory::buliderBase;
        baseBuilder.accept( internalKnowledgeBase, knowledgeBuilder );
        return internalKnowledgeBase;
    }

    /**
     * 測試規則
     *
     * @param resources
     */
    @Override
    public KnowledgeMessage testRule(List<BaseResource> resources) {
        KnowledgeBuilder knowledgeBuilder = droolsServicesBuilderFactory.createKnowledgeBuilder();
        KnowledgeMessage knowledgeMessage = null;
        try {
            droolsServicesBuilderFactory.addRules( knowledgeBuilder, resources, ResourceType.DRL );
            knowledgeBuilder.undo();
            knowledgeMessage = new KnowledgeMessage( "", SUCCESS, null );
        } catch (KnowLedgeBuilderException ruleException) {
            knowledgeMessage = new KnowledgeMessage( "", FAILED, ruleException.toString() );
        }
        return knowledgeMessage;
    }

    /**
     * 重載全部知識庫
     */
    @Override
    public void reloadRules() {
        knowledgeBaseLib.getAllInternalKnowledgeBase().forEach( (name, internalKnowledgeBase) -> internalKnowledgeBase.getKieSessions().forEach( kieSession -> kieSession.destroy() ) );
        knowledgeBaseLib.clearAllInternalKnowledgeBase();
        ruleLoadManager.loadAllRules().forEach( (knowledgeBaseName, resources) -> createKnowledgeBase( resources, knowledgeBaseName ) );
    }

    @Override
    public void reloadRulesByName(String knowLedgeBaseName) {
        knowledgeBaseLib.removeInternalKnowledgeBase( knowLedgeBaseName );
        reloadRules();
    }

    /**
     * 重載特定知識庫規則
     *
     * @param knowLedgeBaseName
     */
    @Override
    public InternalKnowledgeBase reloadRule(String knowLedgeBaseName) {
        knowledgeBaseLib.getInternalKnowledgeBase( knowLedgeBaseName ).getKieSessions().forEach( kiesession -> kiesession.destroy() );
        knowledgeBaseLib.removeInternalKnowledgeBase( knowLedgeBaseName );
        createKnowledgeBase( ruleLoadManager.loadRule( knowLedgeBaseName ), knowLedgeBaseName );
        return knowledgeBaseLib.getInternalKnowledgeBase( knowLedgeBaseName );
    }

    /**
     * 釋放session資源
     *
     * @param kieSession
     */
    @Override
    public void disposeKieSession(KieSession kieSession) {
        kieSession.dispose();
    }

    /**
     * 銷燬session
     *
     * @param kieSession
     */
    @Override
    public void destoryKieSession(KieSession kieSession) {
        kieSession.destroy();
    }

   DroolsServicesBuilderFactory 類實現方法

private static final Logger log = LoggerFactory.getLogger(DroolsServicesBuilderFactory.class);

    @Autowired
    private CustomKieBaseConfiguration customKieBaseConfiguration;

    @Autowired
    private RuleHeadDao ruleHeadDao;

    /**
     * 知識規則的構建器
     *
     * @return
     */
    public KnowledgeBuilder createKnowledgeBuilder() {
        return KnowledgeBuilderFactory.newKnowledgeBuilder();
    }

    /**
     * 添加規則
     * @param knowledgeBuilder
     * @param resources
     * @param type
     */
    public void addRules(KnowledgeBuilder knowledgeBuilder, List<BaseResource> resources,
                                    ResourceType type) {
        resources.forEach(resource -> {
            try {
                log.info( new String( resource.getBytes(), StandardCharsets.UTF_8 ) );
                knowledgeBuilder.add(resource,type);
            } catch (Exception e) {
                log.error( "addRuleError exction is {}",e );
            }
        });
        if(knowledgeBuilder.hasErrors()){
            log.error( "addRuleError is {}",knowledgeBuilder.getErrors().toString() );
        }
    }

    /**
    * @author QIQI
    * @Description: 根據ruleId刪除規則
    * @params [ruleId]
    * @return void
    * @throws 
    * @date 2019-06-21 21:37 

    @LmisDataSource( "ds4" )
    public void removeRule(Long ruleId){
        RuleSaveModel groupCode = ruleHeadDao.getRuleInfoById(Integer.parseInt( String.valueOf( ruleId ) ));
        KnowledgeBuilder knowledgeBuilder = createKnowledgeBuilder();
        knowledgeBuilder.newKieBase().removeRule( FinalArgs.RULE_PACKAGE,groupCode.getRuleName() );
    }*/

    /**
     * 創建內部支持庫
     * @return
     */
    public InternalKnowledgeBase createInternalKnowledgeBase(){
        KieBaseConfiguration kbc = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
        customKieBaseConfiguration.getKieBaseconfiguration().forEach((key,value)->kbc.setProperty(key,value));
        return KnowledgeBaseFactory.newKnowledgeBase(kbc);
    }

    /**
     * 構建基礎庫
     * @param internalKnowledgeBase
     * @param knowledgeBuilder
     */
    public void buliderBase(InternalKnowledgeBase internalKnowledgeBase,KnowledgeBuilder knowledgeBuilder){
        internalKnowledgeBase.addPackages(knowledgeBuilder.getKnowledgePackages());
    }

    /**
     * 創建有狀態session
     * @param internalKnowledgeBase
     * @return
     */
    public KieSession createKieSession(InternalKnowledgeBase internalKnowledgeBase){
        Supplier<KieSession> kieSessionSupplier = internalKnowledgeBase::newKieSession;
        return kieSessionSupplier.get();
    }

    /**
     * 創建無狀態session
     * @param internalKnowledgeBase
     * @return
     */
    public StatelessKieSession createStatelessKieSession(InternalKnowledgeBase internalKnowledgeBase){
        Supplier<StatelessKieSession> kieSessionSupplier = internalKnowledgeBase::newStatelessKieSession;
        return kieSessionSupplier.get();
    }

    /**
     * 釋放session
     * @param kieSession
     */
    public void disposeKieSession(KieSession kieSession){
        kieSession.dispose();
    }


    /**
     * 銷燬Kiession
     * @param kieSession
     */
    public void destroyKiession(KieSession kieSession){
        kieSession.destroy();
    }

KnowledgeBaseLib 類實現方法

private Logger logger = LogManager.getLogger(KnowledgeBaseLib.class);

    private Map<String,InternalKnowledgeBase> internalKnowledgeBaseMap = new ConcurrentHashMap<>();

    private static final String NORULEEXCEPTION = "not found any rule exception";

    @Autowired
    private RuleLoadManager ruleLoadManager;

    @Autowired
    private KnowLedgeBaseManger knowLedgeBaseManger;

    /**
     * 添加知識庫到容器
     * @param knowLedgeBaseName
     * @param internalKnowledgeBase
     */
    public void addInternalKnowledgeBase(String knowLedgeBaseName,InternalKnowledgeBase internalKnowledgeBase){
        this.internalKnowledgeBaseMap.put(knowLedgeBaseName,internalKnowledgeBase);
    }

    /**
     * 獲得知識庫
     * @param knowLedgeBaseName
     * @return
     */
    public InternalKnowledgeBase getInternalKnowledgeBase(String knowLedgeBaseName){
        if(hasInternalKnowledgeBase(knowLedgeBaseName)){
            return this.internalKnowledgeBaseMap.get(knowLedgeBaseName);
        }else{
            List<BaseResource> resourceList = ruleLoadManager.loadRule(knowLedgeBaseName);
            if(resourceList.isEmpty()){
                logger.warn(NORULEEXCEPTION);
                throw new KnowLedgeBuilderException(NORULEEXCEPTION);
            }
            knowLedgeBaseManger.createKnowledgeBase(resourceList,knowLedgeBaseName);
            return this.internalKnowledgeBaseMap.get(knowLedgeBaseName);
        }
    }

    /**
     * 檢測是否存在知識庫
     * @param knowLedgeBaseName
     * @return
     */
    public boolean hasInternalKnowledgeBase(String knowLedgeBaseName){
        return this.internalKnowledgeBaseMap.containsKey(knowLedgeBaseName);
    }

    public Map<String,InternalKnowledgeBase> getAllInternalKnowledgeBase(){
        return this.internalKnowledgeBaseMap;
    }

    public void clearAllInternalKnowledgeBase(){
        this.internalKnowledgeBaseMap.clear();
    }

    public void removeInternalKnowledgeBase(String knowLedgeBaseName){
        this.internalKnowledgeBaseMap.remove(knowLedgeBaseName);
    }

CustomKieBaseConfiguration 類代碼

@Component
@ConfigurationProperties(prefix = "droolsconfig")
public class CustomKieBaseConfiguration {

    private Map<String,String> kieBaseconfiguration = new HashMap<>();

    public Map<String, String> getKieBaseconfiguration() {
        return kieBaseconfiguration;
    }

    public void setKieBaseconfiguration(Map<String, String> kieBaseconfiguration) {
        this.kieBaseconfiguration = kieBaseconfiguration;
    }
}

KnowLedgeBaseHandlerProxyConfig 知識庫初始化使用

@EnableAspectJAutoProxy
@Configuration
public class KnowLedgeBaseHandlerProxyConfig {

    /**
     * 用於添加知識庫
     * @return
     */
    @Bean
    public KnowLedgeBaseHandler knowLedgeBaseHandler(){
        return new KnowLedgeBaseHandler();
    }

}

KnowLedgeBaseHandler 知識庫切面,攔截知識庫進行存儲

/**
 * 用於攔截添加知識庫,將知識庫進行存儲
 */
@Aspect
@Order(2)
@Component
public class KnowLedgeBaseHandler {

    @Autowired
    private KnowledgeBaseLib knowledgeBaseLib;

    @Pointcut("execution(public * com.lmis.manager.knowledge.KnowLedgeBaseMangerImpl.*(..))")
    public void pointCut() {};

    @AfterReturning(value = "pointCut()", returning = "result")
    public void storeInternalKnowledgeBase(JoinPoint joinPoint, InternalKnowledgeBase result) {
        String name = "";
        try{
            name = joinPoint.getArgs()[1].toString();
        }catch(Exception e){
            name = joinPoint.getArgs()[0].toString();
        }
        if(!knowledgeBaseLib.hasInternalKnowledgeBase(name)){
            knowledgeBaseLib.addInternalKnowledgeBase(name,result);
        }
    }

}

DroolsConvertToResource 將數據庫查詢得數據轉換成Drools可認得Resource使用(這個類中包含了很多規則模板類,因爲是內存拼接DRL語句,所以肯定會製作很多DRL模板,這裏就不放出來了,有需要得可以去下載資源,資源地址:https://download.csdn.net/download/wmq880204/11665278

/**
 * Created with IntelliJ IDEA.
 * User: Dean Lu
 * Date: 9/18/18
 * Time: 9:52 AM
 * <p>
 * 用於將當前數據結構轉換爲Resource
 */
public final class DroolsConvertToResource {
    private DroolsConvertToResource(){}
    public static Map<String, List<BaseResource>> getResourceMap(List<RuleCommand> ruleCommands) {
        Map<String, List<BaseResource>> map = new HashMap<>();
        for (RuleCommand ruleCommand : ruleCommands) {
            configRule(map, ruleCommand);
        }

        return map;
    }

    public static void configRule(Map<String, List<BaseResource>> map, RuleCommand ruleCommand) {
        ruleCommand.getRuleGroupRefList().forEach(ruleGroupRef -> configRuleHead(map, ruleCommand, ruleGroupRef));
    }

    public static void configRuleHead(Map<String, List<BaseResource>> map, RuleCommand ruleCommand, RuleGroupRef ruleGroupRef) {
        ruleGroupRef.getRuleHeadList().forEach(ruleHead -> {
            ByteArrayResource byteArrayResource = null;
            if (null != ruleHead.getRuleString()) {
                CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder = configRuleHeaderTemplate(ruleCommand, ruleGroupRef, ruleHead);
                configRuleInfo(ruleHead, ceDescrBuilder);
                byteArrayResource = configRuleEndTemplate(ruleHead, ceDescrBuilder);
            } else {
                byteArrayResource = configRuleWithRuleString(ruleHead);
            }
            configResourceMap(map, byteArrayResource, ruleCommand.getGroupCode());
        });
    }

    public static ByteArrayResource configRuleWithRuleString(RuleHead ruleHead) {
        ByteArrayResource byteArrayResource;
        StringTemplate stringTemplate = new StringTemplate();
        byteArrayResource = stringTemplate.setStringTemplate(ruleHead.getRuleString());
        return byteArrayResource;
    }

    public static ByteArrayResource configRuleEndTemplate(RuleHead ruleHead, CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder) {
        ByteArrayResource byteArrayResource;
        StringBuilder op = configRuleOp(ruleHead);
        EndTemplate endTemplate = new EndTemplate(ceDescrBuilder, op.toString());
        byteArrayResource = endTemplate.returnRuleString();
        return byteArrayResource;
    }

    public static CEDescrBuilder<RuleDescrBuilder, AndDescr> configRuleHeaderTemplate(RuleCommand ruleCommand, RuleGroupRef ruleGroupRef, RuleHead ruleHead) {
        Map<String, String> ruleGlobals = configRuleGlobal(ruleHead);
        HanderTemplate handerTemplate = new HanderTemplate(ruleHead.getPackageName(), ruleHead.getRuleName(), ruleGlobals, ruleCommand.getGroupCode(), String.valueOf(ruleGroupRef.getOrderNo()));
        return handerTemplate.getCeDescrBuilder();
    }

    public static StringBuilder configRuleOp(RuleHead ruleHead) {
        StringBuilder op = new StringBuilder();
        ruleHead.getRuleOpList().forEach(ruleOp -> {
            if (BaseOperationTemplate.SIMPLEOPERATIONTYPE.equals(ruleOp.getType())) {
                SimpleOperationTemplate simpleOperationTemplate = new SimpleOperationTemplate();
                op.append(simpleOperationTemplate.opTemplate(ruleOp.getObjName(), ruleOp.getAttribute(), ruleOp.getValue()));
            }
            if (BaseOperationTemplate.STRINGYPE.equals(ruleOp.getType())) {
                StringOperationTemplate stringOperationTemplate = new StringOperationTemplate();
                op.append(stringOperationTemplate.opTemplate(null, null, ruleOp.getValue()));
            }
        });
        return op;
    }

    public static void configRuleInfo(RuleHead ruleHead, CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder) {
        List<RuleInfo> ruleInfos = ruleHead.getRuleInfoList();
        for (RuleInfo ruleInfo : ruleInfos) {
            configRuleInfo(ceDescrBuilder, ruleInfo);
        }
    }

    public static Map<String, String> configRuleGlobal(RuleHead ruleHead) {
        Map<String, String> ruleGlobals = new HashMap<>();
        ruleHead.getRuleGlobalList().forEach(ruleGlobal -> ruleGlobals.put(ruleGlobal.getGlobalName(), ruleGlobal.getGlobalType()));
        return ruleGlobals;
    }

    public static void configResourceMap(Map<String, List<BaseResource>> map, ByteArrayResource byteArrayResource, String ruleGroupCode) {
        List<BaseResource> list = map.get(ruleGroupCode);
        if (null == list) {
            list = new ArrayList<>();
            list.add(byteArrayResource);
            map.put(ruleGroupCode, list);
        } else {
            list.add(byteArrayResource);
        }
    }


    public static void  configRuleInfo(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo) {
        //各種主模板的判斷加入
        List<BaseConditionTemplate> conditionList = buildCondition(ruleInfo);
        if (SimpleRuleTemplate.TYPE.equals(ruleInfo.getType())) {
            configSimpleRuleTemplate(ceDescrBuilder, ruleInfo, conditionList);
        }
        else if (NestingCountTemplate.TYPE.equals(ruleInfo.getType())) {
            configNestingCountTemplate(ceDescrBuilder, ruleInfo, conditionList);
        }
        else if (NestingExistsTemplate.TYPE.equals(ruleInfo.getType())) {
            configNestingExistsTemplate(ceDescrBuilder, ruleInfo, conditionList);
        }
        else if (NestingCalculationTemplate.TYPE.equals(ruleInfo.getType())) {
            configNestingCalculationTemplate(ceDescrBuilder, ruleInfo, conditionList);
        }
        else if (NestingAllTemplate.TYPE.equals(ruleInfo.getType())) {
            configNestingAllTemplate(ceDescrBuilder, ruleInfo, conditionList);
        }
        else if (NestingAccumulateTemplate.TYPE.equals(ruleInfo.getType())) {
            configNestingAccumulateTemplate(ceDescrBuilder, ruleInfo, conditionList);
        }
        else if (NestingMatchesTemplate.TYPE.equals(ruleInfo.getType())) {
            configNestingMatchesTemplate(ceDescrBuilder, ruleInfo, conditionList);
        }
    }

    public static void configNestingAccumulateTemplate(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo, List<BaseConditionTemplate> conditionList) {
        NestingAccumulateTemplate nestingAccumulateTemplate = new NestingAccumulateTemplate();
        nestingAccumulateTemplate.setObjName(ruleInfo.getObj());
        nestingAccumulateTemplate.setAttribute(ruleInfo.getAttribute());
        nestingAccumulateTemplate.setConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CONDITIONPARAM));
        nestingAccumulateTemplate.setParentName(ruleInfo.getParent());
        nestingAccumulateTemplate.setDescrBuilder(ceDescrBuilder);
        nestingAccumulateTemplate.setNestingAccumulateTemplate();
    }

    public static void configNestingCalculationTemplate(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo, List<BaseConditionTemplate> conditionList) {
        NestingCalculationTemplate nestingCalculationTemplate = new NestingCalculationTemplate(ceDescrBuilder);
        nestingCalculationTemplate.setObjName(ruleInfo.getObj());
        nestingCalculationTemplate.setAttribute(ruleInfo.getAttribute());
        nestingCalculationTemplate.setConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CONDITIONPARAM));
        nestingCalculationTemplate.setCalConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CALPARAM));
        nestingCalculationTemplate.setParentName(ruleInfo.getParent());
        nestingCalculationTemplate.setCalculation(ruleInfo.getCalculation());
        nestingCalculationTemplate.setDescrBuilder(ceDescrBuilder);
        nestingCalculationTemplate.setNestingCalculationTemplate();
    }

    public static void configNestingAllTemplate(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo, List<BaseConditionTemplate> conditionList) {
        NestingAllTemplate nestingAllTemplate = new NestingAllTemplate(ceDescrBuilder);
        nestingAllTemplate.setObjName(ruleInfo.getObj());
        nestingAllTemplate.setAttribute(ruleInfo.getAttribute());
        nestingAllTemplate.setConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CONDITIONPARAM));
        nestingAllTemplate.setParentName(ruleInfo.getParent());
        nestingAllTemplate.setDescrBuilder(ceDescrBuilder);
        nestingAllTemplate.setNestingAllTemplate();
    }

    public static void configNestingExistsTemplate(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo, List<BaseConditionTemplate> conditionList) {
        NestingExistsTemplate nestingExistsTemplate = new NestingExistsTemplate();
        nestingExistsTemplate.setObjName(ruleInfo.getObj());
        nestingExistsTemplate.setAttribute(ruleInfo.getAttribute());
        nestingExistsTemplate.setConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CONDITIONPARAM));
        nestingExistsTemplate.setParentName(ruleInfo.getParent());
        nestingExistsTemplate.setDescrBuilder(ceDescrBuilder);
        nestingExistsTemplate.setNestingExistsTemplate();
    }

    public static void configNestingMatchesTemplate(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo, List<BaseConditionTemplate> conditionList) {
        NestingMatchesTemplate nestingMatchesTemplate = new NestingMatchesTemplate();
        nestingMatchesTemplate.setObjName(ruleInfo.getObj());
        nestingMatchesTemplate.setAttribute(ruleInfo.getAttribute());
        nestingMatchesTemplate.setConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CONDITIONPARAM));
        nestingMatchesTemplate.setParentName(ruleInfo.getParent());
        nestingMatchesTemplate.setDescrBuilder(ceDescrBuilder);
        nestingMatchesTemplate.setNestingExistsTemplate();
    }

    public static void configNestingCountTemplate(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo, List<BaseConditionTemplate> conditionList) {
        NestingCountTemplate nestingCountTemplate = new NestingCountTemplate();
        nestingCountTemplate.setObjName(ruleInfo.getObj());
        nestingCountTemplate.setAttribute(ruleInfo.getAttribute());
        nestingCountTemplate.setConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CONDITIONPARAM));
        nestingCountTemplate.setCalConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CALPARAM));
        nestingCountTemplate.setParentName(ruleInfo.getParent());
        nestingCountTemplate.setDescrBuilder(ceDescrBuilder);
        nestingCountTemplate.setCalculation(ruleInfo.getCalculation());
        nestingCountTemplate.setNestingCountTemplate();
    }

    public static void configSimpleRuleTemplate(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, RuleInfo ruleInfo, List<BaseConditionTemplate> conditionList) {
        SimpleRuleTemplate simpleRuleTemplate = new SimpleRuleTemplate();
        simpleRuleTemplate.setRuleInfo(ruleInfo);
        simpleRuleTemplate.setConditionList(conditionListFilter(conditionList, BaseConditionTemplate.CONDITIONPARAM));
        simpleRuleTemplate.setDescrBuilder(ceDescrBuilder);
        simpleRuleTemplate.setSimpleRuleTemplate();
    }

    public static List<BaseConditionTemplate> conditionListFilter(List<BaseConditionTemplate> baseConditionTemplates, String filter) {
        return baseConditionTemplates.stream().filter(baseConditionTemplate -> filter.equals(baseConditionTemplate.getParamType())).collect(Collectors.toList());
    }

    /**
    * @author QIQI
    * @Description: 子模板的判斷加入
    * @params [ruleInfo]
    * @return java.util.List<com.wms.utility.template.condition.BaseConditionTemplate>
    * @throws 
    * @date 2019-06-20 09:56 
    */
    public static List<BaseConditionTemplate> buildCondition(RuleInfo ruleInfo) {
        List<BaseConditionTemplate> conditionList = new ArrayList<>();
        ruleInfo.getRuleConditions().forEach(ruleCondition -> {
            if (ruleCondition.getType().equals(BaseConditionTemplate.CONTAINSTYPE)) {
                ContainsConditionTemplate containsConditionTemplate = new ContainsConditionTemplate(ruleCondition.getConditionKey(),
                        ruleCondition.getOp(), ruleCondition.getConditionValue(), ruleCondition.getAssociationType());
                containsConditionTemplate.setParamType(ruleCondition.getParamType());
                conditionList.add(containsConditionTemplate);
            }
            else if (ruleCondition.getType().equals(BaseConditionTemplate.EQUALTYPE)) {
                EqualConditionTemplate equalConditionTemplate = new EqualConditionTemplate(ruleCondition.getConditionKey(),
                        ruleCondition.getOp(), ruleCondition.getConditionValue(), ruleCondition.getAssociationType());
                equalConditionTemplate.setParamType(ruleCondition.getParamType());
                conditionList.add(equalConditionTemplate);
            }
            else if (ruleCondition.getType().equals(BaseConditionTemplate.MEMBEROFTYPE)) {
                MemberOfConditionTemplate memberOfConditionTemplate = new MemberOfConditionTemplate(ruleCondition.getConditionKey(),
                        ruleCondition.getOp(), ruleCondition.getConditionValue(), ruleCondition.getAssociationType());
                memberOfConditionTemplate.setParamType(ruleCondition.getParamType());
                conditionList.add(memberOfConditionTemplate);
            }
            else if (ruleCondition.getType().equals(BaseConditionTemplate.RANGETYPE)) {
                RangeConditionTemplate rangeConditionTemplate = new RangeConditionTemplate(ruleCondition.getConditionKey(),
                        ruleCondition.getOp(), ruleCondition.getConditionValue(), ruleCondition.getAssociationType());
                rangeConditionTemplate.setParamType(ruleCondition.getParamType());
                conditionList.add(rangeConditionTemplate);
            }
            else if (ruleCondition.getType().equals(BaseConditionTemplate.MATCHESTYPE)) {
                MatchesConditionTemplate matchesConditionTemplate = new MatchesConditionTemplate(ruleCondition.getConditionKey(),
                        ruleCondition.getOp(), ruleCondition.getConditionValue(), ruleCondition.getAssociationType());
                matchesConditionTemplate.setParamType(ruleCondition.getParamType());
                conditionList.add(matchesConditionTemplate);
            }
            else if (ruleCondition.getType().equals(BaseConditionTemplate.ACCUMULATETYPE)) {
                AccumulateConditionTemplate accumulateConditionTemplate = new AccumulateConditionTemplate(ruleCondition.getConditionKey(),
                        ruleCondition.getOp(), ruleCondition.getConditionValue(), ruleCondition.getAssociationType());
                accumulateConditionTemplate.setParamType(ruleCondition.getParamType());
                conditionList.add(accumulateConditionTemplate);
            }
        });
        return conditionList;
    }

    public static boolean checkRuleId(CEDescrBuilder<RuleDescrBuilder, AndDescr> ceDescrBuilder, String objName) {
        boolean flag = true;
        List<PatternDescr> patternDescrList = ceDescrBuilder.getDescr().getAllPatternDescr();
        if (patternDescrList.isEmpty()) {
            return flag;
        }
        for (PatternDescr patternDescr : patternDescrList) {
            if (objName.equals(patternDescr.getIdentifier())) {
                flag = false;
            }
        }
        return flag;
    }
}

 

QueryManagerImpl 實現類(這個類主要用作規則數據匹配打標使用,分爲無狀態session和有狀態session,差異就不詳細介紹了,小夥伴可以自行百度)

@Service
public class QueryManagerImpl<T, V> implements QueryManager<T, V> {
    private static Logger log = LogManager.getLogger("QueryManagerImpl");
    @Autowired
    private KnowledgeBaseLib knowledgeBaseLib;

    /**
     * 使用有狀態SESSION進行請求
     *
     * @param knowLedgeBaseName
     * @param data
     * @return T
     */
    @Override
    public T queryCommandWithKieSessionAsList(String knowLedgeBaseName, V data) {
        Function<String, InternalKnowledgeBase> internalKnowledgeBaseFunction = knowledgeBaseLib::getInternalKnowledgeBase;
        Supplier<KieSession> kieSessionSupplier = internalKnowledgeBaseFunction.apply(knowLedgeBaseName)::newKieSession;
        KieSession kieSession = kieSessionSupplier.get();
        List resultList = new ArrayList();
        if (data instanceof List) {
            List dataList = (List) data;
            //kieSession.setGlobal(FinalArgs.DROOLS_GLOBAL_TYPE, resultList);
            dataList.forEach(dataInfo->{
                kieSession.insert(dataInfo);
                kieSession.getAgenda().getAgendaGroup( knowLedgeBaseName ).setFocus();
                kieSession.fireAllRules();
            });
        }
        kieSession.dispose();
        return (T) resultList;
    }

    /**
    * @author QIQI
    * @Description: 使用無狀態SESSION進行請求
    * @params [knowLedgeBaseName, dataTmp]
    * @return T
    * @throws 
    * @date 2019-06-17 14:17 
    */
    @Override
    public T queryCommandWithStatelessKieSessionAsList(String knowLedgeBaseName, V data) {
        Function<String, InternalKnowledgeBase> internalKnowledgeBaseFunction = knowledgeBaseLib::getInternalKnowledgeBase;
        Supplier<StatelessKieSession> statelessKieSessionSupplier = internalKnowledgeBaseFunction.apply(knowLedgeBaseName)::newStatelessKieSession;
        StatelessKieSession statelessKieSession = statelessKieSessionSupplier.get();
        if (data instanceof List) {
            ((List) data).parallelStream().forEach(datainfo->statelessKieSession.execute(datainfo));
        }
        return (T) data;
    }

    /**
     * 使用有狀態SESSION進行請求,返回具體請求規則的結果
     *
     * @param knowLedgeBaseName
     * @param ruleMap
     * @param data
     * @return T
     */
    @Override
    public T queryCommandWithKieSession(String knowLedgeBaseName, Map<String, Map<String, List<String>>> ruleMap, V data) {
        Function<String, InternalKnowledgeBase> internalKnowledgeBaseFunction = knowledgeBaseLib::getInternalKnowledgeBase;
        Supplier<KieSession> kieSessionSupplier = internalKnowledgeBaseFunction.apply(knowLedgeBaseName)::newKieSession;
        KieSession kieSession = kieSessionSupplier.get();
        List<Command> list = new ArrayList<>();
        if (data instanceof List) {
            List dataList = (List) data;
            int dataSize = dataList.size();
            for (int i = 0; i < dataSize; i++) {
                list.add(CommandFactory.newInsert(dataList.get(i)));
            }
            list.add(CommandFactory.newFireAllRules());
            list.add(CommandFactory.newGetObjects());
            ruleMap.forEach((ruleName, queryNames) -> queryNames.forEach((queryName, args) -> list.add(CommandFactory.newQuery(ruleName, queryName, args.toArray()))));
        }
        return (T) kieSession.execute(CommandFactory.newBatchExecution(list));
    }

    /**
     * 使用無狀態SESSION進行請求,返回具體請求規則的結果
     *
     * @param knowLedgeBaseName
     * @param ruleMap
     * @param data
     * @return T
     */
    @Override
    public T queryCommandWithStatelessKieSession(String knowLedgeBaseName, Map<String, Map<String, List<String>>> ruleMap, V data) {
        Function<String, InternalKnowledgeBase> internalKnowledgeBaseFunction = knowledgeBaseLib::getInternalKnowledgeBase;
        Supplier<StatelessKieSession> statelessKieSessionSupplier = internalKnowledgeBaseFunction.apply(knowLedgeBaseName)::newStatelessKieSession;
        StatelessKieSession statelessKieSession = statelessKieSessionSupplier.get();
        List<Command> list = new ArrayList<>();
        if (data instanceof List) {
            List dataList = (List) data;
            int dataSize = dataList.size();
            for (int i = 0; i < dataSize; i++) {
                list.add(CommandFactory.newInsert(dataList.get(i)));
            }
            list.add(CommandFactory.newFireAllRules());
            list.add(CommandFactory.newGetObjects());
            ruleMap.forEach((ruleName, queryNames) -> queryNames.forEach((queryName, args) -> list.add(CommandFactory.newQuery(ruleName, queryName, args.toArray()))));
        }
        return (T) statelessKieSession.execute(CommandFactory.newBatchExecution(list));
    }
}

 

通過上面得一系列代碼,已經完成了流程圖中得功能,當然對於規則保存等Action接口實現需要自己完成,細心得小夥伴會發現上面得流程圖中有個很嚴重得問題那就是如果規則應用多實例部署得話就會出現問題,因爲規則是在單個實例得工作內存中,這個也是離線模式得重要問題,不過作者通過簡單得樂觀鎖解決了這個問題,沒有選擇去接入Kie因爲改動成本太大了,並且我們公司對於規則也沒有更重得需求迭代了,樂觀鎖流程圖如下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章