Class Node
- All Implemented Interfaces:
Serializable
Message
s, set and
handle Timer
s, 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 Client
s. 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 -
Method Summary
Modifier and TypeMethodDescriptionfinal Address
address()
The address of this Node.protected final void
addSubNode
(@NonNull Node subNode) Adds a sub-Node to this Node's hierarchy.protected void
Sends a message to all Nodes in the array.protected void
broadcast
(Message message, Collection<Address> to) Sends a message to all Nodes in the collection.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).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
init()
protected final void
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 a message to a Node with the givenAddress
.protected void
Sets aTimer
to be tracked by the environment.protected void
Sets aTimer
to be tracked by the environment.
-
Constructor Details
-
Node
Constructor for a Node which all subclasses must call.- Parameters:
address
- the address of the Node
-
-
Method Details
-
init
public abstract void init() -
addSubNode
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
The address of this Node.- Returns:
- the address
-
send
Send a message to a Node with the givenAddress
. 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 sendto
- the destination address
-
broadcast
Sends a message to all Nodes in the array.- Parameters:
message
- the message to sendto
- the destination addresses
-
broadcast
Sends a message to all Nodes in the collection.- Parameters:
message
- the message to sendto
- the destination addresses
-
set
Sets aTimer
to be tracked by the environment. The Timer will be re-delivered to the settingNode
after timerLengthMillis milliseconds. Timers may be cloned by the testing infrastructure before being re-delivered.- Parameters:
timer
- the timer to settimerLengthMillis
- the timer duration
-
set
Sets aTimer
to be tracked by the environment. The Timer will be re-delivered to the settingNode
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 setminTimerLengthMillis
- the minimum timer durationmaxTimerLengthMillis
- the maximum timer duration
-
handleMessage
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 ofsend(Message, Address)
, the recommended method is to implementCloneable
andObject.clone()
, callObject.clone()
on the message, and pass the cloned result to this method. Alternatively,SerializationUtils.clone(Serializable)
can be used to clone objects without implementingObject.clone()
, but it is much slower.- Parameters:
message
- the message to deliverdestination
- the Node to deliver to- Returns:
- the value returned by the handler or null
-
handleMessage
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 ofsend(Message, Address)
, the recommended method is to implementCloneable
andObject.clone()
, callObject.clone()
on the message, and pass the cloned result to this method. Alternatively,SerializationUtils.clone(Serializable)
can be used to clone objects without implementingObject.clone()
, but it is much slower.- Parameters:
message
- the message to deliver- Returns:
- the value returned by the handler or null
-
onTimer
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
-