Bond Messaging Component
Well present the components of the communication system, by applying them in a
small application. The starting code for this is as follows:
/---------cut here ----------------
import bond.core.*;
import bond.core.util.*;
public class myStarter extends bondExecutable{
public myStarter(){
System.out.println("myStarter:myStarter():\n"+toString(this));
}
public static void main(String argv[]){
bondConfiguration.initSysProperties();
dir= new bondDirectory();
loader = new bondLoader();
com = new bondCommunicator();
conf = new bondConfiguration();
new myStarter();
}
}
/ ---------- end cut here -----------
The communicator - what it does
There are two important components that make the communication
possible: the Communicator and the Message objects
The bondCommunicator object has a unique instance within the
bondResident. It is the module that makes the communication between any
components of the system possible. The scenario for this is the following (see also the basic
messaging sample provided
here) : The sender object creates the
message (object) it wants to send, and also it creates a shadow
of the destination object. On the shadow it calls a communication method (ask(), say(), etc.). These
will call the communicator's send method having the message and the shadow as parameters . The
communicator can either subscribe/unsubscribe the shadow into the
event slots (which enables the dispatching of incomming message to the recipients interested in
receiving it) or will ask the communication engine (UDP, musticast, etc) to deliver the
message to the destination.
At the destination, the CommunicationEngine will distribute the message to the
destination by calling the distribute(message,destObject) which will attempt to
broadcast the message if the destination is unknown, to notify the methods that
were waiting in the reply slots or event slots, or in the end assign a thread
from the communication engine's thread pool to deliver the message to the
destination object. This last step is done by calling the say() method of the
destination object.
In the end, the destination object will receive the message (in the form of
having called its method say() with the message and sender objetcs as
parameters). Upon this, the receiver will try to detect what kind of subprotocol
is appropritate to decode/handle the message (by looking at the subprotocol field in the message) and subsequently it will try to load that
subprotocol. Failing to load the appropriate subprotocol, it will load the
PropertyAccess subprotocol. Note that for each of this subprotocols, the
receiving object will try to load the cooresponding probe that implements the specific subprotocol
For addressing purposes, the communicator has an ID (usually the name of
the machine hosting the resident and the port on which the resident is running).
The communicator handles the task of transferring the message objects from
source to destination. Keep in mind that the source and destination, in our case
are bond objects.
Partially this process is illustrated in the sample
here, being restricted by the
limited posibility of printing aout all the visited methods (unless we want to edit the source
ode of bond environment).Also note that the initialization is done by hand, to ilustrate what
components are required for the communication between two objects to take place. In the
subsequent examples, we will let the initbond()
to do all the necessary steps.
Regarding the messages, there is a more detailed presentation below.
Communicator startup:
Before it starts, the communicator
needs the bondConfiguration to have loaded the information form the property
file $BONDHOME/bond/properties. As seen in the above sample code, it also needs
the loader component, that will facilitate the loading of the communication
protocols. To see that these are needed, go ahead and comment out the two lines
containing bondConfiguration.initSysProperties(), conf = new
bondConfiguration();
and loader = new bondLoader()
. You'll
see that the application will fail to start (because of the communication
component). Looking at the internals of the communicator, this does the
following things at its own inilialization:
- if specified in the properties file, it will load a multicast communication engine
- depending on the setting from the properties file for
bond.communicationengine (default UDP), it will load the appropriate
communication module from the directory
$BONDHOME/core/communicationengine/. This way, the environment can load
different communication engines, depending on the user's choice. Here you can
see a very good example of why we needed the bondLoader and how one can use its
facilities.(for instance you can replace the communication engines at will).
- after it loaded successfully, it will initialize the communicator's
messaging threads and start the listening on the specified port (default 2000)
For successful communication, you'll need to add the initMessage() method, to enable the
decoding of KQML formatted messages, used by default by bond.(see below)
Messages NEEDS REVIEWING !!!
Bond uses messages to achieve communication between all the components of the
system. These are objects that have attached as dynamic
properties name-value pairs. This message object is passed between local
objects, and they interpret the messsages by reading the dynamic properties of
the message object.
Since the objects do not have the same meaning when are
sent form one resident to another, it arose the need for an external messaging
format. For this purpose Bond has chosen to implement KQML and XML. There is a one-to-one mapping of the
bondMessage objects to the external message format
While we ar at it, you
should know that messages can have any number of name-value pairs (as pieces of
inforrmation that we are transmitting) but there are certain reserved names for
variables: - addressing variables
sender
,
receiver
(source, destination, location of objects) - message
identifyers (when needed by matching an answer (
in-reply-to
) with
the question (reply-with
) - semantic identifyers. Identify the
context of the message:
subprotocol, performative, content, language,
ontology
. We'll cover these later with some examples. - hidden
variables: for
piggyback
ing purposes.
. The code at the top needs now more additions, so in order to
ilustrate the communication between two objects, we have created a new example
to start playing with here It contains two
bondObjects one with the role of a client, the other one with the role of a
server. The client will send a message to the server, and this one will only
notice that it has received the message. We'll continue building on this.
Before we can communicate with somebody else, we need to create some messages to
send. The messages are bondMessage objects and have the following structure:
- content - the string containing the message's content. TODO : EXAMPLE:
- performative - is an integer value, encoded as seen here
- hPF, hPFback -
hashtables mapping all the performatives from
KQML to Integers and backwards. The mapping can be also seen here
- suspendedThread - the thread that handles this message.
Communicator: internals and functionality
Internally, the communicator has a hash structure, reply_slots, which is used to
store the messages that have been sent and that wait a reply. On the receiving
of the remote reply, the sent message is removed from the reply_slot, and the
object that has sent the initial message is notified. This process implements an
asynchronous messaging system
The messages contain the
ask
performative with a reply-with field. If the incoming
message contains a waiting slot, the message is delivered directly to the object
that has sent the request. Otherwise, the say() method is used to send the
reply.
The communicator contains a thread pool which has the job to deliver
messages to the objects that are destined for.
The communicator also provide
the framework for synchronous communication. It is implemented
by the ask and on_reply() methods from the bondObject.
An example of this mechanism can be seen in the example
here
The third way for
bondObjects to interact is to use the subscribe-notify model of
event handling. It is an extension of the java listener abstraction.
The object that has subscribed to a certain event on one or several properties
of an object is called monitor. Before a bondObject becomes monitor, it needs to
register itself with the monitored object, in a special hashtable, event_slots
(available at the Communicator), and whenever a property of the monitored object
changes, it will send a message with the tell
performative to all
the elements registered for that specific message. The registering part is being
done on the listeners field of the monitored object, by calling
subscribeAsListener() and unsubscribeListener()
TODO: show the stackTrace
of this process.
CommunicationEngines
Are in charge of transporting the messages from one resident to another. Bond
has several agents, based on different communication protocols:
bondTCPCommunicationEngine, bondUDPCommunicationEngine,
bondInfospheresCommunicationEngine and bondMPCommunicationEngine (IP
multicast).All communication engines implement the common
bondCommunicationEngine interface, which provides the common set of methods:
Each of these have two methods for sending (either messages, or objects)
and one for receiving messages. They are all based on some thread pool, that
assigns each message to be send a thread, this way achieveing better
performance. The thread pool is initialized when the specific communication
engine is started.
Subprotocols
The bond
messages are divided into small sets, called subprtocols. Examples of
subprotocols are: PropertyAccess, Security,Monitoring,Agent Control, Scheduling,
Persistent Storage, DataStaging, Registration. By default, all bond Objects
implement PropertyAccess subprotocol, which allows to interogate and set the
properties of some other object. The subprotocols are inherited by the object's
class hierarchy, and failing to understand a subprotocol, the message is passed
to ancestors. In case nobody understands the message, a sorry
is
returned.
Subprotocols can be learned dynamically by the objects, by
attaching them so-called probes objects. Commonly used probes are the
bondMonitoringProbe (see subscribe-notify model) and bondSecurityProbe.
To make the communication more versatile, you can define your own subprotocol (the sequence and the
format of messages), and implement your own probe that will handle the messages belonging to this
new subprotocol.To see how, look at
this application that uses
this custom-made probe.
To use a custom-made probe, I have built on the usual client-server skeleton. The things to
notice are the addition of the new probe object as a dynamic property to the server object, and the
genaration of a message that is writen in this new subprotocol. In our case the new subprotocol is
called testSubprotocol and the message that is sent conforms to this subprotocol.:
msg=new bondMessage("(tell :content ok)","testSubprotocol");
On the server side, as noted above, all you need to do is add the probe object to the server:
set("randomly chosen name",new myProbe(this));
Miscellaneous
Also part of the communication infrastructure, the Virtual network of Objects is a means of addressing groups of related bondObjects. This group objects will receive messages using the multicast engine.
Object mobility Is a means of moving the objects across the network, from one resident to anoter. It uses the method realize() called on a shadow object. One problem that might arise because of this is the inconsistencies of the existing copies, when they are subject to changes.
Distributed Awareness Is a mechanism used by the residents to find about each other's existence. It is based on collecting information about other residents from received messages, and distributing this knowledge by means of piggybacking it to messages sent to objects at remote residents.