Package com.barchart.feed.api.series.network

Source Code of com.barchart.feed.api.series.network.Node

package com.barchart.feed.api.series.network;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import rx.Observer;
import rx.subscriptions.Subscriptions;

import com.barchart.feed.api.series.DataSeries;
import com.barchart.feed.api.series.Span;

/**
* Base class which participates in an interconnected graph of executable objects,
* which expect a number of pre-configured inputs and deliver one or more pre-configured
* outputs.
*
* @author David Ray
*/
public abstract class Node<S extends Subscription> implements Runnable {
    /** Set of Observers to be notified upon finished processing */
    protected Set<Observer<NetworkNotification>> observers = Collections.synchronizedSet(new HashSet<Observer<NetworkNotification>>());

    /**
     * Set of dependent {@link Observer}s registered to child nodes of this node. This is used for unsubscribe functionality to track
     * when there are no more listeners subscribed to dependent nodes so that we can reclaim/expire resources.
     */
    protected Set<Observer<NetworkNotification>> dependents = Collections.synchronizedSet(new HashSet<Observer<NetworkNotification>>());

  /** List of {@code Node}s which are updated when this {@code Node} has finished processing. */
  protected ConcurrentLinkedQueue<Node<S>> childNodes;

  /** Parent path */
  protected List<Node<S>> parentNodes;

  /** Thread monitor object */
  private final Object waitLock = new Object();

  /** Flag to control startup and shutdown barriers */
  private boolean isRunning;

  /** Flag to indicated that a valid date range has been updated among source {@link DataSeries} */
  private boolean isUpdated;



  /**
   * Constructs a new {@code Node}
   */
  public Node() {
    childNodes = new ConcurrentLinkedQueue<Node<S>>();
    parentNodes = new ArrayList<Node<S>>();
  }

  /**
   * Starts this {@code Node}'s processing.
   */
  public void startUp() {
    if(!isRunning) {
      this.isRunning = true;
      (new Thread(this, this.toString())).start();
    }

    for(Node<S> n : parentNodes) {
        n.startUp();
    }
  }

  /**
   * Shuts down this {@code Node}.
   */
  public void shutDown() {
      this.isRunning = false;
      try {
            synchronized(waitLock) {
                waitLock.notify();
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
  }

  /**
   * Returns a flag indicating whether this {@code Node}
   * is currently running or not.
   *
   * @return     true if so, false if not.
   */
  public boolean isRunning() {
      return isRunning;
  }

  /**
   * Called by ancestors of this {@code Node} in the graph to set
   * the {@link Span} of time modified by that {@code Node}'s
   * internal processing class.
   *
   * @param span              the {@link Span} of time processed.
   * @param ancestorOutputSubscriptions   the List of {@link Subscription}s the ancestor node has processed.
   * @return
   */
  public boolean setModifiedSpan(final Span span, final List<S>  ancestorOutputSubscriptions) {
      final List<S> inputSubscriptions = getInputSubscriptions();
    for(final S s : ancestorOutputSubscriptions) {
        if(inputSubscriptions.contains(s)) {
            updateModifiedSpan(span, s); break;
        }
    }

    try {
      synchronized(waitLock) {
        waitLock.notify();
      }
    }catch(Exception e) {
      e.printStackTrace();
    }

    return true;
  }

  /**
   * Allows the implementing class to add the specified child node which involves connecting the
   * output specified by the {@link Subscription} to the specified output via input/output keys.
   *
   * @param node
   * @param subscription
   */
  public void addChildNode(Node<S> node) {
    childNodes.add(node);
  }

  /**
   * Removes the specified {@link Node} from the list of this {@link Node}'s children.
   *
   * @param node  the {@link Node} to remove.
   */
  public void removeChildNode(Node<S> node) {
    childNodes.remove(node);
  }

  /**
   * Returns a {@link List} view of the child {@link Node}s of this {@code Node}.
   * <em>Note:</em> Modifications to the returned list have no effect on the internal
   * structure of this {@link Node}.
   *
   * @return    a {@link List} view of the child {@link Node}s of this {@code Node}.
   */
  public List<Node<S>> getChildNodes() {
    return new ArrayList<Node<S>>(childNodes);
  }

  /**
     * Allows the implementing class to add the specified child node which involves connecting the
     * output specified by the {@link Subscription} to the specified output via input/output keys.
     *
     * @param node
     * @param subscription
     */
    public void addParentNode(Node<S> node) {
      synchronized(parentNodes) {
        parentNodes.add(node);
      }
    }

    /**
   * Removes the specified {@link Node} from the list of this {@link Node}'s children.
   *
   * @param node  the {@link Node} to remove.
   */
  public void removeParentNode(Node<S> node) {
    synchronized(parentNodes) {
      childNodes.remove(node);
    }
  }

    /**
     * Returns the List of parent {@code Node}s.
     * @return  the List of parent {@code Node}s.
     */
    public List<Node<S>> getParentNodes() {
      synchronized(parentNodes) {
        return new ArrayList<Node<S>>(parentNodes);
      }
    }

  /**
   * Called to set a flag indicating that there is data to process.
   *
   * @param isUpdated
   */
  public void setUpdated(boolean isUpdated) {
    this.isUpdated = isUpdated;
  }

  /**
   * Returns a flag indicated whether or not there is data to process.
   *
   * @return  a flag indicated whether or not there is data to process.
   */
  public boolean isUpdated() {
    return isUpdated;
  }

  /**
   * Returns the
   * @return
   */
  protected Object getLock() {
    return waitLock;
  }

  /**
     * Adds an {@link Observer} to be notified of ongoing updates.
     * @param obs      the observer to be notified.
     */
  public void addObserver(Observer<NetworkNotification> obs) {
        if(obs == null) {
            throw new IllegalArgumentException("Attempt to add a null observer.");
        }
        observers.add(obs);

        for(Node<S> n : parentNodes) {
            n.addDependent(obs);
        }
    }

    /**
     * Removes the specified Observer from notifications.
     * @param obs
     */
    public void removeObserver(Observer<NetworkNotification> obs) {
        if(obs == null) {
            throw new IllegalArgumentException("Attempt to add a null observer.");
        }
        observers.remove(obs);

        for(Node<S> n : parentNodes) {
            n.removeDependent(obs);
        }

        if(observers.isEmpty()) {
            shutDown();
        }
    }

    /**
     * Clears out all Observer references.
     */
    public void removeAllObservers() {
      for(Observer<NetworkNotification> o : observers) {
        for(Node<S> n : parentNodes) {
          n.removeDependent(o);
        }
      }

        observers.clear();
    }

    /**
     * Returns a flag indicating whether the specified {@link Observer} is
     * registered to receive notifications from this {@code AnalyticNode}
     *
     * @param obs      the {@link Observer} who's registration is being tested.
     * @return         true if so, false if not.
     */
    public boolean isObserver(Observer<NetworkNotification> obs) {
        return observers.contains(obs);
    }

    /**
     * Returns a flag indicating whether this {@code Node} has any registered
     * {@link Observer}s
     *
     * @return         true if so, false if not.
     */
    public boolean hasObservers() {
        return !observers.isEmpty();
    }

    /**
     * Adds a dependent {@link Observer} which is not directly registered for
     * notifications to this {@link Node}, but is registered to subsequent
     * child node(s), but whos processing depends on this node to supply it
     * with data.
     *
     * @param obs      the observer to be notified.
     */
  public void addDependent(Observer<NetworkNotification> obs) {
        if(obs == null) {
            throw new IllegalArgumentException("Attempt to add a null dependent.");
        }
        dependents.add(obs);

        for(Node<S> n : parentNodes) {
            n.addDependent(obs);
        }
    }

    /**
     * Removes the specified dependent Observer from this node's tracking.
     * @param obs
     */
    public void removeDependent(Observer<NetworkNotification> obs) {
        if(obs == null) {
            throw new IllegalArgumentException("Attempt to add a null observer.");
        }
        dependents.remove(obs);

        for(Node<S> n : parentNodes) {
            n.removeDependent(obs);
        }

        if(dependents.isEmpty()) {
            shutDown();
        }
    }

    /**
     * Clears out all references to this node's child node dependents.
     */
    public void removeAllDependents() {
      for(Observer<NetworkNotification> o : dependents) {
        for(Node<S> n : parentNodes) {
          n.removeDependent(o);
        }
      }
        dependents.clear();
        shutDown();
    }

    /**
     * Returns a flag indicating whether the specified {@link Observer} is
     * registered to a dependent child {@link Node} of this Node.
     *
     * @param obs      the {@link Observer} who's dependence is being tested.
     * @return         true if so, false if not.
     */
    public boolean isDependent(Observer<NetworkNotification> obs) {
        return dependents.contains(obs);
    }

    /**
     * Returns a flag indicating whether this {@link Node} has child nodes
     * which have registered observers.
     *
     * @return         true if so, false if not.
     */
    public boolean hasDependents() {
        return !dependents.isEmpty();
    }


  ///////////////////////////////////////////////////
  //    ABSTRACT METHODS TO BE IMPLEMENTED BELOW   //
  ///////////////////////////////////////////////////
  /**
   * Returns this {@code Node}'s name.
   * @return this Node's name.
   */
  public abstract String getName();
  /**
   * Returns a flag indicating whether this {@link Node} has an output which the specified
   * {@link Subscription} information can be derived from.
   *
   * @param  subscription  the Subscription which may or may not be derivable from one of
   *               this Node's outputs.
   * @return   true if so, false if not.
   */
  public abstract boolean isDerivableSource(S subscription);
  /**
   * Implemented by the node type handling data expected by this {@code Node}
   *
   * Collects expected input from ancestor nodes and starts this {@code Node}'s
   * processing if all the expected inputs are present and in good form.
   *
   * @param span        the {@link Span} processed.
   * @param subscription    the Subscription describing and identifying the specified input.
   */
  protected abstract <T extends Span> void updateModifiedSpan(T span, S subscription);
  /**
   * Implemented by the node type handling data expected by this {@code Node}
   *
   * Returns a flag indicating whether the implementing class has all of its expected input.
   *
   * @return  a flag indicating whether the implementing class has all of its expected input.
   */
  protected abstract boolean hasAllAncestorUpdates();
  /**
   * Implemented by the node type handling data expected by this {@code Node}
   *
   * Starts the processing of the previously set {@link Span}
   * @param span TODO
   * @return  the updated Span
   */
  protected abstract Span process();
  /**
   * Implemented by the node type handling data expected by this {@code Node}
   *
   * Returns a List of {@link Subscriptions} that the underlying Node supplies as output.
   *
   * @return  a List of output {@link Subscriptions}
   */
  public abstract List<S> getOutputSubscriptions();
  /**
   * Implemented by the node type handling data expected by this {@code Node}
   *
   * Returns a List of {@link Subscriptions} that the underlying Node expects as input.
   *
   * @return  a List of expected input {@link Subscriptions}
   */
  public abstract List<S> getInputSubscriptions();
  /**
   * Returns the  {@link Subscription} from among the many possible node outputs this node may have -
   * from which the specified Subscription is derivable.
   *
   * @param subscription     the Subscription which can be derived from one of this {@code Node}'s outputs.
   * @return                 One of this Node's derivable outputs or null.
   */
  public abstract S getDerivableOutputSubscription(S subscription);


  /**
   * Main {@link Thread} body
   */
  @Override
  public void run() {
    while(isRunning) {
      if(isUpdated()) {
        System.out.println(this + " isUpdated");
        setUpdated(false);
        if(hasAllAncestorUpdates()) {
          System.out.println(this + " hasAllAncestorUpdates()");
          Span span = this.process();
          if(span != null) {
            List<S> outputs = getOutputSubscriptions();
            for(Node<S> nextNode : childNodes) {
              nextNode.setModifiedSpan(span, outputs);
            }
          }
        }
      }

      if(!isUpdated()) {
        try {
          synchronized(waitLock) {
            System.out.println(this + " waiting..." + (getOutputSubscriptions().size() < 1 ? "" : getOutputSubscriptions().get(0)));
            if(isRunning) {
                waitLock.wait();
            }
            System.out.println(this + " waking up " + (getOutputSubscriptions().size() < 1 ? "" : getOutputSubscriptions().get(0)));
          }
        }catch(Exception e) {
          e.printStackTrace();
        }
      }
    }

    System.out.println(this + " shutting down...");
  }

}
TOP

Related Classes of com.barchart.feed.api.series.network.Node

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.