我有一些对象链,如下所示,我想使用 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
的重复,我要定义一个自定义运算符&#34; 先于&#34;简化规则的起草,并能够编写:
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
我创建了PrecedesEvaluator
和PrecedesEvaluatorDefinition
,如下所示:
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};
}
}
}
我在线阅读了一些指南,并尝试以编程方式注册新运算符,如下所示:
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文件)?
答案 0 :(得分:1)
我最后使用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();
来自drools本身的this test启发。
答案 1 :(得分:0)
以下代码有效,但使用了不推荐使用的KnowledgeBase
(因此这不算作答案):
private KnowledgeBase 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 a configuration
KnowledgeBuilderConfiguration configuration = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration();
// Instantiate the new custom operator and add it to configuration
EvaluatorOption option = EvaluatorOption.get(precedesOp, new PrecedesEvaluatorDefinition());
configuration.setOption(option);
// Get a builder from the configuration
KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(configuration);
// Load a DRL and add it to the builder
String location = "/drools/Repetitions.drl";
InputStream stream = getClass().getResourceAsStream(location);
Resource resource = ResourceFactory.newInputStreamResource(stream);
builder.add(resource, ResourceType.DRL);
// Test the builder for errors
if (builder.hasErrors()) {
throw new RuntimeException(join("\n", builder.getErrors()));
}
// Get a knowledge base and fill it with the builder's content
base = KnowledgeBaseFactory.newKnowledgeBase();
base.addKnowledgePackages(builder.getKnowledgePackages());
}
}