Distributed Messenger

using AmbientJS

In this tutorial we build a cross-platform mobile application using the AmbientJS middleware. This mobile application is a distributed messenger application. After providing a username, the messenger application connects to an available ad-hoc network and will discover other clients connected to the same network. After discovery, all users can send messages to each other over the network.

When using the AmbientJS middleware to build distributed, cross-platform mobile applications, the middleware first has to be loaded into the JavaScript source file.

var AmbientJS = require('js/AmbientJS/AmbientJS');

After making the AmbientJS accessible in the JavaScript source file, all functionality of AmbientJS can now be used. When starting a distributed messenger application, the mobile app first has to connect to an available ad-hoc network. One of the features of the AmbientJS middleware is that it supports zero configuration networking. This enables developers to connect the mobile application to the wireless ad-hoc network without any configuration. To make the AmbientJS middleware connect to the available wireless ad-hoc network, the developer only has to put the AmbientJS in Online-modus.

AmbientJS.online();

After making the AmbientJS middleware connect to the network, developers have to define an interface for the mobile application. This interface can be distributed over the network and automatically discovered by others mobile applications. The methods of this interface then can be called remotely by the other mobile applications. To implement such a remote interface, the createRemoteInterface method of AmbientJS has to be called. This method takes a collection of methods that together form the remote interface of that mobile application. For this distributed messenger application, the methods getName and receiveMsg are provided, which return the name of the user and print a received message on the screen respectively.

After defining the remote interface, this interface can be distributed over the network by using the exportAs method of AmbientJS. This method takes both the interface to be distributed and a typetag (a JavaScript string) with which the interface has to be distributed as arguments.

var remoteInterface = AmbientJS.createRemoteInterface({
    "getName" : function () {
        return myName;},
    "receiveMsg" : function (msg) {
        addToLog(msg);}
});

AmbientJS.exprotAs(remoteInterface, "MESSENGER");

Now that we have made the mobile application discoverable over the network through this interface, the mobile application also has to be able to discover the interfaces of other mobile applications over the network. For this a wheneverDiscovered callback has to be provided and the same typetag as with which remote interfaces are exported. The callback is called whenever a remote interface of another mobile application over the network has been discovered. The AmbientJS middleware also provides access to this remote interface through a reference. This reference (accessible in the callback) points to the remote interface and can be used to send messages to that interface. By sending messages, developers are able to remotely call the methods of these interface, which for this messenger application are the getName and receiveMsg methods.

AmbientJS.wheneverDiscovered("MESSENGER", function(reference) {
    var msg = AmbientJS.createMessage("getName", []);

    var future = reference.asyncSend(msg, "twoway");
    future.whenBecomes(function(reply) {
        buddyList[reply] = reference;
        addToLog(reply + " has joined the conversation.");
    });
});

Immediately upon discovering a remote messenger application, the username with which the messenger application has logged in is requested. For this, a message is constructed which will be send to the reference of the remote messenger application by calling the createMessage method (see line 2 in the code snippet below). This message includes the name of the method to be remotely called, which in this case is the getName method and the parameter list for the method call, which in this case is empty.

This newly created message now has to be sent asynchronously to the reference by using the asyncSend method of the reference (see line 3 in the code snippet above). This method takes the message to be sent to the reference and also an option that indicates if a response is expected from the remotely invoke method.

A programmer can send a message to a remote reference by using the oneway option, in case he does not want a return value from the remotely invoked method. However, in most cases, the programmer wants the returned value from the remotely invoked method. For this the message has to be sent with the twoway option. In this example, the message remotely calls the getName method and thus we want to use the return value, which will be the username of the discovered messenger application.

When a return value is expected from a message-send, the mobile application will not wait until the value has been returned. Instead, futures are used that will eventually hold that return value and on which callback can be installed. In this example, the future (fut) will hold the requested name. To install a callback on this future that will be called whenever the future actually holds the return value, the whenBecomes method of that future is called. For this messenger application we first store the reference pointing to the remote messenger application in the buddyList, which is a JavaScript dictionary, with the received name. Secondly, we also call the addToLog function in the callback that prints the returned name on the screen.

This functionality concludes the initialization process of the messenger application. Now in order to send messages to the already connected instances, the programmer can go through the buddyList dictionary and send the message that remotely invokes the receiveMsg method. This functionality is described in the code snippet below.

function broadcast(text) {
    var msg = AmbientJS.createMessage('receiveMsg', [myName + ": " + text]);
    for(var i=0; i<buddyList.length; i++) {
        AmbientJS.asyncSend(buddyList[i], msg);
    };
    addToLog(myName + ": " + text);
}

Note that during this tutorial the source code for the graphical user interface was not described. To implement a graphical user interface in JavaScript for Titanium, we refer to the official documentation.

The source code of this tutorial application can be downloaded here.

A demo video of this distributed messenger application can be watched below.