Class Node

java.lang.Object
dslabs.framework.Node
All Implemented Interfaces:
Serializable

public abstract class Node extends Object implements Serializable
Nodes are the basic unit of computation. They can send and receive Messages, set and handle Timers, and modify private data. These handlers (as well init()) are invoked sequentially, and they should deterministically run to completion without blocking, sleeping, or starting other threads.

Nodes need not handle concurrent access, except for Clients. Nodes should not use any other means to communicate (e.g., communication through static variables is forbidden). Subclasses of Node define Message handlers by creating methods with the correct name and method signature. For instance, to define a message handler for Foo extends Message, a Node would define the method handleFoo(Foo message, Address sender). Similarly, to define a handler for Bar extends Timer a Node would define the method onBar(Bar timer).

After creation (but before any Message or Timer handlers are invoked), the init() method will be invoked. Nodes should not send any messages or set any timers in their constructor. Instead, they should send any necessary messages during initialization.

Nodes can add sub-Nodes, which allow code re-use. When a Node is registered as a sub-Node, it can send messages and set timers as normal. However, messages can also be passed reliably and immediately between the sub-Node and its parent using handleMessage(Message, Address). The parent node registering the sub-Node is responsible for creating the sub-Node with a sub-Address of its own address (Address.subAddress(Address, String)), registering the sub-Node addSubNode(Node), and then initializing the sub-Node by calling its init() method after the parent node has been initialized. Nodes do not need to keep references to their sub-Nodes or parent nodes. Instead, they should keep only the address of the other and communicate through message-passing. The following is an example of how to create a sub-Node:


 public void init() {
     Address subNodeAddress = Address.subAddress(address(), "foo");
     SubNode subNode = new SubNode(subNodeAddress, address());
     addSubNode(subNode);
     subNode.init();
 }
 

The parent node keeps a reference to subNodeAddress, and the sub-Node stores a reference to the parent's address in its constructor, allowing them to communicate.

Subclasses of Node must properly implement Object.equals(Object), Object.hashCode(), and Object.toString(). Subclasses of Node must call this class's implementations of those methods (since sub-Nodes and their state are stored in this class). All data structures held in Nodes must properly implement Object.equals(Object), Object.hashCode(), and Object.toString(). Furthermore, these data structures must also implement Serializable.

To facilitate the copy-on-write style cloning in search tests, subclasses of Node should not use locking or synchronized data structures (e.g., Hashtable), the one exception being the synchronization required in Client methods. Doing so has the potential to cause deadlock in those tests.

See Also:
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    protected
    Node(@NonNull Address address)
    Constructor for a Node which all subclasses must call.
  • Method Summary

    Modifier and Type
    Method
    Description
    final Address
    The address of this Node.
    protected final void
    addSubNode(@NonNull Node subNode)
    Adds a sub-Node to this Node's hierarchy.
    protected void
    broadcast(Message message, Address[] to)
    Sends a message to all Nodes in the array.
    protected void
    Sends a message to all Nodes in the collection.
    protected final Object
    Can be used to handle a message sent by a Node to itself locally (rather than sending the message over the network).
    protected final Object
    handleMessage(Message message, Address destination)
    Can be used to send messages between two nodes within the same root node (e.g., between parent Node and sub-Node).
    abstract void
    Takes any initialization steps necessary (potentially sending Messages and setting Timers).
    protected final void
    onTimer(Timer timer)
    Can be used to invoke a timer handler on a Node, rather than setting the timer and waiting for it to expire.
    protected void
    send(Message message, Address to)
    Send a message to a Node with the given Address.
    protected void
    set(Timer timer, int timerLengthMillis)
    Sets a Timer to be tracked by the environment.
    protected void
    set(Timer timer, int minTimerLengthMillis, int maxTimerLengthMillis)
    Sets a Timer to be tracked by the environment.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • Node

      protected Node(@NonNull @NonNull Address address)
      Constructor for a Node which all subclasses must call.
      Parameters:
      address - the address of the Node
  • Method Details

    • init

      public abstract void init()
      Takes any initialization steps necessary (potentially sending Messages and setting Timers).
    • addSubNode

      protected final void addSubNode(@NonNull @NonNull Node subNode)
      Adds a sub-Node to this Node's hierarchy. The address of the sub-Node must be a sub-Address of this Node's Address. Does not automatically initialize the sub-Node.
      Parameters:
      subNode - the sub-Node to add
    • address

      public final Address address()
      The address of this Node.
      Returns:
      the address
    • send

      protected void send(Message message, Address to)
      Send a message to a Node with the given Address. The message will be cloned or serialized immediately as it is sent; there is no need to deep copy data structures when creating messages.
      Parameters:
      message - the message to send
      to - the destination address
    • broadcast

      protected void broadcast(Message message, Address[] to)
      Sends a message to all Nodes in the array.
      Parameters:
      message - the message to send
      to - the destination addresses
    • broadcast

      protected void broadcast(Message message, Collection<Address> to)
      Sends a message to all Nodes in the collection.
      Parameters:
      message - the message to send
      to - the destination addresses
    • set

      protected void set(Timer timer, int timerLengthMillis)
      Sets a Timer to be tracked by the environment. The Timer will be re-delivered to the setting Node after timerLengthMillis milliseconds. Timers may be cloned by the testing infrastructure before being re-delivered.
      Parameters:
      timer - the timer to set
      timerLengthMillis - the timer duration
    • set

      protected void set(Timer timer, int minTimerLengthMillis, int maxTimerLengthMillis)
      Sets a Timer to be tracked by the environment. The Timer will be re-delivered to the setting Node between minTimerLengthMillis and maxTimerLengthMillis, inclusive, chosen uniformly at random. Timers may be cloned by the testing infrastructure before being re-delivered.
      Parameters:
      timer - the timer to set
      minTimerLengthMillis - the minimum timer duration
      maxTimerLengthMillis - the maximum timer duration
    • handleMessage

      protected final Object handleMessage(Message message, Address destination)
      Can be used to send messages between two nodes within the same root node (e.g., between parent Node and sub-Node). The message is handled immediately. If the handler is successfully executed and returns a value, that value is returned. Otherwise, this method returns null.

      The message and the return value are not cloned or modified in any way; note that this behavior differs from send(Message, Address), which clones or serializes messages immediately. If the caller wants to mirror the behavior of send(Message, Address), the recommended method is to implement Cloneable and Object.clone(), call Object.clone() on the message, and pass the cloned result to this method. Alternatively, SerializationUtils.clone(Serializable) can be used to clone objects without implementing Object.clone(), but it is much slower.

      Parameters:
      message - the message to deliver
      destination - the Node to deliver to
      Returns:
      the value returned by the handler or null
    • handleMessage

      protected final Object handleMessage(Message message)
      Can be used to handle a message sent by a Node to itself locally (rather than sending the message over the network). The message is handled immediately. If the handler is successfully executed and returns a value, that value is returned. Otherwise, this method returns null.

      The message and the return value are not cloned or modified in any way; note that this behavior differs from send(Message, Address), which clones or serializes messages immediately. If the caller wants to mirror the behavior of send(Message, Address), the recommended method is to implement Cloneable and Object.clone(), call Object.clone() on the message, and pass the cloned result to this method. Alternatively, SerializationUtils.clone(Serializable) can be used to clone objects without implementing Object.clone(), but it is much slower.

      Parameters:
      message - the message to deliver
      Returns:
      the value returned by the handler or null
    • onTimer

      protected final void onTimer(Timer timer)
      Can be used to invoke a timer handler on a Node, rather than setting the timer and waiting for it to expire. The timer handler is handled immediately.

      The timer is not cloned or modified in any way.

      Parameters:
      timer - the timer to deliver