Overview of JBotSim's main events

This example illustrates the following aspects:

The proposed scenario involves loner nodes who dislike the company of others (we will turn it into a social node in the next version, promise). These nodes are happy (green) when they are isolated and unhappy (red) otherwise. Different ways to implement this scenario are shown, to illustrate various computational paradigms in JBotSim: 1) Distributed algorithms vs centralized algorithms and 2) Message passing vs graph-level algorithms. Let us start with the message-passing, distributed version.

Message-passing, distributed version

Sending messages: Sending a message can be done by calling one of the parent methods send() or sendAll(). Two parameters are required for the send() method : the destination of the message (typically, another Node), and the message itself (of type Message). If the sendAll() primitive is used, the message will be delivered to all the neighbors of this node (at the beginning of the next round). The text of a message may be empty (like in the example below, just to say hello), or it can be any object of your choice. Beware, the object is passed by reference! In this example, the nodes say hello in each round, from within the onClock() method.

import jbotsim.Message;
import jbotsim.Node;

import java.awt.*;

public class LonerMessageBased extends Node {
    boolean mayBeAlone = true;
    @Override
    public void onClock(){
        setColor(mayBeAlone? Color.green:Color.red);
        sendAll(new Message());
        mayBeAlone = true;
    }
    @Override
    public void onMessage(Message msg) {
        mayBeAlone = false;
    }
}    

Receiving messages: There is essentially two ways to receive messages: event-based or round-based. The event-based option makes it possible to be notified directly when a message is received. This is done by overriding the onMessage() method, as in this example. The round-based approach (not used here) would consist in checking the node's mailbox explicitly in the onClock() method, through calling getMailbox(). Here, the algorithm is pretty simple: whenever a node receives (at least) one message, it gets mad for the current round.

By default, a message sent in round r is delivered at the beginning of round r+1. Precisely, JBotSim's default message engine guarantees that all messages will be delivered before any of the onClock() methods are called in the current round. Asynchronous messaging can be simulated using another message engine, but this is beyond the scope of this tutorial (have a look at JBotSim's extensions and the jbotsimx.messaging package for details). The main() method is similar to what you already know, that is, we create the topology, then we set the default type of node to be LonerMessageBased, then finally we create the viewer.

public static void main(String[] args) {
    Topology tp = new Topology();
    tp.setDefaultNodeModel(LonerMessageBased.class);
    new JViewer(tp);
}    

Graph-level, distributed version

While the above implementation relies on message passing, we now look at a high-level, graph-based implementation of the same scenario. At this level, nodes are directly notified of the appearance or disappearance of local links, which allows them to react instantly.

import jbotsim.Link;
import jbotsim.Node;

import java.awt.*;

public class LonerGraphBased extends Node{
    @Override
    public void onStart() {
        setColor(Color.green);
    }
    @Override
    public void onLinkAdded(Link link) {
        setColor(Color.red);
    }
    @Override
    public void onLinkRemoved(Link link) {
        if (!hasNeighbors())
            setColor(Color.green);
    }
}    

Method hasNeighbors() is also a graph-level method, which returns true iff this node has at least one adjacent link. Of course, this method and other graph-level methods such as getLinks() or getNeighbors() should not be used (or be used knowingly) in a message passing context, where connectivity is normally known only in retrospect.

Centralized version

We are now looking at the centralized paradigm, and as such, our program no more extends class Node. It is just a basic class as any other program.

Listening to connectivity: The appearance or disappearance of links can be listened to globally by means of the ConnectivityListener interface. This interface consists of two methods, onLinkAdded() and onLinkRemoved(), as before, except that these are called relative to all the links network wide. Registration to these events requires calling addConnectivityListener() on the topology (see below).

Listening to churn: In the same way as we did for link connectivity, we can listen to the addition or removal of nodes using the TopologyListener interface. This interface consists of two methods, onNodeAdded() and onNodeRemoved(), which are called by JBotSim whenever a new node is added or removed from the topology.

import jbotsim.Link;
import jbotsim.Node;
import jbotsim.Topology;
import jbotsim.event.ConnectivityListener;
import jbotsim.event.TopologyListener;

import java.awt.*;

public class LonerCentralized implements TopologyListener, 
                                         ConnectivityListener{
    public LonerCentralized(Topology tp){
        tp.addTopologyListener(this);
        tp.addConnectivityListener(this);
    }
    @Override // TopologyListener
    public void onNodeAdded(Node node) {
        node.setColor(node.hasNeighbors()?Color.red:Color.green);
    }
    @Override // TopologyListener
    public void onNodeRemoved(Node node) {
    }
    @Override // ConnectivityListener
    public void onLinkAdded(Link link) {
        for (Node node : link.endpoints())
            node.setColor(Color.red);
    }
    @Override // ConnectivityListener
    public void onLinkRemoved(Link link) {
        for (Node node : link.endpoints())
            if (!node.hasNeighbors())
                node.setColor(Color.green);
    }
}    

Here, the use of TopologyListener allows us to initialize the color of a node when it is added to the topology, which we keep updating afterwards as new links appear or disappear. The main method creates a topology, then initializes the algorithm with it and finally creates the viewer.

public static void main(String[] args) {
Topology();
    new LonerCentralized(tp);
    new JViewer(tp);
}