public synchronized Address<? extends Node> getOrCreateNodeByRecipe(RecipeTraceInfo recipeTrace) {
   final ReteNodeRecipe recipe = recipeTrace.getRecipe();
   Address<? extends Node> result = getNodesByRecipe().get(recipe);
   if (result != null) {
     // NODE ALREADY CONSTRUCTED FOR RECIPE, only needs to add trace
     if (getRecipeTraces().add(recipeTrace)) result.getNodeCache().assignTraceInfo(recipeTrace);
   } else {
     // No node for this recipe object - but equivalent recipes still reusable
     Collection<ReteNodeRecipe> sameClassRecipes = getSameClassRecipes(recipe);
     for (ReteNodeRecipe knownRecipe : sameClassRecipes) {
       if (equivalentRecipes(recipe, knownRecipe)) {
         // FOUND EQUIVALENT RECIPE
         result = getNodesByRecipe().get(knownRecipe);
         getNodesByRecipe().put(recipe, result);
         result.getNodeCache().assignTraceInfo(recipeTrace);
         break;
       }
     }
     if (result == null) {
       // MUST INSTANTIATE NEW NODE FOR RECIPE
       final Node freshNode = instantiateNodeForRecipe(recipeTrace, recipe, sameClassRecipes);
       result = reteContainer.makeAddress(freshNode);
     }
   }
   return result;
 }
 /**
  * Internal message delivery method.
  *
  * @pre threads > 0
  */
 private void sendUpdate(
     Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
   ReteContainer affectedContainer = receiver.getContainer();
   synchronized (globalTerminationCriteria) {
     long newCriterion =
         affectedContainer.sendUpdateToLocalAddress(receiver, direction, updateElement);
     terminationCriterion(affectedContainer, newCriterion);
   }
 }
 /**
  * Sends multiple update messages atomically to the receiver node, indicating a newly found or
  * lost partial matching. The node may reside in any of the containers associated with this
  * network. To be called from a user thread during construction.
  *
  * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as
  *     the sequence may span through network calls, that's why it's not enforced here )
  * @return the value of the target container's clock at the time when the message was accepted
  *     into its message queue
  */
 public void sendConstructionUpdates(
     Address<? extends Receiver> receiver, Direction direction, Collection<Tuple> updateElements) {
   // structuralChangeLock.lock();
   if (threads > 0) sendUpdates(receiver, direction, updateElements);
   else
     receiver
         .getContainer()
         .sendUpdatesToLocalAddressSingleThreaded(receiver, direction, updateElements);
   // structuralChangeLock.unlock();
 }
 /**
  * Internal message delivery method.
  *
  * @pre threads > 0
  */
 private void sendUpdates(
     Address<? extends Receiver> receiver, Direction direction, Collection<Tuple> updateElements) {
   if (updateElements.isEmpty()) return;
   ReteContainer affectedContainer = receiver.getContainer();
   synchronized (globalTerminationCriteria) {
     long newCriterion =
         affectedContainer.sendUpdatesToLocalAddress(receiver, direction, updateElements);
     terminationCriterion(affectedContainer, newCriterion);
   }
 }
 /**
  * Severs connection between a supplier and a receiver node, regardless which container they are
  * in. Not to be called remotely, because this method enforces the structural lock.
  *
  * @param supplier
  * @param receiver
  * @param desynchronise indicates whether the current contents of the supplier should be
  *     subtracted from the receiver
  */
 public void disconnectRemoteNodes(
     Address<? extends Supplier> supplier,
     Address<? extends Receiver> receiver,
     boolean desynchronise) {
   try {
     if (threads > 0) structuralChangeLock.lock();
     receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise);
   } finally {
     if (threads > 0) structuralChangeLock.unlock();
   }
 }
 /**
  * Internal message delivery method for single-threaded operation
  *
  * @pre threads == 0
  */
 private void sendUpdateSingleThreaded(
     Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
   ReteContainer affectedContainer = receiver.getContainer();
   affectedContainer.sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement);
 }