如何更改默认的ConflictResolver

时间:2015-01-02 17:31:57

标签: drools

如标题I所述,我想在我的Drools项目中更改冲突解决方案。我在此site

上找到了以下代码段
ConflictResolver[] conflictResolvers = 
new ConflictResolver[] { SalienceConflictResolver.getInstance( ),
                         FifoConflictResolver.getInstance( ) };

RuleBase ruleBase = java.io.RuleBaseLoader( url, CompositeConflitResolver( conflictResolver));

但是它缺少信息放在哪里以及sholud是url参数。

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

正如文件所说:

  

Drools 4.0支持自定义冲突解决策略;而这个   它还没有暴露在Drools中   最终用户通过Drools 5.0中的knowledge-api。   http://docs.jboss.org/drools/release/5.2.0.Final/drools-expert-docs/html/ch04.html

因此,如果你使用Drools 5+,你将无法改变冲突解决方案除非你做一些反思魔术。冲突解决程序在StatefulKnowledgeSession对象的Agenda对象中结算。您可以通过使用调试器(它是Agenda对象的内容)来看到这一点:

enter image description here

要替换ConflictResolver,首先需要StatefulKnowledgeSession的实例(在以下代码段中将是ksession)。然后,您需要提取一些嵌套的私有字段,然后您可以使用例如 RandomConflictResolver 的实例替换字段值。完整代码:

        Agenda agenda = ksession.getAgenda();
        Field agendaField = agenda.getClass().getDeclaredField("agenda");
        agendaField.setAccessible(true);
        Object agendaObject = agendaField.get(agenda);


        Field mainField = agendaObject.getClass().getDeclaredField("main");
        mainField.setAccessible(true);
        Object mainObject = mainField.get(agendaObject);

        Field queueField = mainObject.getClass().getDeclaredField("queue");
        queueField.setAccessible(true);
        Object queueObject = queueField.get(mainObject);

        Field comparatorField = queueObject.getClass().getDeclaredField("comparator");
        comparatorField.setAccessible(true);
        Object comparator = comparatorField.get(queueObject);

        ConflictResolver randomResolver = org.drools.conflict.RandomConflictResolver.getInstance();
        comparatorField.set(queueObject, randomResolver);

基于:documentation和调试器会话。

答案 1 :(得分:0)

为了证明解雇顺序不会影响整体结果我会使用AgendaFilter。我正在使用6.x草绘轮廓,但是这个API自5.x以来没有改变。

KieBase kieBase = ...;
Collection<KiePackage> packages = kieBase.getKiePackages();
List<Rule> rules = new ArrayList<>();
for( KiePackage p: packages ){
    rules.addAll( p.getRules() );
}

现在你有了所有的规则。构建这个接受单个规则的简单过滤器:

class Selector implements AgendaFilter {
    List<Rule> rules;
    int ruleIndex;
    Selector( List<Rule> rules ){
        this.rules = rules;
    }
    void setRuleIndex( int value ){ this.ruleIndex = value; }
    int getRulesSize(){ return rules.size(); }
    boolean accept(Match match){
        return match.getRule().equals( rules.get( ruleIndex ) );
    }
}

实例化:

Selector selector = newSelector( rules );

您现在可以执行所有已激活的规则(但请参见下文):

for( int i = 0; i < selector.getRulesSize(); ++i ){
    int fired = kieSession.fireAllRules( selector, i );
}

0..size-1的任何其他排列可能会产生另一个发射序列。您可以针对少数规则系统地执行此操作,或者您可以使用一些随机排列。

更有效的测试将跟踪第一次运行中传递给过滤器的Match数据,并仅将这些数据用于连续执行。

警告到目前为止概述的方法不考虑工作记忆中的更改。当某些规则 n + k 被触发时,可能会激活规则 n 。如果您确实更改了工作记忆,则必须

do {
    int sumf = 0;
    for( int i : somePermutation ){
        int fired = kieSession.fireAllRules( selector, i );
        sumf += fired;
    }
} while( sumf > 0 );

我从未做过这样的测试。似乎通过依赖规则发射的固有顺序来获得正确的结果是非常罕见的,与此订单中获得各种错误结果相反。

注意规则触发顺序的其他排列是可以通过更改DRL(或其包中的)规则的顺序或通过将事实插入的顺序更改为工作内存来实现的。对于某些情况,即使这样也可以提供足够的测试用例来显示您的意图。