/*
* Copyright 2005 JBoss Inc
*
* 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 org.drools.core.FactException;
import org.drools.core.FactHandle;
import org.drools.core.RuleBaseConfiguration;
import org.drools.core.RuleBaseFactory;
import org.drools.core.base.ClassObjectType;
import org.drools.core.base.ShadowProxy;
import org.drools.core.common.AbstractWorkingMemory;
import org.drools.core.common.DefaultFactHandle;
import org.drools.core.common.InternalRuleBase;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.PropagationContextFactory;
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 java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class ReteTest extends DroolsTestCase {
private PropagationContextFactory pctxFactory;
private ReteooRuleBase ruleBase;
private BuildContext buildContext;
private EntryPointNode entryPoint;
@Before
public void setUp() throws Exception {
this.ruleBase = (ReteooRuleBase) RuleBaseFactory.newRuleBase();
this.pctxFactory = ruleBase.getConfiguration().getComponentFactory().getPropagationContextFactory();
this.buildContext = new BuildContext(ruleBase,
((ReteooRuleBase) ruleBase).getReteooBuilder().getIdGenerator());
this.entryPoint = new EntryPointNode(0,
this.ruleBase.getRete(),
buildContext);
this.entryPoint.attach(buildContext);
}
/**
* Tests ObjectTypeNodes are correctly added to the Rete object
*
* @throws Exception
*/
@Test
public void testObjectTypeNodes() throws Exception {
final Rete rete = ruleBase.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(2,
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
*
* @throws org.drools.core.FactException
*/
@Test
public void testCache() throws FactException {
final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) this.ruleBase.newStatefulSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = ruleBase.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.INSERTION,
null,
null,
null),
workingMemory);
// LinkedList matches two ObjectTypeNodes
h1.setObject(new LinkedList());
rete.assertObject(h1,
pctxFactory.createPropagationContext(0,
PropagationContext.INSERTION,
null,
null,
null),
workingMemory);
ClassObjectTypeConf conf = (ClassObjectTypeConf) workingMemory.getObjectTypeConfigurationRegistry().getObjectTypeConf(this.entryPoint.getEntryPoint(), new ArrayList());
assertLength(3,
conf.getObjectTypeNodes());
conf = (ClassObjectTypeConf) workingMemory.getObjectTypeConfigurationRegistry().getObjectTypeConf(this.entryPoint.getEntryPoint(), new ArrayList());
assertLength(3,
conf.getObjectTypeNodes());
}
/**
* Test asserts correctly propagate
*
* @throws Exception
*/
@Test
public void testAssertObject() throws Exception {
final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) this.ruleBase.newStatefulSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = ruleBase.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.INSERTION,
null,
null,
null),
workingMemory);
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.INSERTION,
null,
null,
null),
workingMemory);
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() {
final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) this.ruleBase.newStatefulSession();
final Rete rete = ruleBase.getRete();
assertEquals(1,
rete.getObjectTypeNodes().size());
List list = new ArrayList();
workingMemory.insert(list);
assertEquals(2,
rete.getObjectTypeNodes().size());
}
@Test
@Ignore
public void testHierarchy() {
final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) this.ruleBase.newStatefulSession();
final Rete rete = ruleBase.getRete();
final IdGenerator idGenerator = ruleBase.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 = workingMemory.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) workingMemory.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
workingMemory.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);
workingMemory.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(new InternalWorkingMemory[]{workingMemory}));
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 retracted from a RootNode must be propagated to all children
* ObjectTypeNodes.
*/
@Test
public void testRetractObject() throws Exception {
final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) this.ruleBase.newStatefulSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = ruleBase.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.INSERTION,
null,
null,
null),
workingMemory);
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.INSERTION,
null,
null,
null),
workingMemory);
rete.retractObject(h2,
pctxFactory.createPropagationContext(0,
PropagationContext.INSERTION,
null,
null,
null),
workingMemory);
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() {
final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) this.ruleBase.newStatefulSession();
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = ruleBase.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.INSERTION,
null,
null,
null),
workingMemory);
final Object[] results = (Object[]) sink1.getAsserted().get(0);
}
@Test
public void testNotShadowed() {
Properties properties = new Properties();
properties.setProperty("drools.shadowProxyExcludes",
"org.drools.core.test.model.Cheese");
RuleBaseConfiguration conf = new RuleBaseConfiguration(properties);
final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory.newRuleBase(conf);
buildContext = new BuildContext(ruleBase,
((ReteooRuleBase) ruleBase).getReteooBuilder().getIdGenerator());
final AbstractWorkingMemory workingMemory = new AbstractWorkingMemory(1,
ruleBase);
// Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
final Rete rete = ruleBase.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();
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.INSERTION,
null,
null,
null),
workingMemory);
final Object[] results = (Object[]) sink1.getAsserted().get(0);
assertFalse(((DefaultFactHandle) results[0]).getObject() instanceof ShadowProxy);
}
public static class TestBuildContext extends BuildContext {
InternalWorkingMemory[] workingMemories;
TestBuildContext(InternalWorkingMemory[] workingMemories) {
super(workingMemories[0] != null ? (InternalRuleBase) workingMemories[0].getRuleBase() : null,
null);
this.workingMemories = workingMemories;
}
public InternalWorkingMemory[] getWorkingMemories() {
return workingMemories;
}
}
}