All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.drools.core.reteoo.EntryPointNode Maven / Gradle / Ivy

There is a newer version: 9.44.0.Final
Show newest version
/*
 * Copyright 2007 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.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.drools.core.WorkingMemoryEntryPoint;
import org.drools.core.base.ClassObjectType;
import org.drools.core.common.BaseNode;
import org.drools.core.common.EventFactHandle;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.RuleBasePartitionId;
import org.drools.core.phreak.PropagationEntry;
import org.drools.core.reteoo.builder.BuildContext;
import org.drools.core.rule.EntryPointId;
import org.drools.core.spi.ObjectType;
import org.drools.core.spi.PropagationContext;
import org.drools.core.util.bitmask.BitMask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A node that is an entry point into the Rete network.
 *
 * As we move the design to support network partitions and concurrent processing
 * of parts of the network, we also need to support multiple, independent entry
 * points and this class represents that.
 *
 * It replaces the function of the Rete Node class in previous designs.
 *
 * @see ObjectTypeNode
 */
public class EntryPointNode extends ObjectSource
    implements
    Externalizable,
    ObjectSink {
    // ------------------------------------------------------------
    // Instance members
    // ------------------------------------------------------------

    private static final long               serialVersionUID = 510l;

    protected static final transient Logger log = LoggerFactory.getLogger(EntryPointNode.class);

    /**
     * The entry point ID for this node
     */
    private EntryPointId                      entryPoint;

    /**
     * The object type nodes under this node
     */
    protected Map objectTypeNodes;

    protected ObjectTypeNode queryNode;

    private ObjectTypeNode activationNode;

    // ------------------------------------------------------------
    // Constructors
    // ------------------------------------------------------------
    public EntryPointNode() {

    }

    public EntryPointNode(final int id,
                          final ObjectSource objectSource,
                          final BuildContext context) {
        this( id,
              context.getPartitionId(),
              context.getKnowledgeBase().getConfiguration().isMultithreadEvaluation(),
              objectSource,
              context.getCurrentEntryPoint() ); // irrelevant for this node, since it overrides sink management
    }

    public EntryPointNode(final int id,
                          final RuleBasePartitionId partitionId,
                          final boolean partitionsEnabled,
                          final ObjectSource objectSource,
                          final EntryPointId entryPoint) {
        super( id,
               partitionId,
               partitionsEnabled,
               objectSource,
               999 ); // irrelevant for this node, since it overrides sink management
        this.entryPoint = entryPoint;
        this.objectTypeNodes = new ConcurrentHashMap();

        hashcode = calculateHashCode();
    }

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

    @SuppressWarnings("unchecked")
    public void readExternal(ObjectInput in) throws IOException,
                                            ClassNotFoundException {
        super.readExternal( in );
        entryPoint = (EntryPointId) in.readObject();
        objectTypeNodes = (Map) in.readObject();
    }

    public void writeExternal(ObjectOutput out) throws IOException {
        super.writeExternal(out);
        out.writeObject( entryPoint );
        out.writeObject( objectTypeNodes );
    }

    public short getType() {
        return NodeTypeEnums.EntryPointNode;
    }

    /**
     * @return the entryPoint
     */
    public EntryPointId getEntryPoint() {
        return entryPoint;
    }
    void setEntryPoint(EntryPointId entryPoint) {
        this.entryPoint = entryPoint;
    }

    public void assertQuery(final InternalFactHandle factHandle,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
        throw new UnsupportedOperationException("rete only");
    }

    public void retractQuery(final InternalFactHandle factHandle,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
        throw new UnsupportedOperationException("rete only");
    }

    public void modifyQuery(final InternalFactHandle factHandle,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
        throw new UnsupportedOperationException("rete only");
     }

    public ObjectTypeNode getQueryNode() {
        if ( queryNode == null ) {
            this.queryNode = objectTypeNodes.get( ClassObjectType.DroolsQuery_ObjectType );
        }
        return this.queryNode;
    }

    public void assertActivation(final InternalFactHandle factHandle,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
        if ( activationNode == null ) {
            this.activationNode = objectTypeNodes.get( ClassObjectType.Match_ObjectType );
        }

        if ( activationNode != null ) {
            // There may be no queries defined
            this.activationNode.propagateAssert(factHandle, context, workingMemory);
        }
    }

    public void retractActivation(final InternalFactHandle factHandle,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
        if ( activationNode == null ) {
            this.activationNode = objectTypeNodes.get( ClassObjectType.Match_ObjectType );
        }

        if ( activationNode != null ) {
            // There may be no queries defined
            this.activationNode.retractObject(factHandle, context, workingMemory);
        }
    }

    public void modifyActivation(final InternalFactHandle factHandle,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
         if ( activationNode == null ) {
             this.activationNode = objectTypeNodes.get( ClassObjectType.Match_ObjectType );
         }

         if ( activationNode != null ) {
             ModifyPreviousTuples modifyPreviousTuples = new ModifyPreviousTuples( factHandle.detachLinkedTuples() );

             // There may be no queries defined
             this.activationNode.modifyObject( factHandle, modifyPreviousTuples, context, workingMemory );
             modifyPreviousTuples.retractTuples(context, workingMemory);
         }

     }

    public void assertObject(final InternalFactHandle handle,
                             final PropagationContext context,
                             final ObjectTypeConf objectTypeConf,
                             final InternalWorkingMemory workingMemory) {
        if ( log.isTraceEnabled() ) {
            log.trace("Insert {}", handle.toString());
        }

        if ( partitionsEnabled ) {
            // In case of multithreaded evaluation the CompositePartitionAwareObjectSinkAdapter
            // used by the OTNs will take care of enqueueing this inseretion on the propagation queues
            // of the different agendas
            PropagationEntry.Insert.execute( handle, context, workingMemory, objectTypeConf );
        } else {
            workingMemory.addPropagation( new PropagationEntry.Insert( handle, context, workingMemory, objectTypeConf ) );
        }
    }


    public void modifyObject(final InternalFactHandle handle,
                             final PropagationContext pctx,
                             final ObjectTypeConf objectTypeConf,
                             final InternalWorkingMemory workingMemory) {
        if ( log.isTraceEnabled() ) {
            log.trace( "Update {}", handle.toString()  );
        }

        workingMemory.addPropagation(new PropagationEntry.Update(handle, pctx, objectTypeConf));
    }

    public static void propagateModify(InternalFactHandle handle, PropagationContext pctx, ObjectTypeConf objectTypeConf, InternalWorkingMemory wm) {
        // make a reference to the previous tuples, then null then on the handle
        propagateModify( handle, pctx, objectTypeConf, wm, new ModifyPreviousTuples( handle.detachLinkedTuples() ) );
    }

    public static void propagateModify( InternalFactHandle handle, PropagationContext pctx, ObjectTypeConf objectTypeConf, InternalWorkingMemory wm, ModifyPreviousTuples modifyPreviousTuples ) {
        ObjectTypeNode[] cachedNodes = objectTypeConf.getObjectTypeNodes();
        for ( int i = 0, length = cachedNodes.length; i < length; i++ ) {
            cachedNodes[i].modifyObject( handle, modifyPreviousTuples, pctx, wm );
            if (i < cachedNodes.length - 1) {
                removeRightTuplesMatchingOTN( pctx, wm, modifyPreviousTuples, cachedNodes[i], 0 );
            }
        }
        modifyPreviousTuples.retractTuples(pctx, wm);
    }

    public static void removeRightTuplesMatchingOTN( PropagationContext pctx, InternalWorkingMemory wm, ModifyPreviousTuples modifyPreviousTuples, ObjectTypeNode node, int partition ) {
        // remove any right tuples that matches the current OTN before continue the modify on the next OTN cache entry
        RightTuple rightTuple = modifyPreviousTuples.peekRightTuple(partition);
        while ( rightTuple != null &&
                ((BaseNode) rightTuple.getTupleSink()).getObjectTypeNode() == node ) {
            modifyPreviousTuples.removeRightTuple(partition);

            modifyPreviousTuples.doRightDelete(pctx, wm, rightTuple);

            rightTuple = modifyPreviousTuples.peekRightTuple(partition);
        }


        while ( true ) {
            LeftTuple leftTuple = modifyPreviousTuples.peekLeftTuple(partition);
            ObjectTypeNode otn = null;
            if (leftTuple != null) {
                LeftTupleSink leftTupleSink = leftTuple.getTupleSink();
                if (leftTupleSink instanceof LeftTupleSource ) {
                    otn = leftTupleSink.getLeftTupleSource().getObjectTypeNode();
                } else if (leftTupleSink instanceof RuleTerminalNode ) {
                    otn = ((RuleTerminalNode)leftTupleSink).getObjectTypeNode();
                }
            }

            if ( otn == node ) {
                modifyPreviousTuples.removeLeftTuple(partition);
                modifyPreviousTuples.doDeleteObject( pctx, wm, leftTuple );
            } else {
                break;
            }
        }
    }

    public void modifyObject(InternalFactHandle factHandle,
                                   ModifyPreviousTuples modifyPreviousTuples,
                                   PropagationContext context,
                                   InternalWorkingMemory workingMemory) {
        // this method was silently failing, so I am now throwing an exception to make
        // sure no one calls it by mistake
        throw new UnsupportedOperationException( "This method should NEVER EVER be called" );
    }

    /**
     * This is the entry point into the network for all asserted Facts. Iterates a cache
     * of matching ObjectTypdeNodes asserting the Fact. If the cache does not
     * exist it first iterates and builds the cache.
     *
     * @param factHandle
     *            The FactHandle of the fact to assert
     * @param context
     *            The PropagationContext of the WorkingMemory action
     * @param workingMemory
     *            The working memory session.
     */
    public void assertObject(final InternalFactHandle factHandle,
                             final PropagationContext context,
                             final InternalWorkingMemory workingMemory) {
        // this method was silently failing, so I am now throwing an exception to make
        // sure no one calls it by mistake
        throw new UnsupportedOperationException( "This method should NEVER EVER be called" );
    }

    /**
     * Retract a fact object from this RuleBase and the specified
     * WorkingMemory.
     *
     * @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 ObjectTypeConf objectTypeConf,
                              final InternalWorkingMemory workingMemory) {
        if ( log.isTraceEnabled() ) {
            log.trace( "Delete {}", handle.toString()  );
        }

        workingMemory.addPropagation(new PropagationEntry.Delete(this, handle, context, objectTypeConf));
    }

    public void propagateRetract(InternalFactHandle handle, PropagationContext context, ObjectTypeConf objectTypeConf, InternalWorkingMemory workingMemory) {
        ObjectTypeNode[] cachedNodes = objectTypeConf.getObjectTypeNodes();

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

        for ( ObjectTypeNode cachedNode : cachedNodes ) {
            cachedNode.retractObject( handle,
                                      context,
                                      workingMemory );
        }

        if (handle.isEvent()) {
            ((EventFactHandle) handle).unscheduleAllJobs(workingMemory);
        }
    }

    /**
     * Adds the ObjectSink so that it may receive
     * Objects propagated from this ObjectSource.
     *
     * @param objectSink
     *            The ObjectSink to receive propagated
     *            Objects. Rete only accepts ObjectTypeNodes
     *            as parameters to this method, though.
     */
    public void addObjectSink(final ObjectSink objectSink) {
        final ObjectTypeNode node = (ObjectTypeNode) objectSink;
        this.objectTypeNodes.put(node.getObjectType(),
                                 node);
    }

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

    public void removeObjectType(ObjectType objectType) {
        this.objectTypeNodes.remove( objectType );
    }

    public void attach() {
        attach(null);
    }

    public void attach( BuildContext context ) {
        this.source.addObjectSink( this );
        if (context == null ) {
            return;
        }
        for ( InternalWorkingMemory workingMemory : context.getWorkingMemories() ) {
            workingMemory.updateEntryPointsCache();
        }
    }

    protected boolean doRemove(final RuleRemovalContext context,
                               final ReteooBuilder builder,
                               final InternalWorkingMemory[] workingMemories) {
        return false;
    }

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

    private int calculateHashCode() {
        return this.entryPoint.hashCode();
    }

    @Override
    public boolean equals(final Object object) {
        return this == object || internalEquals( object );
    }

    @Override
    protected boolean internalEquals( Object object ) {
        return object instanceof EntryPointNode && this.hashCode() == object.hashCode() &&
               this.entryPoint.equals( ( (EntryPointNode) object ).entryPoint );
    }

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

        final ObjectType newObjectType = node.getObjectType();

        WorkingMemoryEntryPoint wmEntryPoint = workingMemory.getWorkingMemoryEntryPoint( this.entryPoint.getEntryPointId() );

        for ( ObjectTypeConf objectTypeConf : wmEntryPoint.getObjectTypeConfigurationRegistry().values() ) {
            if ( objectTypeConf.getConcreteObjectTypeNode() != null && newObjectType.isAssignableFrom( objectTypeConf.getConcreteObjectTypeNode().getObjectType() ) ) {
                objectTypeConf.resetCache();
                ObjectTypeNode sourceNode = objectTypeConf.getConcreteObjectTypeNode();
                Iterator it = workingMemory.getNodeMemory( sourceNode ).iterator();
                while ( it.hasNext() ) {
                    sink.assertObject( it.next(),
                                       context,
                                       workingMemory );
                }
            }
        }
    }

    public String toString() {
        return "[EntryPointNode(" + this.id + ") " + this.entryPoint + " ]";
    }

    public void byPassModifyToBetaNode(InternalFactHandle factHandle,
                                       ModifyPreviousTuples modifyPreviousTuples,
                                       PropagationContext context,
                                       InternalWorkingMemory workingMemory) {
        throw new UnsupportedOperationException();
    }

    @Override
    public BitMask calculateDeclaredMask(List settableProperties) {
        throw new UnsupportedOperationException();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy