Package org.drools.reteoo

Source Code of org.drools.reteoo.Rete

package org.drools.reteoo;

/*
* 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.
*/

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.drools.FactException;
import org.drools.RuleBaseConfiguration;
import org.drools.RuntimeDroolsException;
import org.drools.base.ClassObjectType;
import org.drools.base.DroolsQuery;
import org.drools.base.ShadowProxy;
import org.drools.base.ShadowProxyFactory;
import org.drools.common.BaseNode;
import org.drools.common.DroolsObjectInputStream;
import org.drools.common.InternalFactHandle;
import org.drools.common.InternalRuleBase;
import org.drools.common.InternalWorkingMemory;
import org.drools.common.NodeMemory;
import org.drools.facttemplates.Fact;
import org.drools.facttemplates.FactTemplate;
import org.drools.facttemplates.FactTemplateObjectType;
import org.drools.objenesis.Objenesis;
import org.drools.objenesis.ObjenesisStd;
import org.drools.objenesis.instantiator.ObjectInstantiator;
import org.drools.reteoo.builder.BuildContext;
import org.drools.reteoo.builder.PatternBuilder;
import org.drools.spi.ObjectType;
import org.drools.spi.PropagationContext;
import org.drools.util.FactEntry;
import org.drools.util.FactHashTable;
import org.drools.util.Iterator;
import org.drools.util.ObjectHashMap;
import org.drools.util.ObjectHashMap.ObjectEntry;

/**
* The Rete-OO network.
*
* The Rete class is the root <code>Object</code>. All objects are asserted into
* the Rete node where it propagates to all matching ObjectTypeNodes.
*
* The first time an  instance of a Class type is asserted it does a full
* iteration of all ObjectTyppeNodes looking for matches, any matches are
* then cached in a HashMap which is used for future assertions.
*
* While Rete  extends ObjectSource nad implements ObjectSink it nulls the
* methods attach(), remove() and  updateNewNode() as this is the root node
* they are no applicable
*
* @see ObjectTypeNode
*
* @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a>
* @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
*/
public class Rete extends ObjectSource
    implements
    Serializable,
    ObjectSink,
    NodeMemory {
    // ------------------------------------------------------------
    // Instance members
    // ------------------------------------------------------------

    /**
     *
     */
    private static final long          serialVersionUID = 400L;
    /** The <code>Map</code> of <code>ObjectTypeNodes</code>. */
    private final ObjectHashMap        objectTypeNodes;

    private transient InternalRuleBase ruleBase;

    // ------------------------------------------------------------
    // Constructors
    // ------------------------------------------------------------

    public Rete(InternalRuleBase ruleBase) {
        super( 0 );
        this.objectTypeNodes = new ObjectHashMap();
        this.ruleBase = ruleBase;
    }

    private void readObject(ObjectInputStream stream) throws IOException,
                                                     ClassNotFoundException {
        stream.defaultReadObject();
        this.ruleBase = ((DroolsObjectInputStream) stream).getRuleBase();
    }

    // ------------------------------------------------------------
    // Instance methods
    // ------------------------------------------------------------

    /**
     * This is the entry point into the network for all asserted Facts. Iterates a cache
     * of matching <code>ObjectTypdeNode</code>s asserting the Fact. If the cache does not
     * exist it first iteraes and builds the cache.
     *
     * @param handle
     *            The FactHandle of the fact to assert
     * @param context
     *            The <code>PropagationContext</code> of the <code>WorkingMemory</code> action
     * @param workingMemory
     *            The working memory session.
     */
    public void assertObject(final InternalFactHandle handle,
                             final PropagationContext context,
                             final InternalWorkingMemory workingMemory) {
        final ObjectHashMap memory = (ObjectHashMap) workingMemory.getNodeMemory( this );

        Object object = handle.getObject();

        ObjectTypeConf ojectTypeConf;
        if ( object instanceof Fact ) {
            String key = ((Fact) object).getFactTemplate().getName();
            ojectTypeConf = (ObjectTypeConf) memory.get( key );
            if ( ojectTypeConf == null ) {
                ojectTypeConf = new FactTemplateTypeConf( ((Fact) object).getFactTemplate(),
                                                          this.ruleBase );
                memory.put( key,
                            ojectTypeConf,
                            false );
            }
            object = key;
        } else {
            Class cls = null;
            if ( object instanceof ShadowProxy ) {
                cls = ((ShadowProxy) object).getShadowedObject().getClass();
            } else {
                cls = object.getClass();
            }

            ojectTypeConf = (ObjectTypeConf) memory.get( cls );
            if ( ojectTypeConf == null ) {
                ojectTypeConf = new ClassObjectTypeConf( cls,
                                                         this.ruleBase );
                memory.put( cls,
                            ojectTypeConf,
                            false );
            }

            // checks if shadow is enabled
            if ( ojectTypeConf.isShadowEnabled() ) {
                // need to improve this
                if ( !(handle.getObject() instanceof ShadowProxy) ) {
                    // replaces the actual object by its shadow before propagating
                    handle.setObject( ojectTypeConf.getShadow( handle.getObject() ) );
                    handle.setShadowFact( true );
                } else {
                    ((ShadowProxy) handle.getObject()).updateProxy();
                }
            }
        }

        ObjectTypeNode[] cachedNodes = ojectTypeConf.getObjectTypeNodes();

        for ( int i = 0, length = cachedNodes.length; i < length; i++ ) {
            cachedNodes[i].assertObject( handle,
                                         context,
                                         workingMemory );
        }
    }

    /**
     * Retract a fact object from this <code>RuleBase</code> and the specified
     * <code>WorkingMemory</code>.
     *
     * @param handle
     *            The handle of the fact to retract.
     * @param workingMemory
     *            The working memory session.
     */
    public void retractObject(final InternalFactHandle handle,
                              final PropagationContext context,
                              final InternalWorkingMemory workingMemory) {
        final ObjectHashMap memory = (ObjectHashMap) workingMemory.getNodeMemory( this );

        final Object object = handle.getObject();

        ObjectTypeConf objectTypeConf;
        if ( object instanceof ShadowProxy ) {
            objectTypeConf = (ObjectTypeConf) memory.get( ((ShadowProxy) object).getShadowedObject().getClass() );
        } else {
            objectTypeConf = (ObjectTypeConf) memory.get( object.getClass() );
        }

        ObjectTypeNode[] cachedNodes = objectTypeConf.getObjectTypeNodes();

        if ( cachedNodes == null ) {
            // it is  possible that there are no ObjectTypeNodes for an  object being retracted
            return;
        }

        for ( int i = 0; i < cachedNodes.length; i++ ) {
            cachedNodes[i].retractObject( handle,
                                          context,
                                          workingMemory );
        }
    }

    /**
     * Adds the <code>TupleSink</code> so that it may receive
     * <code>Tuples</code> propagated from this <code>TupleSource</code>.
     *
     * @param tupleSink
     *            The <code>TupleSink</code> to receive propagated
     *            <code>Tuples</code>.
     */
    protected void addObjectSink(final ObjectSink objectSink) {
        final ObjectTypeNode node = (ObjectTypeNode) objectSink;
        this.objectTypeNodes.put( node.getObjectType(),
                                  node,
                                  true );
    }

    protected void removeObjectSink(final ObjectSink objectSink) {
        final ObjectTypeNode node = (ObjectTypeNode) objectSink;
        this.objectTypeNodes.remove( node.getObjectType() );
    }

    public void attach() {
        throw new UnsupportedOperationException( "cannot call attach() from the root Rete node" );
    }

    public void attach(final InternalWorkingMemory[] workingMemories) {
        throw new UnsupportedOperationException( "cannot call attach() from the root Rete node" );
    }

    public void remove(final BaseNode node,
                       final InternalWorkingMemory[] workingMemories) {
        final ObjectTypeNode objectTypeNode = (ObjectTypeNode) node;
        removeObjectSink( objectTypeNode );
        for ( int i = 0; i < workingMemories.length; i++ ) {
            // clear the node memory for each working memory.
            workingMemories[i].clearNodeMemory( (NodeMemory) node );
        }
    }

    public ObjectHashMap getObjectTypeNodes() {
        return this.objectTypeNodes;
    }

    public Object createMemory(final RuleBaseConfiguration config) {
        return new ObjectHashMap();
    }

    public InternalRuleBase getRuleBase() {
        return this.ruleBase;
    }

    public int hashCode() {
        return this.objectTypeNodes.hashCode();
    }

    public boolean equals(final Object object) {
        if ( object == this ) {
            return true;
        }

        if ( object == null || !(object instanceof Rete) ) {
            return false;
        }

        final Rete other = (Rete) object;
        return this.objectTypeNodes.equals( other.objectTypeNodes );
    }

    public void updateSink(final ObjectSink sink,
                           final PropagationContext context,
                           final InternalWorkingMemory workingMemory) {
        // JBRULES-612: the cache MUST be invalidated when a new node type is added to the network, so iterate and reset all caches.
        final ObjectHashMap memory = (ObjectHashMap) workingMemory.getNodeMemory( this );
        Iterator it = memory.iterator();
        final ObjectTypeNode node = (ObjectTypeNode) sink;

        ObjectType newObjectType = node.getObjectType();

        for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
            ObjectTypeConf objectTypeConf = (ObjectTypeConf) entry.getValue();
            if ( newObjectType.isAssignableFrom( objectTypeConf.getConcreteObjectTypeNode().getObjectType() ) ) {
                objectTypeConf.resetCache();
                ObjectTypeNode sourceNode = objectTypeConf.getConcreteObjectTypeNode();
                FactHashTable table = (FactHashTable) workingMemory.getNodeMemory( sourceNode );
                Iterator factIter = table.iterator();
                for ( FactEntry factEntry = (FactEntry) factIter.next(); factEntry != null; factEntry = (FactEntry) factIter.next() ) {
                    sink.assertObject( factEntry.getFactHandle(),
                                       context,
                                       workingMemory );
                }
            }
        }

        //        ObjectType
        //        this.c

        //        final ObjectTypeNode node = (ObjectTypeNode) sink;
        //        it = workingMemory.getFactHandleMap().iterator();
        //        for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
        //            final InternalFactHandle handle = (InternalFactHandle) entry.getValue();
        //            if ( node.matches( handle.getObject() ) ) {
        //                node.assertObject( handle,
        //                                   context,
        //                                   workingMemory );
        //            }
        //        }
    }
   
    public boolean isObjectMemoryEnabled() {
        throw new UnsupportedOperationException("Rete has no Object memory");
    }

    public void setObjectMemoryEnabled(boolean objectMemoryEnabled) {
        throw new UnsupportedOperationException("ORete has no Object memory");
    }    

    public static interface ObjectTypeConf {
        public ObjectTypeNode[] getObjectTypeNodes();

        public boolean isShadowEnabled();

        public Object getShadow(final Object fact) throws RuntimeDroolsException;

        public ObjectTypeNode getConcreteObjectTypeNode();

        public void resetCache();

        public boolean isAssignableFrom(Object object);
       
        public boolean isActive();
    }

    public static class FactTemplateTypeConf
        implements
        ObjectTypeConf,
        Serializable {
        private InternalRuleBase ruleBase;
        private FactTemplate     factTemplate;
        private ObjectTypeNode   concreteObjectTypeNode;
        private ObjectTypeNode[] cache;

        public FactTemplateTypeConf(FactTemplate factTemplate,
                                    InternalRuleBase ruleBase) {
            this.ruleBase = ruleBase;
            this.factTemplate = factTemplate;
            ObjectType objectType = new FactTemplateObjectType( factTemplate );
            this.concreteObjectTypeNode = (ObjectTypeNode) ruleBase.getRete().getObjectTypeNodes().get( objectType );
            if ( this.concreteObjectTypeNode == null ) {
                BuildContext context = new BuildContext( ruleBase,
                                                         ((ReteooRuleBase) ruleBase.getRete().getRuleBase()).getReteooBuilder().getIdGenerator() );
                if ( context.getRuleBase().getConfiguration().isSequential() ) {
                    // We are in sequential mode, so no nodes should have memory
                    context.setTupleMemoryEnabled( false );
                    context.setObjectTypeNodeMemoryEnabled( false );
                    context.setTerminalNodeMemoryEnabled( false );
                } else {
                    context.setTupleMemoryEnabled( true );
                    context.setObjectTypeNodeMemoryEnabled( true );
                    context.setTerminalNodeMemoryEnabled( true );
                }
                // there must exist an ObjectTypeNode for this concrete class               
                this.concreteObjectTypeNode = PatternBuilder.attachObjectTypeNode( context,
                                                                                   objectType );
            }
            this.cache = new ObjectTypeNode[]{this.concreteObjectTypeNode};
        }

        public ObjectTypeNode getConcreteObjectTypeNode() {
            return this.concreteObjectTypeNode;
        }

        public ObjectTypeNode[] getObjectTypeNodes() {
            if ( this.cache == null ) {
                this.cache = new ObjectTypeNode[]{this.concreteObjectTypeNode};
            }
            return this.cache;
        }

        public Object getShadow(Object fact) throws RuntimeDroolsException {
            return null;
        }

        public boolean isShadowEnabled() {
            return false;
        }

        public boolean isAssignableFrom(Object object) {
            return this.factTemplate.equals( object );
        }

        public void resetCache() {
            this.cache = null;
        }

        public boolean isActive() {
            return true;
        }

    }

    public static class ClassObjectTypeConf
        implements
        ObjectTypeConf,
        Serializable {
        // Objenesis instance without cache (false)
        private static final Objenesis         OBJENESIS = new ObjenesisStd( false );

        private final Class                    cls;
        private transient InternalRuleBase     ruleBase;
        private ObjectTypeNode[]               objectTypeNodes;

        protected boolean                      shadowEnabled;
        protected Class                        shadowClass;
        protected transient ObjectInstantiator instantiator;

        private ObjectTypeNode                 concreteObjectTypeNode;

        public ClassObjectTypeConf(Class clazz,
                                   InternalRuleBase ruleBase) {
            this.cls = clazz;
            this.ruleBase = ruleBase;

            ObjectType objectType = new ClassObjectType( clazz );
            this.concreteObjectTypeNode = (ObjectTypeNode) ruleBase.getRete().getObjectTypeNodes().get( objectType );
            if ( this.concreteObjectTypeNode == null ) {
                BuildContext context = new BuildContext( ruleBase,
                                                         ((ReteooRuleBase) ruleBase.getRete().getRuleBase()).getReteooBuilder().getIdGenerator() );
                if ( DroolsQuery.class == clazz ) {
                    context.setTupleMemoryEnabled( false );
                    context.setObjectTypeNodeMemoryEnabled( false );
                    context.setTerminalNodeMemoryEnabled( false );
                } else if ( context.getRuleBase().getConfiguration().isSequential() ) {
                    // We are in sequential mode, so no nodes should have memory
                    context.setTupleMemoryEnabled( false );
                    context.setObjectTypeNodeMemoryEnabled( false );
                    context.setTerminalNodeMemoryEnabled( false );
                } else {
                    context.setTupleMemoryEnabled( true );
                    context.setObjectTypeNodeMemoryEnabled( true );
                    context.setTerminalNodeMemoryEnabled( true );
                }
                // there must exist an ObjectTypeNode for this concrete class
                this.concreteObjectTypeNode = PatternBuilder.attachObjectTypeNode( context,
                                                                                   objectType );
            }

            defineShadowProxyData( clazz );
        }

        public boolean isAssignableFrom(Object object) {
            return this.cls.isAssignableFrom( (Class) object );
        }

        public ObjectTypeNode getConcreteObjectTypeNode() {
            return this.concreteObjectTypeNode;
        }

        private void defineShadowProxyData(Class clazz) {
            Rete rete = this.ruleBase.getRete();

            if ( !ruleBase.getConfiguration().isShadowProxy() || clazz == null || !ruleBase.getConfiguration().isShadowed( clazz.getName() ) ) {
                this.shadowEnabled = false;
                this.shadowClass = null;
                this.instantiator = null;
                return;
            }

            //String pkgName = (pkg != null) ? pkg.getName() : "";
            String pkgName = getPackageName( clazz,
                                             clazz.getPackage() );
            if ( "org.drools.reteoo".equals( pkgName ) || "org.drools.base".equals( pkgName ) ) {
                // We don't shadow internal classes
                this.shadowEnabled = false;
                this.shadowClass = null;
                this.instantiator = null;
                return;
            }

            // try to generate proxy for the actual class
            Class shadowClass = loadOrGenerateProxy( clazz,
                                                     rete );

            if ( shadowClass == null ) {
                // if it failed, try to find a parent class
                ObjectTypeNode[] nodes = this.getMatchingObjectTypes( clazz );
                Class shadowClassRoot = clazz;
                while ( shadowClass == null && (shadowClassRoot = this.findAFeasibleSuperclassOrInterface( nodes,
                                                                                                           shadowClassRoot )) != null ) {
                    shadowClass = loadOrGenerateProxy( shadowClassRoot,
                                                       rete );
                }
            }

            if ( shadowClass != null ) {
                this.shadowClass = shadowClass;
                this.shadowEnabled = true;
                setInstantiator();
            }
        }

        /**
         * This will return the package name - if the package is null, it will
         * work it out from the class name (this is in cases where funky classloading is used).
         */
        public static String getPackageName(Class clazz,
                                            Package pkg) {
            String pkgName = "";
            if ( pkg == null ) {
                int index = clazz.getName().lastIndexOf( '.' );
                if ( index != -1 ) pkgName = clazz.getName().substring( 0,
                                                                        index );
            } else {
                pkgName = pkg.getName();
            }
            return pkgName;

        }

        private Class loadOrGenerateProxy(Class clazz,
                                          Rete rete) {
            Class shadowClass = null;
            final String shadowProxyName = ShadowProxyFactory.getProxyClassNameForClass( clazz );
            try {
                // if already loaded
                shadowClass = rete.getRuleBase().getMapBackedClassLoader().loadClass( shadowProxyName );
            } catch ( final ClassNotFoundException cnfe ) {
                // otherwise, create and load
                final byte[] proxyBytes = ShadowProxyFactory.getProxyBytes( clazz );
                if ( proxyBytes != null ) {
                    rete.getRuleBase().getMapBackedClassLoader().addClass( shadowProxyName,
                                                                           proxyBytes );
                    try {
                        shadowClass = rete.getRuleBase().getMapBackedClassLoader().loadClass( shadowProxyName );
                    } catch ( ClassNotFoundException e ) {
                        throw new RuntimeException( "Unable to find or generate the ShadowProxy implementation for '" + clazz + "'" );
                    }
                }

            }
            return shadowClass;
        }

        private Class findAFeasibleSuperclassOrInterface(ObjectTypeNode[] nodes,
                                                         Class clazz) {

            // check direct superclass
            Class ret = clazz.getSuperclass();
            boolean isOk = ret != null && ret != Object.class; // we don't want to shadow java.lang.Object
            if ( isOk ) {
                for ( int i = 0; isOk && ret != null && i < nodes.length; i++ ) {
                    isOk = nodes[i].getSinkPropagator().size() == 0 || nodes[i].isAssignableFrom( ret );
                }
            }

            if ( !isOk ) {
                // try the interfaces now...
                Class[] interfaces = clazz.getInterfaces();
                boolean notFound = true;
                isOk = interfaces.length > 0;
                for ( int i = 0; notFound && i < interfaces.length; i++ ) {
                    ret = interfaces[i];
                    isOk = interfaces[i] != Serializable.class && interfaces[i] != Cloneable.class && interfaces[i] != Comparable.class;
                    for ( int j = 0; isOk && j < nodes.length; j++ ) {
                        isOk = nodes[j].getSinkPropagator().size() == 0 || nodes[j].isAssignableFrom( ret );
                    }
                    notFound = !isOk;
                }
                if ( notFound ) {
                    ret = null;
                }
            }

            // ret now contains a superclass/interface that can be shadowed or null if none
            return ret;
        }

        private void readObject(ObjectInputStream stream) throws IOException,
                                                         ClassNotFoundException {
            stream.defaultReadObject();
            this.ruleBase = ((DroolsObjectInputStream) stream).getRuleBase();
        }

        /**
         *
         */
        private void setInstantiator() {
            this.instantiator = OBJENESIS.getInstantiatorOf( this.shadowClass );
        }

        public Object getShadow(final Object fact) throws RuntimeDroolsException {
            ShadowProxy proxy = null;
            if ( isShadowEnabled() ) {
                try {
                    if ( Collection.class.isAssignableFrom( this.shadowClass ) || Map.class.isAssignableFrom( this.shadowClass ) ) {
                        // if it is a collection, try to instantiate using constructor
                        try {
                            proxy = (ShadowProxy) this.shadowClass.getConstructor( new Class[]{cls} ).newInstance( new Object[]{fact} );
                        } catch ( Exception e ) {
                            // not possible to instantiate using constructor
                        }
                    }
                    if ( proxy == null ) {
                        if ( this.instantiator == null ) {
                            this.setInstantiator();
                        }
                        proxy = (ShadowProxy) this.instantiator.newInstance();
                    }
                    proxy.setShadowedObject( fact );
                } catch ( final Exception e ) {
                    throw new RuntimeDroolsException( "Error creating shadow fact for object: " + fact,
                                                      e );
                }
            }
            return proxy;
        }

        public boolean isShadowEnabled() {
            return this.shadowEnabled;
        }

        public void resetCache() {
            this.objectTypeNodes = null;
            defineShadowProxyData( cls );
        }

        public ObjectTypeNode[] getObjectTypeNodes() {
            if ( this.objectTypeNodes == null ) {
                this.objectTypeNodes = getMatchingObjectTypes( this.cls );
            }
            return this.objectTypeNodes;
        }

        private ObjectTypeNode[] getMatchingObjectTypes(final Class clazz) throws FactException {
            final List cache = new ArrayList();

            final Iterator it = ruleBase.getRete().getObjectTypeNodes().newIterator();
            for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
                final ObjectTypeNode node = (ObjectTypeNode) entry.getValue();
                if ( node.isAssignableFrom( clazz ) ) {
                    cache.add( node );
                }
            }

            return (ObjectTypeNode[]) cache.toArray( new ObjectTypeNode[cache.size()] );
        }

        public boolean isActive() {
            return getConcreteObjectTypeNode().getSinkPropagator().getSinks().length > 0;
        }
    }

}
TOP

Related Classes of org.drools.reteoo.Rete

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.