java - 如何使用 Java 在 KieServices 中以编程方式注册 Drools 6 自定义运算符

标签 java operators drools kie

我有一些像下面这样的对象链,我想使用 Drools 6.4.0 进行处理:

@Value
public final class Node {
    private final String code;
    private final Node prev;
}

例如,要使用的示例规则如下:

rule "Initial repetition"
when
    $n1: Node(prev == null, $c: code)
    $n2: Node(prev == $n1, code == $c)
then
    System.out.println($c + ": " + $n1 + ":" + $n2);
end

Drools 使用以下代码初始化并运行:

private KieBase base;

public void process(List<Node> nodes) {
    initialise();
    KieSession session = base.newKieSession();
    nodes.forEach(session::insert);
    session.fireAllRules();
    session.dispose();
}

private void initialise() {
    if (base == null) {
        // Get the KIE services
        KieServices services = KieServices.Factory.get();
        // Get a virtual file system
        KieFileSystem fileSystem = services.newKieFileSystem();
        // Add a DRL file to the virtual file system
        String location = "/drools/Repetitions.drl";
        InputStream stream = getClass().getResourceAsStream(location);
        Resource resource = ResourceFactory.newInputStreamResource(stream);
        fileSystem.write("src/main/resources" + location, resource);
        // Build the virtual file system into a repository's container
        KieBuilder builder = services.newKieBuilder(fileSystem).buildAll();
        Results results = builder.getResults();
        if (results.hasMessages(ERROR)) {
            throw new RuntimeException(join("\n", results.getMessages()));
        }
        KieRepository repository = services.getRepository();
        KieContainer container = services.newKieContainer(repository.getDefaultReleaseId());
        // Get the knowledge base
        base = container.newKieBase();
    }
}

因为我必须识别每条链中第一个 Node 的任何重复,所以我想定义一个自定义运算符“precedes”以简化规则的起草并且例如能够写:

rule "Any repetition of first nodes"
when
    $n1: Node(prev == null, $c: code)
    $n2: Node($n1 precedes this, code == $c)
then
    System.out.println($n2);
end

我创建了 PrecedesEvaluatorPrecedesEvaluatorDefinition 如下:

public class PrecedesEvaluator extends BaseEvaluator {
    private static final long serialVersionUID = ...L;
    private final boolean isNegated; 

    public PrecedesEvaluator(ValueType type, boolean isNegated) {
        super(type, isNegated ?
                PrecedesEvaluatorDefinition.NOT_PRECEDES :
                PrecedesEvaluatorDefinition.PRECEDES);
        this.isNegated = isNegated;
    }

    @Override
    public boolean evaluate(InternalWorkingMemory workingMemory, InternalReadAccessor extractor, InternalFactHandle factHandle, FieldValue value) {
        Object nodeLeft = extractor.getValue(workingMemory, factHandle.getObject());
        return isNegated ^ evaluateUnsafe(nodeLeft, value.getValue());
    }

    @Override
    public boolean evaluate(InternalWorkingMemory workingMemory, InternalReadAccessor leftExtractor, InternalFactHandle left, InternalReadAccessor rightExtractor, InternalFactHandle right) {
        Object nodeLeft = leftExtractor.getValue(workingMemory, left.getObject());
        Object nodeRight = rightExtractor.getBigDecimalValue(workingMemory, right.getObject());
        return isNegated ^ evaluateUnsafe(nodeLeft, nodeRight);
    }

    @Override
    public boolean evaluateCachedLeft(InternalWorkingMemory workingMemory, VariableContextEntry context, InternalFactHandle right) {
        Object nodeLeft = context.getFieldExtractor().getValue(workingMemory, right.getObject());
        Object nodeRight = right.getObject();
        return isNegated ^ evaluateUnsafe(nodeLeft, nodeRight);
    }

    @Override
    public boolean evaluateCachedRight(InternalWorkingMemory workingMemory, VariableContextEntry context, InternalFactHandle left) {
        Object nodeLeft = ((ObjectVariableContextEntry) context).right;
        Object nodeRight = context.getFieldExtractor().getValue(workingMemory, left.getObject());
        return isNegated ^ evaluateUnsafe(nodeLeft, nodeRight);
    }

    private boolean evaluateUnsafe(Object nodeLeft, Object nodeRight) {
        if (!(nodeLeft instanceof Node)) {
            throw new IllegalArgumentException("'nodeLeft' can't be casted to Node: " + nodeLeft.getClass());
        }
        if (!(nodeRight instanceof Node)) {
            throw new IllegalArgumentException("'nodeRight' can't be casted to Node: " + nodeRight.getClass());
        }
        return evaluate((Node) nodeLeft, (Node) nodeRight);
    }

    private boolean evaluate(node nodeLeft, node nodeRight) {
        Node current = nodeRight;
        while (current != null) {
            if (current == null) {
                return false;
            }
            if (current == nodeLeft) {
                return true;
            }
            current = current.getPrev();
        }
        return false;
    }
}

public class PrecedesEvaluatorDefinition implements EvaluatorDefinition {
    private static final long serialVersionUID = ...L;

    protected static final String precedesOp = "precedes";

    public static Operator PRECEDES;
    public static Operator NOT_PRECEDES;
    private static String[] SUPPORTED_IDS;

    private PrecedesEvaluator evaluator;
    private PrecedesEvaluator negatedEvaluator;

    @Override
    public String[] getEvaluatorIds() {
        return new String[] {precedesOp};
    }

    @Override
    public boolean isNegatable() {
        return true;
    }

    @Override
    public Evaluator getEvaluator(ValueType type, String operatorId, boolean isNegated, String parameterText, Target leftTarget, Target rightTarget) {
        return isNegated ?
                (negatedEvaluator == null ? new PrecedesEvaluator(type, true) : negatedEvaluator) :
                (evaluator == null ? new PrecedesEvaluator(type, false) : evaluator);
    }

    @Override
    public Evaluator getEvaluator(ValueType type, String operatorId, boolean isNegated, String parameterText) {
        return getEvaluator(type, operatorId, isNegated, parameterText, Target.BOTH, Target.BOTH);
    }

    @Override
    public Evaluator getEvaluator(ValueType type, Operator operator, String parameterText) {
        return getEvaluator(type, operator.getOperatorString(), operator.isNegated(), parameterText);
    }

    @Override
    public Evaluator getEvaluator(ValueType type, Operator operator) {
        return getEvaluator(type, operator.getOperatorString(), operator.isNegated(), null);
    }

    @Override
    public boolean supportsType(ValueType type) {
        return true;
    }

    @Override
    public Target getTarget() {
        return Target.BOTH;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        throw new UnsupportedOperationException("writeExternal not usable");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        throw new UnsupportedOperationException("readExternal not usable");
    }

    static {
        if (SUPPORTED_IDS == null) {
            PRECEDES = Operator.addOperatorToRegistry(precedesOp, false);
            NOT_PRECEDES = Operator.addOperatorToRegistry(precedesOp, true);
            SUPPORTED_IDS = new String[] {precedesOp};
        }
    }
}

我在线阅读了一些指南,并尝试以编程方式注册新的运算符(operator),如下所示:

private void initialise() {
    if (base == null) {
        ...
        KieBaseConfiguration configuration = services.newKieBaseConfiguration();
        KieBaseOption option = EvaluatorOption.get(precedesOp, new PrecedesEvaluatorDefinition());
        configuration.setOption(option); // Wrong type
        ...
        base = container.newKieBase(configuration);
    }
}

private void initialise() {
    if (base == null) {
        KnowledgeBuilderConfiguration configuration = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration();
        EvaluatorOption option = EvaluatorOption.get(precedesOp, new PrecedesEvaluatorDefinition());
        configuration.setOption(option);
        ...
        base = container.newKieBase(configuration); // Wrong type!
    }
}

然而,在这两种情况下,都会发生类型不匹配且编译失败。

所以我的问题是:我应该如何注册我的运算符以在规则中使用(请注意,如果可能,我不希望使用 XML 文件)?

最佳答案

我最终使用 KieHelper 进行了以下初始化,整个初始化更加清晰。

KieServices ks = KieServices.Factory.get();
KieModuleModel kieModel = ks
    .newKieModuleModel()
    .setConfigurationProperty("drools.evaluator.precedes", PrecedesEvaluatorDefinition.class.getName());

KieBase kieBase = new KieHelper()
    .setKieModuleModel(kieModel)
    .addFromClassPath("/drools/Repetitions.drl")
    .build();

灵感来自 this test来自流口水本身。

关于java - 如何使用 Java 在 KieServices 中以编程方式注册 Drools 6 自定义运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39767630/

相关文章:

java - 流口水 : Setting a flag from within the drl file

drools - 找不到KieModule : org.默认:artifact:1. 0.0-快照

java - 在 Android 版 Google API 10 中显示我的当前位置

java - 如何确保 java 对象是可序列化的?

java - 检测Spring框架中所有bean已经实例化

javascript - 添加没有 "+"符号的数字和字符串

php 不等于,不等于,等于

c# - '^' 在 c#(枚举)中做什么?

java - 如何在 Spring 中禁用代理 Bean

drools - '&&' 或 ',' "short-circuit"会在流口水 LHS 中吗?