/** * 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. */ package org.drools.reteoo; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import org.drools.common.BaseNode; import org.drools.common.DefaultFactHandle; import org.drools.common.InternalWorkingMemory; import org.drools.common.NodeMemory; import org.drools.common.RuleBasePartitionId; import org.drools.spi.PropagationContext; /** * A source of <code>FactHandle</code>s for an <code>ObjectSink</code>. * * <p> * Nodes that propagate <code>FactHandleImpl</code> extend this class. * </p> * * @see ObjectSource * @see DefaultFactHandle * * @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a> * @author <a href="mailto:bob@werken.com">Bob McWhirter</a> */ public abstract class ObjectSource extends BaseNode implements Externalizable { // ------------------------------------------------------------ // Instance members // ------------------------------------------------------------ /** The destination for <code>FactHandleImpl</code>. */ protected ObjectSinkPropagator sink; protected ObjectSource source; private int alphaNodeHashingThreshold; // ------------------------------------------------------------ // Constructors // ------------------------------------------------------------ public ObjectSource() { } /** * Single parameter constructor that specifies the unique id of the node. * * @param id */ ObjectSource(final int id, final RuleBasePartitionId partitionId, final boolean partitionsEnabled) { this( id, partitionId, partitionsEnabled, null, 3 ); } /** * Single parameter constructor that specifies the unique id of the node. * * @param id */ ObjectSource(final int id, final RuleBasePartitionId partitionId, final boolean partitionsEnabled, final ObjectSource objectSource, final int alphaNodeHashingThreshold) { super( id, partitionId, partitionsEnabled ); this.source = objectSource; this.alphaNodeHashingThreshold = alphaNodeHashingThreshold; this.sink = EmptyObjectSinkAdapter.getInstance(); } // ------------------------------------------------------------ // Instance methods // ------------------------------------------------------------ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal( in ); sink = (ObjectSinkPropagator) in.readObject(); source = (ObjectSource) in.readObject(); alphaNodeHashingThreshold = in.readInt(); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal( out ); out.writeObject( sink ); out.writeObject( source ); out.writeInt( alphaNodeHashingThreshold ); } /** * Adds the <code>ObjectSink</code> so that it may receive * <code>FactHandleImpl</code> propagated from this * <code>ObjectSource</code>. * * @param objectSink * The <code>ObjectSink</code> to receive propagated * <code>FactHandleImpl</code>. */ public void addObjectSink(final ObjectSink objectSink) { if ( this.sink instanceof EmptyObjectSinkAdapter ) { if( this.partitionsEnabled && ! this.getPartitionId().equals( objectSink.getPartitionId() ) ) { // if partitions are enabled and the next node belongs to a different partition, // we need to use the asynchronous propagator this.sink = new AsyncSingleObjectSinkAdapter( this.getPartitionId(), objectSink ); } else { // otherwise, we use the lighter synchronous propagator this.sink = new SingleObjectSinkAdapter( this.getPartitionId(), objectSink ); } } else if ( this.sink instanceof SingleObjectSinkAdapter ) { final CompositeObjectSinkAdapter sinkAdapter; if( this.partitionsEnabled ) { // a composite propagator may propagate to both nodes in the same partition // as well as in a different partition, so, if partitions are enabled, we // must use the asynchronous version sinkAdapter = new AsyncCompositeObjectSinkAdapter( this.getPartitionId(), this.alphaNodeHashingThreshold ); } else { // if partitions are disabled, then it is safe to use the lighter synchronous propagator sinkAdapter = new CompositeObjectSinkAdapter( this.getPartitionId(), this.alphaNodeHashingThreshold ); } sinkAdapter.addObjectSink( this.sink.getSinks()[0] ); sinkAdapter.addObjectSink( objectSink ); this.sink = sinkAdapter; } else { ((CompositeObjectSinkAdapter) this.sink).addObjectSink( objectSink ); } } /** * Removes the <code>ObjectSink</code> * * @param objectSink * The <code>ObjectSink</code> to remove */ protected void removeObjectSink(final ObjectSink objectSink) { if ( this.sink instanceof EmptyObjectSinkAdapter ) { throw new IllegalArgumentException( "Cannot remove a sink, when the list of sinks is null" ); } if ( this.sink instanceof SingleObjectSinkAdapter ) { this.sink = EmptyObjectSinkAdapter.getInstance(); } else { final CompositeObjectSinkAdapter sinkAdapter = (CompositeObjectSinkAdapter) this.sink; sinkAdapter.removeObjectSink( objectSink ); if ( sinkAdapter.size() == 1 ) { if( this.partitionsEnabled && ! this.getPartitionId().equals( sinkAdapter.getSinks()[0].getPartitionId() ) ) { // if partitions are enabled and the next node belongs to a different partition, // we need to use the asynchronous propagator this.sink = new AsyncSingleObjectSinkAdapter( this.getPartitionId(), sinkAdapter.getSinks()[0] ); } else { // otherwise, we use the lighter synchronous propagator this.sink = new SingleObjectSinkAdapter( this.getPartitionId(), sinkAdapter.getSinks()[0] ); } } } } public abstract void updateSink(ObjectSink sink, PropagationContext context, InternalWorkingMemory workingMemory); public void networkUpdated() { this.source.networkUpdated(); } public ObjectSinkPropagator getSinkPropagator() { return this.sink; } public boolean isInUse() { return this.sink.size() > 0; } protected void doRemove(final RuleRemovalContext context, final ReteooBuilder builder, final BaseNode node, final InternalWorkingMemory[] workingMemories) { if ( !node.isInUse() ) { removeObjectSink( (ObjectSink) node ); } if ( !this.isInUse() && this instanceof NodeMemory ) { for( InternalWorkingMemory workingMemory : workingMemories ) { workingMemory.clearNodeMemory( (NodeMemory) this ); } } this.source.remove( context, builder, this, workingMemories ); } }