package org.drools.reteoo; /* * Copyright 2007 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. * * Created on January 8th, 2007 */ import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.drools.base.ShadowProxy; import org.drools.common.BaseNode; import org.drools.common.InternalFactHandle; import org.drools.common.InternalWorkingMemory; import org.drools.common.InternalWorkingMemoryEntryPoint; import org.drools.common.NodeMemory; import org.drools.common.ObjectTypeConfigurationRegistry; import org.drools.common.PropagationContextImpl; import org.drools.common.RuleBasePartitionId; import org.drools.reteoo.builder.BuildContext; import org.drools.rule.EntryPoint; import org.drools.spi.ObjectType; import org.drools.spi.PropagationContext; import org.drools.util.Iterator; import org.drools.util.ObjectHashSet; import org.drools.util.ObjectHashSet.ObjectEntry; /** * 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 * * @author Edson Tirelli */ public class EntryPointNode extends ObjectSource implements Externalizable, ObjectSink { // ------------------------------------------------------------ // Instance members // ------------------------------------------------------------ private static final long serialVersionUID = 420L; /** * The entry point ID for this node */ private EntryPoint entryPoint; /** * The object type nodes under this node */ private Map objectTypeNodes; // ------------------------------------------------------------ // Constructors // ------------------------------------------------------------ public EntryPointNode() { } public EntryPointNode(final int id, final ObjectSource objectSource, final BuildContext context) { this( id, context.getPartitionId(), context.getRuleBase().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 EntryPoint entryPoint) { super( id, partitionId, partitionsEnabled, objectSource, 999 ); // irrelevant for this node, since it overrides sink management this.entryPoint = entryPoint; this.objectTypeNodes = new HashMap(); } // ------------------------------------------------------------ // Instance methods // ------------------------------------------------------------ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal( in ); entryPoint = (EntryPoint) in.readObject(); objectTypeNodes = (Map) in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal( out ); out.writeObject( entryPoint ); out.writeObject( objectTypeNodes ); } /** * @return the entryPoint */ public EntryPoint getEntryPoint() { return entryPoint; } public void assertObject(final InternalFactHandle handle, final PropagationContext context, final ObjectTypeConf objectTypeConf, final InternalWorkingMemory workingMemory) { // checks if shadow is enabled if ( objectTypeConf.isShadowEnabled() ) { // the user has implemented the ShadowProxy interface, let their implementation // know it is safe to update the information the engine can see. ((ShadowProxy) handle.getObject()).updateProxy(); } ObjectTypeNode[] cachedNodes = objectTypeConf.getObjectTypeNodes(); for ( int i = 0, length = cachedNodes.length; i < length; i++ ) { cachedNodes[i].assertObject( handle, context, workingMemory ); } } /** * 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) { final Object object = handle.getObject(); 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 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. */ protected void addObjectSink(final ObjectSink objectSink) { final ObjectTypeNode node = (ObjectTypeNode) objectSink; this.objectTypeNodes.put( node.getObjectType(), node ); } protected void removeObjectSink(final ObjectSink objectSink) { final ObjectTypeNode node = (ObjectTypeNode) objectSink; this.objectTypeNodes.remove( node.getObjectType() ); } public void attach() { this.source.addObjectSink( this ); } public void attach(final InternalWorkingMemory[] workingMemories) { attach(); for ( int i = 0, length = workingMemories.length; i < length; i++ ) { final InternalWorkingMemory workingMemory = workingMemories[i]; final PropagationContext propagationContext = new PropagationContextImpl( workingMemory.getNextPropagationIdCounter(), PropagationContext.RULE_ADDITION, null, null, null ); this.source.updateSink( this, propagationContext, workingMemory ); } } protected void doRemove(final RuleRemovalContext context, final ReteooBuilder builder, 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 Map getObjectTypeNodes() { return this.objectTypeNodes; } public int hashCode() { return this.entryPoint.hashCode(); } public boolean equals(final Object object) { if ( object == this ) { return true; } if ( object == null || !(object instanceof EntryPointNode) ) { return false; } final EntryPointNode other = (EntryPointNode) object; return this.entryPoint.equals( other.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(); InternalWorkingMemoryEntryPoint wmEntryPoint = (InternalWorkingMemoryEntryPoint) workingMemory.getWorkingMemoryEntryPoint( this.entryPoint.getEntryPointId() ); ObjectTypeConfigurationRegistry registry = wmEntryPoint.getObjectTypeConfigurationRegistry(); synchronized (registry.getRuleBase().getPackagesMap()) { for ( ObjectTypeConf objectTypeConf : registry.values() ) { if ( newObjectType.isAssignableFrom( objectTypeConf.getConcreteObjectTypeNode().getObjectType() ) ) { objectTypeConf.resetCache(); ObjectTypeNode sourceNode = objectTypeConf.getConcreteObjectTypeNode(); Iterator it = ((ObjectHashSet) workingMemory.getNodeMemory( sourceNode )).iterator(); for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) { sink.assertObject( (InternalFactHandle) entry.getValue(), context, workingMemory ); } } } } } public boolean isObjectMemoryEnabled() { return false; } public void setObjectMemoryEnabled(boolean objectMemoryEnabled) { throw new UnsupportedOperationException( "Entry Point Node has no Object memory" ); } public String toString() { return "[EntryPointNode(" + this.id + ") " + this.entryPoint + " ]"; } }