/*
* Copyright 2005 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.core.reteoo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.drools.core.RuleBaseConfiguration;
import org.drools.core.base.ClassObjectType;
import org.drools.core.common.DefaultFactHandle;
import org.drools.core.common.PropagationContextFactory;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.impl.StatefulKnowledgeSessionImpl;
import org.drools.core.reteoo.ReteooBuilder.IdGenerator;
import org.drools.core.reteoo.builder.BuildContext;
import org.drools.core.rule.EntryPointId;
import org.drools.core.spi.PropagationContext;
import org.drools.core.test.model.Cheese;
import org.drools.core.test.model.DroolsTestCase;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.kie.api.runtime.rule.FactHandle;
import org.kie.internal.KnowledgeBaseFactory;
import static org.junit.Assert.*;
public class ReteTest extends DroolsTestCase {
private PropagationContextFactory pctxFactory;
private InternalKnowledgeBase kBase;
private BuildContext buildContext;
private EntryPointNode entryPoint;
@Before
public void setUp() throws Exception {
this.kBase = (InternalKnowledgeBase) KnowledgeBaseFactory.newKnowledgeBase();
this.pctxFactory = kBase.getConfiguration().getComponentFactory().getPropagationContextFactory();
this.buildContext = new BuildContext(kBase);
this.entryPoint = buildContext.getKnowledgeBase().getRete().getEntryPointNodes().values().iterator().next();;
}
/**
* Tests ObjectTypeNodes are correctly added to the Rete object
*
* @throws Exception
*/
@Test
public void testObjectTypeNodes() throws Exception {
final Rete rete = kBase.getRete();
final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
this.entryPoint,
new ClassObjectType(Object.class),
buildContext);
objectTypeNode.attach(buildContext);
final ObjectTypeNode stringTypeNode = new ObjectTypeNode(2,
this.entryPoint,
new ClassObjectType(String.class),
buildContext);
stringTypeNode.attach(buildContext);
final List<ObjectTypeNode> list = rete.getObjectTypeNodes();
// Check the ObjectTypeNodes are correctly added to Rete
assertEquals(3,
list.size());
assertTrue(list.contains(objectTypeNode));
assertTrue(list.contains(stringTypeNode));
}
/**
* Tests that interfaces and parent classes for an asserted class are cached, for quick future iterations
*/
@Test
public void testCache() {
StatefulKnowledgeSessionImpl ksession = (StatefulKnowledgeSessionImpl)kBase.newStatefulKnowledgeSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = kBase.getRete();
ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
this.entryPoint,
new ClassObjectType(List.class),
buildContext);
objectTypeNode.attach(buildContext);
MockObjectSink sink = new MockObjectSink();
objectTypeNode.addObjectSink(sink);
objectTypeNode = new ObjectTypeNode(1,
this.entryPoint,
new ClassObjectType(Collection.class),
buildContext);
objectTypeNode.attach(buildContext);
sink = new MockObjectSink();
objectTypeNode.addObjectSink(sink);
objectTypeNode = new ObjectTypeNode(1,
this.entryPoint,
new ClassObjectType(ArrayList.class),
buildContext);
objectTypeNode.attach(buildContext);
sink = new MockObjectSink();
objectTypeNode.addObjectSink(sink);
// ArrayList matches all three ObjectTypeNodes
final DefaultFactHandle h1 = new DefaultFactHandle(1,
new ArrayList());
rete.assertObject(h1,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
// LinkedList matches two ObjectTypeNodes
h1.setObject(new LinkedList());
rete.assertObject(h1,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
ClassObjectTypeConf conf = (ClassObjectTypeConf) ksession.getObjectTypeConfigurationRegistry().getObjectTypeConf(this.entryPoint.getEntryPoint(), new ArrayList());
assertLength(3,
conf.getObjectTypeNodes());
conf = (ClassObjectTypeConf) ksession.getObjectTypeConfigurationRegistry().getObjectTypeConf(this.entryPoint.getEntryPoint(), new ArrayList());
assertLength(3,
conf.getObjectTypeNodes());
}
/**
* Test asserts correctly propagate
*
* @throws Exception
*/
@Test
public void testAssertObject() throws Exception {
StatefulKnowledgeSessionImpl ksession = (StatefulKnowledgeSessionImpl)kBase.newStatefulKnowledgeSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = kBase.getRete();
final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
this.entryPoint,
new ClassObjectType(List.class),
buildContext);
objectTypeNode.attach(buildContext);
final MockObjectSink sink1 = new MockObjectSink();
objectTypeNode.addObjectSink(sink1);
// There are no String ObjectTypeNodes, make sure its not propagated
final String string = "String";
final DefaultFactHandle h1 = new DefaultFactHandle(1,
string);
rete.assertObject(h1,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
assertLength(0,
sink1.getAsserted());
// There is a List ObjectTypeNode, make sure it was propagated
final List list = new ArrayList();
final DefaultFactHandle h2 = new DefaultFactHandle(1,
list);
rete.assertObject(h2,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
ksession.fireAllRules();
final List asserted = sink1.getAsserted();
assertLength(1,
asserted);
final Object[] results = (Object[]) asserted.get(0);
assertSame(list,
((DefaultFactHandle) results[0]).getObject());
}
@Test
public void testAssertObjectWithNoMatchingObjectTypeNode() {
StatefulKnowledgeSessionImpl ksession = (StatefulKnowledgeSessionImpl)kBase.newStatefulKnowledgeSession();
final Rete rete = kBase.getRete();
assertEquals(1,
rete.getObjectTypeNodes().size());
List list = new ArrayList();
ksession.insert(list);
ksession.fireAllRules();
assertEquals(1,
rete.getObjectTypeNodes().size());
}
@Test
@Ignore
public void testHierarchy() {
StatefulKnowledgeSessionImpl ksession = (StatefulKnowledgeSessionImpl)kBase.newStatefulKnowledgeSession();
final Rete rete = kBase.getRete();
final IdGenerator idGenerator = kBase.getReteooBuilder().getIdGenerator();
// Attach a List ObjectTypeNode
final ObjectTypeNode listOtn = new ObjectTypeNode(idGenerator.getNextId(),
this.entryPoint,
new ClassObjectType(List.class),
buildContext);
listOtn.attach(buildContext);
// Will automatically create an ArrayList ObjectTypeNode
FactHandle handle = ksession.insert(new ArrayList());
// Check we have three ObjectTypeNodes, List, ArrayList and InitialFactImpl
assertEquals(3,
rete.getObjectTypeNodes().size());
// double check that the List reference is the same as the one we created, i.e. engine should try and recreate it
assertSame(listOtn,
rete.getObjectTypeNodes(EntryPointId.DEFAULT).get(new ClassObjectType(List.class)));
// ArrayConf should match two ObjectTypenodes for List and ArrayList
ClassObjectTypeConf arrayConf = (ClassObjectTypeConf) ksession.getObjectTypeConfigurationRegistry().getObjectTypeConf(this.entryPoint.getEntryPoint(), new ArrayList());
final ObjectTypeNode arrayOtn = arrayConf.getConcreteObjectTypeNode();
assertEquals(2,
arrayConf.getObjectTypeNodes().length);
// Check it contains List and ArrayList
List nodes = Arrays.asList(arrayConf.getObjectTypeNodes());
assertEquals(2,
nodes.size());
assertTrue(nodes.contains(arrayOtn));
assertTrue(nodes.contains(listOtn));
// Nodes are there, retract the fact so we can check both nodes are populated
ksession.retract(handle);
// Add MockSinks so we can track assertions
final MockObjectSink listSink = new MockObjectSink();
listOtn.addObjectSink(listSink);
final MockObjectSink arraySink = new MockObjectSink();
listOtn.addObjectSink(arraySink);
ksession.insert(new ArrayList());
assertEquals(1,
listSink.getAsserted().size());
assertEquals(1,
arraySink.getAsserted().size());
// Add a Collection ObjectTypeNode, so that we can check that the data from ArrayList is sent to it
final ObjectTypeNode collectionOtn = new ObjectTypeNode(idGenerator.getNextId(),
this.entryPoint,
new ClassObjectType(Collection.class),
buildContext);
final MockObjectSink collectionSink = new MockObjectSink();
collectionOtn.addObjectSink(collectionSink);
collectionOtn.attach(new TestBuildContext(kBase));
assertEquals(1,
collectionSink.getAsserted().size());
// check that ArrayListConf was updated with the new ObjectTypeNode
nodes = Arrays.asList(arrayConf.getObjectTypeNodes());
assertEquals(3,
nodes.size());
assertTrue(nodes.contains(arrayOtn));
assertTrue(nodes.contains(listOtn));
assertTrue(nodes.contains(collectionOtn));
}
/**
* All objects deleted from a RootNode must be propagated to all children
* ObjectTypeNodes.
*/
@Test
public void testRetractObject() throws Exception {
StatefulKnowledgeSessionImpl ksession = (StatefulKnowledgeSessionImpl)kBase.newStatefulKnowledgeSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = kBase.getRete();
final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
this.entryPoint,
new ClassObjectType(List.class),
buildContext);
objectTypeNode.attach(buildContext);
final MockObjectSink sink1 = new MockObjectSink();
objectTypeNode.addObjectSink(sink1);
// There are no String ObjectTypeNodes, make sure its not propagated
final String string = "String";
final DefaultFactHandle h1 = new DefaultFactHandle(1,
string);
rete.assertObject(h1,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
assertLength(0,
sink1.getAsserted());
assertLength(0,
sink1.getRetracted());
// There is a List ObjectTypeNode, make sure it was propagated
final List list = new ArrayList();
final DefaultFactHandle h2 = new DefaultFactHandle(1,
list);
// need to assert first, to force it to build up the cache
rete.assertObject(h2,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
rete.retractObject(h2,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
ksession.fireAllRules();
final List retracted = sink1.getRetracted();
assertLength(1,
retracted);
final Object[] results = (Object[]) retracted.get(0);
assertSame(list,
((DefaultFactHandle) results[0]).getObject());
}
@Test
public void testIsShadowed() {
StatefulKnowledgeSessionImpl ksession = (StatefulKnowledgeSessionImpl)kBase.newStatefulKnowledgeSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = kBase.getRete();
final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
this.entryPoint,
new ClassObjectType(Cheese.class),
buildContext);
objectTypeNode.attach(buildContext);
final MockObjectSink sink1 = new MockObjectSink();
objectTypeNode.addObjectSink(sink1);
// There are no String ObjectTypeNodes, make sure its not propagated
final Cheese cheese = new Cheese("brie",
15);
final DefaultFactHandle h1 = new DefaultFactHandle(1,
cheese);
rete.assertObject(h1,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
ksession.fireAllRules();
final Object[] results = (Object[]) sink1.getAsserted().get(0);
}
@Test @Ignore
public void testNotShadowed() {
Properties properties = new Properties();
properties.setProperty("drools.shadowProxyExcludes",
"org.drools.core.test.model.Cheese");
RuleBaseConfiguration conf = new RuleBaseConfiguration(properties);
InternalKnowledgeBase kBase = (InternalKnowledgeBase) KnowledgeBaseFactory.newKnowledgeBase(conf);
buildContext = new BuildContext(kBase);
final StatefulKnowledgeSessionImpl ksession = new StatefulKnowledgeSessionImpl(1L, kBase);
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = kBase.getRete();
final EntryPointNode entryPoint = new EntryPointNode(0,
rete,
buildContext);
entryPoint.attach(buildContext);
final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
entryPoint,
new ClassObjectType(Cheese.class),
buildContext);
objectTypeNode.attach(buildContext);
final MockObjectSink sink1 = new MockObjectSink();
objectTypeNode.addObjectSink(sink1);
// There are no String ObjectTypeNodes, make sure its not propagated
final Cheese cheese = new Cheese("brie",
15);
final DefaultFactHandle h1 = new DefaultFactHandle(1,
cheese);
rete.assertObject(h1,
pctxFactory.createPropagationContext(0,
PropagationContext.Type.INSERTION,
null,
null,
null),
ksession);
ksession.fireAllRules();
final Object[] results = (Object[]) sink1.getAsserted().get(0);
}
public static class TestBuildContext extends BuildContext {
InternalKnowledgeBase kBase;
TestBuildContext(InternalKnowledgeBase kBase) {
super(kBase);
this.kBase = kBase;
}
}
}