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.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.Serializable;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.drools.FactException;
028: import org.drools.RuleBaseConfiguration;
029: import org.drools.RuntimeDroolsException;
030: import org.drools.base.ClassObjectType;
031: import org.drools.base.ShadowProxy;
032: import org.drools.base.ShadowProxyFactory;
033: import org.drools.common.BaseNode;
034: import org.drools.common.DroolsObjectInputStream;
035: import org.drools.common.InternalFactHandle;
036: import org.drools.common.InternalRuleBase;
037: import org.drools.common.InternalWorkingMemory;
038: import org.drools.common.NodeMemory;
039: import org.drools.facttemplates.Fact;
040: import org.drools.facttemplates.FactImpl;
041: import org.drools.facttemplates.FactTemplate;
042: import org.drools.facttemplates.FactTemplateObjectType;
043: import org.drools.objenesis.Objenesis;
044: import org.drools.objenesis.ObjenesisStd;
045: import org.drools.objenesis.instantiator.ObjectInstantiator;
046: import org.drools.reteoo.builder.PatternBuilder;
047: import org.drools.spi.ObjectType;
048: import org.drools.spi.PropagationContext;
049: import org.drools.util.FactEntry;
050: import org.drools.util.FactHashTable;
051: import org.drools.util.Iterator;
052: import org.drools.util.ObjectHashMap;
053: import org.drools.util.ObjectHashMap.ObjectEntry;
054:
055: /**
056: * The Rete-OO network.
057: *
058: * The Rete class is the root <code>Object</code>. All objects are asserted into
059: * the Rete node where it propagates to all matching ObjectTypeNodes.
060: *
061: * The first time an instance of a Class type is asserted it does a full
062: * iteration of all ObjectTyppeNodes looking for matches, any matches are
063: * then cached in a HashMap which is used for future assertions.
064: *
065: * While Rete extends ObjectSource nad implements ObjectSink it nulls the
066: * methods attach(), remove() and updateNewNode() as this is the root node
067: * they are no applicable
068: *
069: * @see ObjectTypeNode
070: *
071: * @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a>
072: * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
073: */
074: public class Rete extends ObjectSource implements Serializable,
075: ObjectSink, NodeMemory {
076: // ------------------------------------------------------------
077: // Instance members
078: // ------------------------------------------------------------
079:
080: /**
081: *
082: */
083: private static final long serialVersionUID = 400L;
084: /** The <code>Map</code> of <code>ObjectTypeNodes</code>. */
085: private final ObjectHashMap objectTypeNodes;
086:
087: private transient InternalRuleBase ruleBase;
088:
089: // ------------------------------------------------------------
090: // Constructors
091: // ------------------------------------------------------------
092:
093: public Rete(InternalRuleBase ruleBase) {
094: super (0);
095: this .objectTypeNodes = new ObjectHashMap();
096: this .ruleBase = ruleBase;
097: }
098:
099: private void readObject(ObjectInputStream stream)
100: throws IOException, ClassNotFoundException {
101: stream.defaultReadObject();
102: this .ruleBase = ((DroolsObjectInputStream) stream)
103: .getRuleBase();
104: }
105:
106: // ------------------------------------------------------------
107: // Instance methods
108: // ------------------------------------------------------------
109:
110: /**
111: * This is the entry point into the network for all asserted Facts. Iterates a cache
112: * of matching <code>ObjectTypdeNode</code>s asserting the Fact. If the cache does not
113: * exist it first iteraes and builds the cache.
114: *
115: * @param handle
116: * The FactHandle of the fact to assert
117: * @param context
118: * The <code>PropagationContext</code> of the <code>WorkingMemory</code> action
119: * @param workingMemory
120: * The working memory session.
121: */
122: public void assertObject(final InternalFactHandle handle,
123: final PropagationContext context,
124: final InternalWorkingMemory workingMemory) {
125: final ObjectHashMap memory = (ObjectHashMap) workingMemory
126: .getNodeMemory(this );
127:
128: Object object = handle.getObject();
129:
130: ObjectTypeConf ojectTypeConf;
131: if (object instanceof FactImpl) {
132: String key = ((Fact) object).getFactTemplate().getName();
133: ojectTypeConf = (ObjectTypeConf) memory.get(key);
134: if (ojectTypeConf == null) {
135: ojectTypeConf = new FactTemplateTypeConf(
136: ((Fact) object).getFactTemplate(),
137: this .ruleBase);
138: memory.put(key, ojectTypeConf, false);
139: }
140: object = key;
141: } else {
142: Class cls = null;
143: if (object instanceof ShadowProxy) {
144: cls = ((ShadowProxy) object).getShadowedObject()
145: .getClass();
146: } else {
147: cls = object.getClass();
148: }
149:
150: ojectTypeConf = (ObjectTypeConf) memory.get(cls);
151: if (ojectTypeConf == null) {
152: ojectTypeConf = new ClassObjectTypeConf(cls,
153: this .ruleBase);
154: memory.put(cls, ojectTypeConf, false);
155: }
156:
157: // checks if shadow is enabled
158: if (ojectTypeConf.isShadowEnabled()) {
159: // need to improve this
160: if (!(handle.getObject() instanceof ShadowProxy)) {
161: // replaces the actual object by its shadow before propagating
162: handle.setObject(ojectTypeConf.getShadow(handle
163: .getObject()));
164: handle.setShadowFact(true);
165: } else {
166: ((ShadowProxy) handle.getObject()).updateProxy();
167: }
168: }
169: }
170:
171: ObjectTypeNode[] cachedNodes = ojectTypeConf
172: .getObjectTypeNodes();
173:
174: for (int i = 0, length = cachedNodes.length; i < length; i++) {
175: cachedNodes[i].assertObject(handle, context, workingMemory);
176: }
177: }
178:
179: /**
180: * Retract a fact object from this <code>RuleBase</code> and the specified
181: * <code>WorkingMemory</code>.
182: *
183: * @param handle
184: * The handle of the fact to retract.
185: * @param workingMemory
186: * The working memory session.
187: */
188: public void retractObject(final InternalFactHandle handle,
189: final PropagationContext context,
190: final InternalWorkingMemory workingMemory) {
191: final ObjectHashMap memory = (ObjectHashMap) workingMemory
192: .getNodeMemory(this );
193:
194: final Object object = handle.getObject();
195:
196: ObjectTypeConf objectTypeConf;
197: if (object instanceof ShadowProxy) {
198: objectTypeConf = (ObjectTypeConf) memory
199: .get(((ShadowProxy) object).getShadowedObject()
200: .getClass());
201: } else {
202: objectTypeConf = (ObjectTypeConf) memory.get(object
203: .getClass());
204: }
205:
206: ObjectTypeNode[] cachedNodes = objectTypeConf
207: .getObjectTypeNodes();
208:
209: if (cachedNodes == null) {
210: // it is possible that there are no ObjectTypeNodes for an object being retracted
211: return;
212: }
213:
214: for (int i = 0; i < cachedNodes.length; i++) {
215: cachedNodes[i]
216: .retractObject(handle, context, workingMemory);
217: }
218: }
219:
220: /**
221: * Adds the <code>TupleSink</code> so that it may receive
222: * <code>Tuples</code> propagated from this <code>TupleSource</code>.
223: *
224: * @param tupleSink
225: * The <code>TupleSink</code> to receive propagated
226: * <code>Tuples</code>.
227: */
228: protected void addObjectSink(final ObjectSink objectSink) {
229: final ObjectTypeNode node = (ObjectTypeNode) objectSink;
230: this .objectTypeNodes.put(node.getObjectType(), node, true);
231: }
232:
233: protected void removeObjectSink(final ObjectSink objectSink) {
234: final ObjectTypeNode node = (ObjectTypeNode) objectSink;
235: this .objectTypeNodes.remove(node.getObjectType());
236: }
237:
238: public void attach() {
239: throw new UnsupportedOperationException(
240: "cannot call attach() from the root Rete node");
241: }
242:
243: public void attach(final InternalWorkingMemory[] workingMemories) {
244: throw new UnsupportedOperationException(
245: "cannot call attach() from the root Rete node");
246: }
247:
248: public void remove(final BaseNode node,
249: final InternalWorkingMemory[] workingMemories) {
250: final ObjectTypeNode objectTypeNode = (ObjectTypeNode) node;
251: removeObjectSink(objectTypeNode);
252: for (int i = 0; i < workingMemories.length; i++) {
253: // clear the node memory for each working memory.
254: workingMemories[i].clearNodeMemory((NodeMemory) node);
255: }
256: }
257:
258: public ObjectHashMap getObjectTypeNodes() {
259: return this .objectTypeNodes;
260: }
261:
262: public Object createMemory(final RuleBaseConfiguration config) {
263: return new ObjectHashMap();
264: }
265:
266: public InternalRuleBase getRuleBase() {
267: return this .ruleBase;
268: }
269:
270: public int hashCode() {
271: return this .objectTypeNodes.hashCode();
272: }
273:
274: public boolean equals(final Object object) {
275: if (object == this ) {
276: return true;
277: }
278:
279: if (object == null || !(object instanceof Rete)) {
280: return false;
281: }
282:
283: final Rete other = (Rete) object;
284: return this .objectTypeNodes.equals(other.objectTypeNodes);
285: }
286:
287: public void updateSink(final ObjectSink sink,
288: final PropagationContext context,
289: final InternalWorkingMemory workingMemory) {
290: // JBRULES-612: the cache MUST be invalidated when a new node type is added to the network, so iterate and reset all caches.
291: final ObjectHashMap memory = (ObjectHashMap) workingMemory
292: .getNodeMemory(this );
293: Iterator it = memory.iterator();
294: final ObjectTypeNode node = (ObjectTypeNode) sink;
295:
296: ObjectType newObjectType = node.getObjectType();
297:
298: for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
299: .next()) {
300: ObjectTypeConf objectTypeConf = (ObjectTypeConf) entry
301: .getValue();
302: if (newObjectType.isAssignableFrom(objectTypeConf
303: .getConcreteObjectTypeNode().getObjectType())) {
304: objectTypeConf.resetCache();
305: ObjectTypeNode sourceNode = objectTypeConf
306: .getConcreteObjectTypeNode();
307: FactHashTable table = (FactHashTable) workingMemory
308: .getNodeMemory(sourceNode);
309: Iterator factIter = table.iterator();
310: for (FactEntry factEntry = (FactEntry) factIter.next(); factEntry != null; factEntry = (FactEntry) factIter
311: .next()) {
312: sink.assertObject(factEntry.getFactHandle(),
313: context, workingMemory);
314: }
315: }
316: }
317:
318: // ObjectType
319: // this.c
320:
321: // final ObjectTypeNode node = (ObjectTypeNode) sink;
322: // it = workingMemory.getFactHandleMap().iterator();
323: // for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
324: // final InternalFactHandle handle = (InternalFactHandle) entry.getValue();
325: // if ( node.matches( handle.getObject() ) ) {
326: // node.assertObject( handle,
327: // context,
328: // workingMemory );
329: // }
330: // }
331: }
332:
333: public static interface ObjectTypeConf {
334: public ObjectTypeNode[] getObjectTypeNodes();
335:
336: public boolean isShadowEnabled();
337:
338: public Object getShadow(final Object fact)
339: throws RuntimeDroolsException;
340:
341: public ObjectTypeNode getConcreteObjectTypeNode();
342:
343: public void resetCache();
344:
345: public boolean isAssignableFrom(Object object);
346: }
347:
348: public static class FactTemplateTypeConf implements ObjectTypeConf,
349: Serializable {
350: private InternalRuleBase ruleBase;
351: private FactTemplate factTemplate;
352: private ObjectTypeNode concreteObjectTypeNode;
353: private ObjectTypeNode[] cache;
354:
355: public FactTemplateTypeConf(FactTemplate factTemplate,
356: InternalRuleBase ruleBase) {
357: this .ruleBase = ruleBase;
358: this .factTemplate = factTemplate;
359: ObjectType objectType = new FactTemplateObjectType(
360: factTemplate);
361: this .concreteObjectTypeNode = (ObjectTypeNode) ruleBase
362: .getRete().getObjectTypeNodes().get(objectType);
363: if (this .concreteObjectTypeNode == null) {
364: // there must exist an ObjectTypeNode for this concrete class
365: this .concreteObjectTypeNode = PatternBuilder
366: .attachObjectTypeNode(ruleBase.getRete(),
367: objectType);
368: }
369: this .cache = new ObjectTypeNode[] { this .concreteObjectTypeNode };
370: }
371:
372: public ObjectTypeNode getConcreteObjectTypeNode() {
373: return this .concreteObjectTypeNode;
374: }
375:
376: public ObjectTypeNode[] getObjectTypeNodes() {
377: if (this .cache == null) {
378: this .cache = new ObjectTypeNode[] { this .concreteObjectTypeNode };
379: }
380: return this .cache;
381: }
382:
383: public Object getShadow(Object fact)
384: throws RuntimeDroolsException {
385: return null;
386: }
387:
388: public boolean isShadowEnabled() {
389: return false;
390: }
391:
392: public boolean isAssignableFrom(Object object) {
393: return this .factTemplate.equals(object);
394: }
395:
396: public void resetCache() {
397: this .cache = null;
398: }
399:
400: }
401:
402: public static class ClassObjectTypeConf implements ObjectTypeConf,
403: Serializable {
404: // Objenesis instance without cache (false)
405: private static final Objenesis OBJENESIS = new ObjenesisStd(
406: false);
407:
408: private final Class cls;
409: private transient InternalRuleBase ruleBase;
410: private ObjectTypeNode[] objectTypeNodes;
411:
412: protected boolean shadowEnabled;
413: protected Class shadowClass;
414: protected transient ObjectInstantiator instantiator;
415:
416: private ObjectTypeNode concreteObjectTypeNode;
417:
418: public ClassObjectTypeConf(Class clazz,
419: InternalRuleBase ruleBase) {
420: this .cls = clazz;
421: this .ruleBase = ruleBase;
422:
423: ObjectType objectType = new ClassObjectType(clazz);
424: this .concreteObjectTypeNode = (ObjectTypeNode) ruleBase
425: .getRete().getObjectTypeNodes().get(objectType);
426: if (this .concreteObjectTypeNode == null) {
427: // there must exist an ObjectTypeNode for this concrete class
428: this .concreteObjectTypeNode = PatternBuilder
429: .attachObjectTypeNode(ruleBase.getRete(),
430: objectType);
431: }
432:
433: defineShadowProxyData(clazz);
434: }
435:
436: public boolean isAssignableFrom(Object object) {
437: return this .cls.isAssignableFrom((Class) object);
438: }
439:
440: public ObjectTypeNode getConcreteObjectTypeNode() {
441: return this .concreteObjectTypeNode;
442: }
443:
444: private void defineShadowProxyData(Class clazz) {
445: Rete rete = this .ruleBase.getRete();
446:
447: if (!ruleBase.getConfiguration().isShadowProxy()
448: || clazz == null
449: || !ruleBase.getConfiguration().isShadowed(
450: clazz.getName())) {
451: this .shadowEnabled = false;
452: this .shadowClass = null;
453: this .instantiator = null;
454: return;
455: }
456:
457: //String pkgName = (pkg != null) ? pkg.getName() : "";
458: String pkgName = getPackageName(clazz, clazz.getPackage());
459: if ("org.drools.reteoo".equals(pkgName)
460: || "org.drools.base".equals(pkgName)) {
461: // We don't shadow internal classes
462: this .shadowEnabled = false;
463: this .shadowClass = null;
464: this .instantiator = null;
465: return;
466: }
467:
468: // try to generate proxy for the actual class
469: Class shadowClass = loadOrGenerateProxy(clazz, rete);
470:
471: if (shadowClass == null) {
472: // if it failed, try to find a parent class
473: ObjectTypeNode[] nodes = this
474: .getMatchingObjectTypes(clazz);
475: Class shadowClassRoot = clazz;
476: while (shadowClass == null
477: && (shadowClassRoot = this
478: .findAFeasibleSuperclassOrInterface(
479: nodes, shadowClassRoot)) != null) {
480: shadowClass = loadOrGenerateProxy(shadowClassRoot,
481: rete);
482: }
483: }
484:
485: if (shadowClass != null) {
486: this .shadowClass = shadowClass;
487: this .shadowEnabled = true;
488: setInstantiator();
489: }
490: }
491:
492: /**
493: * This will return the package name - if the package is null, it will
494: * work it out from the class name (this is in cases where funky classloading is used).
495: */
496: public static String getPackageName(Class clazz, Package pkg) {
497: String pkgName = "";
498: if (pkg == null) {
499: int index = clazz.getName().lastIndexOf('.');
500: if (index != -1)
501: pkgName = clazz.getName().substring(0, index);
502: } else {
503: pkgName = pkg.getName();
504: }
505: return pkgName;
506:
507: }
508:
509: private Class loadOrGenerateProxy(Class clazz, Rete rete) {
510: Class shadowClass = null;
511: final String shadowProxyName = ShadowProxyFactory
512: .getProxyClassNameForClass(clazz);
513: try {
514: // if already loaded
515: shadowClass = rete.getRuleBase()
516: .getMapBackedClassLoader().loadClass(
517: shadowProxyName);
518: } catch (final ClassNotFoundException cnfe) {
519: // otherwise, create and load
520: final byte[] proxyBytes = ShadowProxyFactory
521: .getProxyBytes(clazz);
522: if (proxyBytes != null) {
523: rete.getRuleBase().getMapBackedClassLoader()
524: .addClass(shadowProxyName, proxyBytes);
525: try {
526: shadowClass = rete.getRuleBase()
527: .getMapBackedClassLoader().loadClass(
528: shadowProxyName);
529: } catch (ClassNotFoundException e) {
530: throw new RuntimeException(
531: "Unable to find or generate the ShadowProxy implementation for '"
532: + clazz + "'");
533: }
534: }
535:
536: }
537: return shadowClass;
538: }
539:
540: private Class findAFeasibleSuperclassOrInterface(
541: ObjectTypeNode[] nodes, Class clazz) {
542:
543: // check direct superclass
544: Class ret = clazz.getSuperclass();
545: boolean isOk = ret != null && ret != Object.class; // we don't want to shadow java.lang.Object
546: if (isOk) {
547: for (int i = 0; isOk && ret != null && i < nodes.length; i++) {
548: isOk = nodes[i].isAssignableFrom(ret);
549: }
550: }
551:
552: if (!isOk) {
553: // try the interfaces now...
554: Class[] interfaces = clazz.getInterfaces();
555: boolean notFound = true;
556: isOk = interfaces.length > 0;
557: for (int i = 0; notFound && i < interfaces.length; i++) {
558: ret = interfaces[i];
559: isOk = interfaces[i] != Serializable.class
560: && interfaces[i] != Cloneable.class
561: && interfaces[i] != Comparable.class;
562: for (int j = 0; isOk && j < nodes.length; j++) {
563: isOk = nodes[j].isAssignableFrom(ret);
564: }
565: notFound = !isOk;
566: }
567: if (notFound) {
568: ret = null;
569: }
570: }
571:
572: // ret now contains a superclass/interface that can be shadowed or null if none
573: return ret;
574: }
575:
576: private void readObject(ObjectInputStream stream)
577: throws IOException, ClassNotFoundException {
578: stream.defaultReadObject();
579: this .ruleBase = ((DroolsObjectInputStream) stream)
580: .getRuleBase();
581: }
582:
583: /**
584: *
585: */
586: private void setInstantiator() {
587: this .instantiator = OBJENESIS
588: .getInstantiatorOf(this .shadowClass);
589: }
590:
591: public Object getShadow(final Object fact)
592: throws RuntimeDroolsException {
593: ShadowProxy proxy = null;
594: if (isShadowEnabled()) {
595: try {
596: if (Collection.class
597: .isAssignableFrom(this .shadowClass)
598: || Map.class
599: .isAssignableFrom(this .shadowClass)) {
600: // if it is a collection, try to instantiate using constructor
601: try {
602: proxy = (ShadowProxy) this .shadowClass
603: .getConstructor(new Class[] { cls })
604: .newInstance(new Object[] { fact });
605: } catch (Exception e) {
606: // not possible to instantiate using constructor
607: }
608: }
609: if (proxy == null) {
610: if (this .instantiator == null) {
611: this .setInstantiator();
612: }
613: proxy = (ShadowProxy) this .instantiator
614: .newInstance();
615: }
616: proxy.setShadowedObject(fact);
617: } catch (final Exception e) {
618: throw new RuntimeDroolsException(
619: "Error creating shadow fact for object: "
620: + fact, e);
621: }
622: }
623: return proxy;
624: }
625:
626: public boolean isShadowEnabled() {
627: return this .shadowEnabled;
628: }
629:
630: public void resetCache() {
631: this .objectTypeNodes = null;
632: defineShadowProxyData(cls);
633: }
634:
635: public ObjectTypeNode[] getObjectTypeNodes() {
636: if (this .objectTypeNodes == null) {
637: this .objectTypeNodes = getMatchingObjectTypes(this .cls);
638: }
639: return this .objectTypeNodes;
640: }
641:
642: private ObjectTypeNode[] getMatchingObjectTypes(
643: final Class clazz) throws FactException {
644: final List cache = new ArrayList();
645:
646: final Iterator it = ruleBase.getRete().getObjectTypeNodes()
647: .newIterator();
648: for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
649: .next()) {
650: final ObjectTypeNode node = (ObjectTypeNode) entry
651: .getValue();
652: if (node.isAssignableFrom(clazz)) {
653: cache.add(node);
654: }
655: }
656:
657: return (ObjectTypeNode[]) cache
658: .toArray(new ObjectTypeNode[cache.size()]);
659: }
660: }
661:
662: }
|