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 ObjectTypdeNode
s 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 ObjectTypeNode
s
* 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 + " ]";
}
}