001: package org.drools.reteoo;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.lang.reflect.Field;
020: import java.util.ArrayList;
021: import java.util.Arrays;
022: import java.util.Collection;
023: import java.util.LinkedList;
024: import java.util.List;
025: import java.util.Properties;
026:
027: import org.drools.Cheese;
028: import org.drools.DroolsTestCase;
029: import org.drools.FactException;
030: import org.drools.FactHandle;
031: import org.drools.RuleBaseConfiguration;
032: import org.drools.RuleBaseFactory;
033: import org.drools.base.ClassObjectType;
034: import org.drools.base.ShadowProxy;
035: import org.drools.common.DefaultFactHandle;
036: import org.drools.common.InternalWorkingMemory;
037: import org.drools.common.PropagationContextImpl;
038: import org.drools.reteoo.Rete.ClassObjectTypeConf;
039: import org.drools.reteoo.Rete.ObjectTypeConf;
040: import org.drools.reteoo.ReteooBuilder.IdGenerator;
041: import org.drools.spi.PropagationContext;
042: import org.drools.util.ObjectHashMap;
043:
044: /**
045: * @author mproctor
046: *
047: */
048: public class ReteTest extends DroolsTestCase {
049:
050: /**
051: * Tests ObjectTypeNodes are correctly added to the Rete object
052: *
053: * @throws Exception
054: */
055: public void testObjectTypeNodes() throws Exception {
056: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
057: .newRuleBase();
058:
059: final Rete rete = ruleBase.getRete();
060:
061: final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
062: new ClassObjectType(Object.class), rete, 3);
063: objectTypeNode.attach();
064:
065: final ObjectTypeNode stringTypeNode = new ObjectTypeNode(2,
066: new ClassObjectType(String.class), rete, 3);
067: stringTypeNode.attach();
068:
069: final Field field = Rete.class
070: .getDeclaredField("objectTypeNodes");
071: field.setAccessible(true);
072: final ObjectHashMap map = (ObjectHashMap) field.get(rete);
073:
074: // Check the ObjectTypeNodes are correctly added to Rete
075: assertEquals(2, map.size());
076:
077: assertNotNull(map.get(new ClassObjectType(Object.class)));
078: assertNotNull(map.get(new ClassObjectType(String.class)));
079: }
080:
081: /**
082: * Tests that interfaces and parent classes for an asserted class are cached, for quick future iterations
083: *
084: * @throws FactException
085: */
086: public void testCache() throws FactException {
087: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
088: .newRuleBase();
089: final ReteooWorkingMemory workingMemory = new ReteooWorkingMemory(
090: 1, ruleBase);
091:
092: // Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
093: final Rete rete = ruleBase.getRete();
094: ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
095: new ClassObjectType(List.class), rete, 3);
096: objectTypeNode.attach();
097: MockObjectSink sink = new MockObjectSink();
098: objectTypeNode.addObjectSink(sink);
099:
100: objectTypeNode = new ObjectTypeNode(1, new ClassObjectType(
101: Collection.class), rete, 3);
102: objectTypeNode.attach();
103: sink = new MockObjectSink();
104: objectTypeNode.addObjectSink(sink);
105:
106: objectTypeNode = new ObjectTypeNode(1, new ClassObjectType(
107: ArrayList.class), rete, 3);
108: objectTypeNode.attach();
109: sink = new MockObjectSink();
110: objectTypeNode.addObjectSink(sink);
111:
112: // ArrayList matches all three ObjectTypeNodes
113: final DefaultFactHandle h1 = new DefaultFactHandle(1,
114: new ArrayList());
115: rete.assertObject(h1, new PropagationContextImpl(0,
116: PropagationContext.ASSERTION, null, null),
117: workingMemory);
118:
119: // LinkedList matches two ObjectTypeNodes
120: h1.setObject(new LinkedList());
121: rete.assertObject(h1, new PropagationContextImpl(0,
122: PropagationContext.ASSERTION, null, null),
123: workingMemory);
124:
125: final ObjectHashMap map = (ObjectHashMap) workingMemory
126: .getNodeMemory(rete);
127: ClassObjectTypeConf conf = (ClassObjectTypeConf) map
128: .get(ArrayList.class);
129: assertLength(3, conf.getObjectTypeNodes());
130:
131: conf = (ClassObjectTypeConf) map.get(LinkedList.class);
132: assertLength(3, conf.getObjectTypeNodes());
133:
134: }
135:
136: /**
137: * Test asserts correctly propagate
138: *
139: * @throws Exception
140: */
141: public void testAssertObject() throws Exception {
142: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
143: .newRuleBase();
144: final ReteooWorkingMemory workingMemory = new ReteooWorkingMemory(
145: 1, ruleBase);
146:
147: // Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
148: final Rete rete = ruleBase.getRete();
149: final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
150: new ClassObjectType(List.class), rete, 3);
151: objectTypeNode.attach();
152: final MockObjectSink sink1 = new MockObjectSink();
153: objectTypeNode.addObjectSink(sink1);
154:
155: // There are no String ObjectTypeNodes, make sure its not propagated
156:
157: final String string = "String";
158: final DefaultFactHandle h1 = new DefaultFactHandle(1, string);
159:
160: rete.assertObject(h1, new PropagationContextImpl(0,
161: PropagationContext.ASSERTION, null, null),
162: workingMemory);
163:
164: assertLength(0, sink1.getAsserted());
165:
166: // There is a List ObjectTypeNode, make sure it was propagated
167: final List list = new ArrayList();
168: final DefaultFactHandle h2 = new DefaultFactHandle(1, list);
169:
170: rete.assertObject(h2, new PropagationContextImpl(0,
171: PropagationContext.ASSERTION, null, null),
172: workingMemory);
173:
174: final List asserted = sink1.getAsserted();
175: assertLength(1, asserted);
176:
177: final Object[] results = (Object[]) asserted.get(0);
178: assertSame(list, unwrapShadow(((DefaultFactHandle) results[0])
179: .getObject()));
180: }
181:
182: public void testAssertObjectWithNoMatchingObjectTypeNode() {
183: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
184: .newRuleBase();
185: final ReteooWorkingMemory workingMemory = (ReteooWorkingMemory) ruleBase
186: .newStatefulSession();
187:
188: final Rete rete = ruleBase.getRete();
189: assertEquals(0, rete.getObjectTypeNodes().size());
190:
191: List list = new ArrayList();
192:
193: workingMemory.insert(list);
194:
195: assertEquals(2, rete.getObjectTypeNodes().size());
196: }
197:
198: public void testHierarchy() {
199: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
200: .newRuleBase();
201: final ReteooWorkingMemory workingMemory = (ReteooWorkingMemory) ruleBase
202: .newStatefulSession();
203:
204: final Rete rete = ruleBase.getRete();
205: final IdGenerator idGenerator = ruleBase.getReteooBuilder()
206: .getIdGenerator();
207:
208: // Attach a List ObjectTypeNode
209: final ObjectTypeNode listOtn = new ObjectTypeNode(idGenerator
210: .getNextId(), new ClassObjectType(List.class), rete, 3);
211: listOtn.attach();
212:
213: // Will automatically create an ArrayList ObjectTypeNode
214: FactHandle handle = workingMemory.insert(new ArrayList());
215:
216: // Check we have three ObjectTypeNodes, List, ArrayList and InitialFactImpl
217: assertEquals(3, rete.getObjectTypeNodes().size());
218:
219: // double check that the Listreference is the same as the one we created, i.e. engine should try and recreate it
220: assertSame(listOtn, rete.getObjectTypeNodes().get(
221: new ClassObjectType(List.class)));
222:
223: // ArrayConf should match two ObjectTypenodes for List and ArrayList
224: ObjectHashMap memory = (ObjectHashMap) workingMemory
225: .getNodeMemory(rete);
226: ObjectTypeConf arrayConf = (ObjectTypeConf) memory
227: .get(ArrayList.class);
228: final ObjectTypeNode arrayOtn = arrayConf
229: .getConcreteObjectTypeNode();
230: assertEquals(2, arrayConf.getObjectTypeNodes().length);
231:
232: // Check it contains List and ArrayList
233: List nodes = Arrays.asList(arrayConf.getObjectTypeNodes());
234: assertEquals(2, nodes.size());
235: assertTrue(nodes.contains(arrayOtn));
236: assertTrue(nodes.contains(listOtn));
237:
238: // Nodes are there, retract the fact so we can check both nodes are populated
239: workingMemory.retract(handle);
240:
241: // Add MockSinks so we can track assertions
242: final MockObjectSink listSink = new MockObjectSink();
243: listOtn.addObjectSink(listSink);
244:
245: final MockObjectSink arraySink = new MockObjectSink();
246: listOtn.addObjectSink(arraySink);
247:
248: workingMemory.insert(new ArrayList());
249: assertEquals(1, listSink.getAsserted().size());
250: assertEquals(1, arraySink.getAsserted().size());
251:
252: // Add a Collection ObjectTypeNode, so that we can check that the data from ArrayList is sent to it
253: final ObjectTypeNode collectionOtn = new ObjectTypeNode(
254: idGenerator.getNextId(), new ClassObjectType(
255: Collection.class), rete, 3);
256: final MockObjectSink collectionSink = new MockObjectSink();
257: collectionOtn.addObjectSink(collectionSink);
258: collectionOtn
259: .attach(new InternalWorkingMemory[] { workingMemory });
260:
261: assertEquals(1, collectionSink.getAsserted().size());
262:
263: // check that ArrayListConf was updated with the new ObjectTypeNode
264: nodes = Arrays.asList(arrayConf.getObjectTypeNodes());
265: assertEquals(3, nodes.size());
266: assertTrue(nodes.contains(arrayOtn));
267: assertTrue(nodes.contains(listOtn));
268: assertTrue(nodes.contains(collectionOtn));
269: }
270:
271: /**
272: * All objects retracted from a RootNode must be propagated to all children
273: * ObjectTypeNodes.
274: */
275: public void testRetractObject() throws Exception {
276: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
277: .newRuleBase();
278: final ReteooWorkingMemory workingMemory = new ReteooWorkingMemory(
279: 1, ruleBase);
280:
281: // Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
282: final Rete rete = ruleBase.getRete();
283: final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
284: new ClassObjectType(List.class), rete, 3);
285: objectTypeNode.attach();
286: final MockObjectSink sink1 = new MockObjectSink();
287: objectTypeNode.addObjectSink(sink1);
288:
289: // There are no String ObjectTypeNodes, make sure its not propagated
290: final String string = "String";
291: final DefaultFactHandle h1 = new DefaultFactHandle(1, string);
292:
293: rete.assertObject(h1, new PropagationContextImpl(0,
294: PropagationContext.ASSERTION, null, null),
295: workingMemory);
296: assertLength(0, sink1.getAsserted());
297: assertLength(0, sink1.getRetracted());
298:
299: // There is a List ObjectTypeNode, make sure it was propagated
300: final List list = new ArrayList();
301: final DefaultFactHandle h2 = new DefaultFactHandle(1, list);
302:
303: // need to assert first, to force it to build up the cache
304: rete.assertObject(h2, new PropagationContextImpl(0,
305: PropagationContext.ASSERTION, null, null),
306: workingMemory);
307:
308: rete.retractObject(h2, new PropagationContextImpl(0,
309: PropagationContext.ASSERTION, null, null),
310: workingMemory);
311:
312: final List retracted = sink1.getRetracted();
313: assertLength(1, retracted);
314:
315: final Object[] results = (Object[]) retracted.get(0);
316: assertSame(list, unwrapShadow(((DefaultFactHandle) results[0])
317: .getObject()));
318: }
319:
320: public void testIsShadowed() {
321: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
322: .newRuleBase();
323: final ReteooWorkingMemory workingMemory = new ReteooWorkingMemory(
324: 1, ruleBase);
325:
326: // Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
327: final Rete rete = ruleBase.getRete();
328: final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
329: new ClassObjectType(Cheese.class), rete, 3);
330: objectTypeNode.attach();
331: final MockObjectSink sink1 = new MockObjectSink();
332: objectTypeNode.addObjectSink(sink1);
333:
334: // There are no String ObjectTypeNodes, make sure its not propagated
335:
336: final Cheese cheese = new Cheese("brie", 15);
337: final DefaultFactHandle h1 = new DefaultFactHandle(1, cheese);
338:
339: rete.assertObject(h1, new PropagationContextImpl(0,
340: PropagationContext.ASSERTION, null, null),
341: workingMemory);
342:
343: assertTrue(h1.isShadowFact());
344:
345: final Object[] results = (Object[]) sink1.getAsserted().get(0);
346: assertTrue(((DefaultFactHandle) results[0]).getObject() instanceof ShadowProxy);
347: }
348:
349: public void testNotShadowed() {
350:
351: Properties properties = new Properties();
352: properties.setProperty("drools.shadowProxyExcludes",
353: "org.drools.Cheese");
354: RuleBaseConfiguration conf = new RuleBaseConfiguration(
355: properties);
356: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
357: .newRuleBase(conf);
358: final ReteooWorkingMemory workingMemory = new ReteooWorkingMemory(
359: 1, ruleBase);
360:
361: // Create a Rete network with ObjectTypeNodes for List, Collection and ArrayList
362: final Rete rete = ruleBase.getRete();
363: final ObjectTypeNode objectTypeNode = new ObjectTypeNode(1,
364: new ClassObjectType(Cheese.class), rete, 3);
365: objectTypeNode.attach();
366: final MockObjectSink sink1 = new MockObjectSink();
367: objectTypeNode.addObjectSink(sink1);
368:
369: // There are no String ObjectTypeNodes, make sure its not propagated
370:
371: final Cheese cheese = new Cheese("brie", 15);
372: final DefaultFactHandle h1 = new DefaultFactHandle(1, cheese);
373:
374: rete.assertObject(h1, new PropagationContextImpl(0,
375: PropagationContext.ASSERTION, null, null),
376: workingMemory);
377:
378: assertFalse(h1.isShadowFact());
379: final Object[] results = (Object[]) sink1.getAsserted().get(0);
380: assertFalse(((DefaultFactHandle) results[0]).getObject() instanceof ShadowProxy);
381: }
382:
383: private Object unwrapShadow(Object object) {
384: if (object instanceof ShadowProxy) {
385: return ((ShadowProxy) object).getShadowedObject();
386: } else {
387: return object;
388: }
389: }
390: }
|