Version 0.1.1
Programming Mobile Agents in JavaTM
- With the Java Aglet API
Danny B. Lange and Mitsuru Oshima © 1997

This is a preview of Chapter 5. The text and code fragments in this Chapter are for the upcoming Alpha 5 release of AWB. However, the referenced examples have been prepared for the current Alpha 4c release. 

Chapter 5

Aglet Messaging

An important property of aglets is that they can communicate with each other. Inter-aglet communication is supported by a rich framework in which aglets that do not necessarily "know" each other can interchange messages. That is, an aglet is often supposed to cooperate with  aglets developed in different organizations. The cooperating aglets in such a collection are not likely to be developed at the same time or even to be known to each other at compile time. This demands an interaction and communication model that is richer, more flexible, and more extensible than that used in normal method invocation.

The aglet supports an object-based messaging framework that is
In this chapter you will learn about the basics of aglet messaging. Several means of inter-aglet communication are supported, and you will learn about simple messaging with and without reply, advanced message management, and multicast messaging between aglets.

Simple Messaging

The principal way for aglets to communicate with each other is by message passing. Inter-aglet messaging is based on a simple callback scheme that requires an aglet to implement handlers only for the kinds of messages that it is supposed to understand. The message callback method in the Aglet class is called handleMessage. This is not a method that you call directly when to wish to send a message to an aglet. Instead, you invoke either the sendMessage or the sendAsyncMessage method on the proxy, which serves as a message gateway for the aglet. One of the benefits of using the proxy is that it provides you with a location-independent interface for sending messages to aglets. In other words, it really does not matter whether you are using a remote proxy (a proxy for a remote aglet) or a local proxy to send a message; the interface (sendMessage) is the same.

   

The sendMessage method of the proxy takes a message object as argument and sends the synchronous message to the aglet for which the proxy is acting as a gateway, and returns an object as a reply. On the other handl, the sendAyncMessage method sends the message to the aglet in a asynchrnous manner, and returns a FutureReply object that the sender of the message can use to retrieve a possible (future) reply.
public Object sendMessage(Message message) 
Sends a message object to the aglet, waits until the handling is completed and returns the reply from the aglet. 
public FutureReply sendAsyncMessage(Message message) 
Sends a message object to the aglet and returns a handle for a future reply from the aglet. 
Below, a simple message object of the "Hello" kind is created and send to the aglet. In this example we are not interested in the reply to the message we sent, and will therefore simply ignore the object returned by sendMessage():
Message msg = new Message("Hello");
someProxy.sendMessage(msg);
The handleMessage method of the Aglet class is one of the key callback methods that the aglet programmer is supposed to override. It enables the aglet to respond to messages sent to it. Typically, the implementation of this method will consist of a switch statement that tests for the kind of the incoming message. Notice that the handler is supposed to return a Boolean value; true if the message kind was understood and handled, and false if the message kind was neither understood nor handled.
public boolean handleMessage(Message message) 
Handles an incoming message object. 
This simple message handler accepts and handles messages of the "Hello" kind. To acknowledge that it has handled such a message, it returns the Boolean value true. It will reject all other kinds of message by returning the value false:
public boolean handleMessage(Message msg) {
   if ("Hello".equals(msg.kind)) {
      doHello();
      return true;   // Okay, I handled this message.
   } else
      return false;  // No, I do handle this kind of message.
}
Full source code: SimpleMessageExample.java and SimpleMessageChild.java.

The Message Class

Messages are objects. A message object is characterized by its kind. This string property is used to distinguish messages from each other. The Message class supports a range of constructors that all have kind as a mandatory argument. Message objects also contain an optional argument field for data associated with a particular message. The argument field can be either atomic (String, int, etc.) or tabular (Hashtable).

 

Message Creation

The many message constructors represent shortcuts for the initialization of the argument field.
public Message(String kind) 
    Constructs a message of the specified kind. Creates by default an argument hash table that can hold a set of key-value pairs. The setArg method can be used to initialize the argument hash table. 
public Message(String kind, Object arg) 
Constructs a message of the specified kind with an initial binding of the Object argument. 
public Message(String kind, int arg) 
Constructs a message of the specified kind and with an initial binding of the int argument. 
public Message(String kind, long arg) 
Constructs a message of the specified kind and with an initial binding of the long argument. 
public Message(String kind, float arg) 
Constructs a message of the specified kind and with an initial binding of the float argument. 
public Message(String kind, double arg) 
Constructs a message of the specified kind and with an initial binding of the double argument. 
public Message(String kind, char arg) 
Constructs a message of the specified kind and with an initial binding of the char argument. 
public Message(String kind, boolean arg) 
Constructs a message of the specified kind and with an initial binding of the boolean argument. 
We will now look at a few examples of typical message objects. The first shows the construction of a simple message of the " Hello" kind: The second example shows the creation of a "Greeting" message with the string "Happy Birthday" as argument:
Message msg = new Message("Greeting", "Happy Birthday");
The final example of a simple message object is:
Message msg = new Message("Height", 175);
The constructors initialize three public fields in the message object. The first field is kind, a String field that indicates the kind of the message and can be obtained by getKind() accessor method. The second field is arg, an Object field that hold the argument object of the message, and can be obtained by getArg() method. The final public field is timeStamp, a long field that holds a time stamp automatically set at the creation of the message, and can be obtained by getTimeStamp() method.
public String getKind() 
Indicates the kind of the message. 
public Object getArg() 
Holds the argument object of the message. 
public long getTimeStamp() 
Holds the time stamp of the message. 
There is the method, sameKind(String) which can be used to compare the given kind with the message. Say you have a message object named msg; then the kind can be compared by msg.sameKind("kind"), the argument and timestamp can be accessed by msg.getArg() and msg.getTimeStamp() respectively. As an example, let us take the message handler below. It implements a switch statement that enables it to recognize and handle three kinds of message, "Hello", "Greeting", and "Height":
public boolean handleMessage(Message msg) {
   if (msg.sameKind("Hello"))
      System.out.println("Hello")
   if (msg.sameKind("Greeting"))
      system.out.println("Greeting: " + msg.getArg());
   else if (msg.sameKind("Height")) {
      int height = ((Integer)msg.getArg()).intValue();
      system.out.println("Height: " + height);
   }
   return true;
}
Full source code: MessageExample.java and MessageChild.java.

The Message class has two methods for handling messages with non-atomic arguments. The reason is that messages often need to carry multiple arguments to the receivers. Such arguments are most effectively handled as key-value pairs. The setArg and getArg methods are convenient methods for organizing multiple arguments into a hash table.
public void setArg(String key, Object value)  
Maps the specified key to the specified value in the argument hash table. 
public Object getArg(String key) 
Gets the value to which the key is mapped in the argument hash table; this value is null if the key is not mapped to any value in the argument hash table. 
Say you want to send a "Location" message to an aglet. This message obviously requires two arguments (Horizontal and Vertical) to advertise the location. We use setArg() to create two key-value pairs in the message object:
Message msg = new Message("Location");
msg.setArg("Horizontal", new Integer(40));
msg.setArg("Vertical", new Integer(60));
The message handler can now retrieve the advertised location by means of the two keys:
public boolean handleMessage(Message msg) {
   if ("Location".equals(msg.kind)) {
      int h = ((Integer)msg.getArg("Horizontal")).intValue();
      int v = ((Integer)msg.getArg("Vertical")).intValue();
      System.out.println("Location: (" + h + "," + v + ")");
   }
}
Full source code: MessageHashExample.java and MessageHashChild.java.

Replying to Messages

The last group of methods in the Message class that will be described in this section is aimed at message handlers that want to reply to incoming messages. The message handler uses the incoming message object to deliver a reply to the message. The sendReply method of the message object is used to return a reply to the sender of the message.

  

There are several sendReply methods. The first of them does not take any arguments. It is used to send a reply without any specific value. The others all take one argument, namely the reply.
public void sendReply() 
Sends a reply without any specific value. 
public void sendReply(Object reply) 
Sends an object reply. 
public void sendReply(int reply) 
Sends a numeric reply. 
public void sendReply(long reply) 
Sends a numeric reply. 
public void sendReply(float reply) 
Sends a numeric reply. 
public void sendReply(double reply) 
Sends a numeric reply. 
public void sendReply(char reply) 
Sends a character reply. 
public void sendReply(boolean reply) 
Sends a Boolean reply. 
It is time for an example that demonstrates how a message handler actually responds to an incoming message. When the message handler below receives the "What is your height?" message, it is supposed to respond to that message with an integer reply. If its "height" is, say, 175, it can create a reply with the numeric value 175. The way that the (possibly remote) sender of the message will receive and process that reply will be covered in a few moments.
public boolean handleMessage(Message msg) {
   if ("What is your height?".equals(msg.kind)) {
      msg.sendReply(myHeight);
      return true;
   }
   return false;
}
What if the message handler fails while handling a message? So far we have treated message handling in a black-and-white manner: a message is either handled or it is not handled. Obviously, message handling can also lead to exceptions being thrown. In that case, the message handler should not just return the Boolean value false to indicate that the message was not handled, but instead reply with an exception stating the cause of the failure. For this purpose the sendReply method is paired with the sendException method.
public void sendException(Exception exception) 
Replies with an exception. 
This example of a message handler accepts "Set the height" messages carrying an integer argument. It is reasonable for the message handler to reject negative integer values. If a negative height is given, the handler will respond to the message with an exception:
public boolean handleMessage(Message msg) {
   if ("Set the height".equals(msg.kind)) 
      if (((Integer)msg.arg).intValue() < 0)
         msg.sendException(new Exception("Illegal value"));
      else ...
   return true;
}

Getting the Reply

We will now continue to describe how the sender of a message receives and processes a reply from the message handler. As noted at the beginning of this chapter, the proxy's sendMessage method returns an object of the FutureReply class. This future object serves as a handle for the expected reply to the message. The object is called a future because it is returned immediately to the sender of the message, bearing a promise of some future reply. The concept of a future is a very flexible one that covers synchronous as well as asynchronous messaging. In synchronous messaging, the sender of the message suspends its execution until a reply has arrived. In asynchronous messaging, the sender continues its execution and subsequently retrieves the reply. We also call this "non-blocking messaging."

 

Synchronous Messaging

Let us start with the case of synchronous inter-aglet messaging. Having received the future object from the proxy's sendAsyncMessage(), you can immediately retrieve the reply from the future by using its getReply method. This method will block until it can return a reply.
public Object getReply() 
Gets the reply to the message. This method will block until a reply can be returned. 
In this example, we send the message "What is your height?" to the proxy of an aglet. We receive the future object and immediately attempt to retrieve the expected reply. Since the getReply method blocks until a reply actually has arrived, we can think of this as a case of synchronous messaging:
Message msg = new Message("What is your height?");
FutureReply future = proxy.sendAsyncMessage(msg);
int height = ((Integer)future.getReply()).intValue();
Full source code: ReplyExample.java and ReplyChild.java.

The getReply method throws two different kinds of exception: MessageNotHandledException and MessageException. An exception of the first kind, MessageNotHandledException, is thrown when the message handler returns a false instead of true result to indicate that it did not handle a given message: An exception of the second kind, MessageException, is thrown when the message handler replied with an exception by calling sendException() on the message object: The example code below demonstrates how to catch the two different exceptions thrown by the getReply method. In the first part of the example below, a message of an unknown kind is sent to an aglet. The message handler of that aglet does not recognize "Unknown", and returns false. As a result, getReply() throws the MessageNotHandledException exception. In the second part, a message with an invalid argument value is sent to the aglet. This time, the message handler recognizes the message kind but finds the value of its argument invalid. It responds by sending an exception. The getReply method will receive the reply and throw a MessageException exception. Notice that it is necessary to explicitly invoke getReply() to know whether the target aglet is really handling the message, since the sendAsyncMessage method itself does not throw that kind of exception. Full source code: ExceptionExample.java and ExceptionChild.java.

Asynchronous Messaging

The group of methods in the FutureReply class that we cover in this section are related to non-blocking retrieval of the reply to a message. Non-blocking or asynchronous messaging allows you to continue execution while the reply has not yet arrived. The first method, isAvailable(), allows you to test whether the reply has come. It returns true if the reply is ready and can be retrieved without blocking for continued execution. The waitForReply method allows the current thread to wait a specified period of time for a reply.
public boolean isAvailable() 
Checks whether a reply has arrived. 
public void waitForReply(long duration) 
Waits for a reply to arrive. If the reply does not arrive within the specified period of time, the thread will continue its execution. 
Public void waitForReply() 
 Waits for a reply to arrive before it continues execution. 
The example below is to demonstrate how the aglet can proceed with a given task from the moment a message is sent until a reply has arrived. In this example, we have created a small loop that uses the isAvailable method to test whether a reply has arrived. As long as no reply has arrived, the body of the loop (doIncrement()) will be repeatedly executed:
FutureReply future = proxy.sendAsyncMessage(msg);
while (!future.isAvailable())               // Checks whether a reply has arrived.
   doIncrement();                           // If not: do incremental work...
String reply = (String)future.getReply();   // Gets the reply.
Another typical case of asynchronous messaging is found in situations where we wish to avoid being trapped in a call to getReply that may never return. This can happen when the message handler goes into a non-terminating loop or when a deadlock occurs. Below, we will show how to wait a limited period of time for a reply. The example uses the waitForReply method, which takes a timeout argument. The waitForReply method will block until either a reply has arrived or the timer expires. Since one cannot tell why waitForReply() returns, an additional call to isAvailable() is needed: Full source code: TimeoutExample.java and TimeoutChild.java.

Message Management

Each aglet has a guard called the message manager that allows deterministic message handling by inserting incoming messages into a queue. It forwards these messages one at a time to the aglet's message handler, in the same order as they were received. It also ensures that the next message is not forwarded until the current message has been handled. Technically, the message manager serializes message handling.

 

The message manager features a number of methods that enable the aglet to customize the behavior of the message manager. This set of methods includes the ability of the aglet

Serialized Message Handling

Let us start our coverage of the message handler by taking a look at message serialization. The following example demonstrates serialized message handling. One aglet (MsgManagerExample) sends three consecutive messages ("One", "Two", and "Three") to another aglet (MsgManagerChild): Messages are handled deterministically. That is, the MsgManagerChild aglet will not start handling one message before it has completed the handling of the previous message. In this particular example, the aglet will not start handling message "Two" until it has finished handling message "One", and it will not start handling message "Three" until it has finished handling message "Two": Full source code: MsgManagerExample.java and MsgManagerChild.java.

The following interaction chart visualizes the execution trace of this example:

 

Message Priorities

The rest of this section covers the advanced use of the message manager. As a first step, the aglet needs to access its message manager. This is done through the getMessageManager method defined in the aglet's own interface.
public final MessageManager getMessageManager() 
    Gets the message manager. 
Now that we have access to the message manager, we will demonstrate how it allows you to set the priority of specific kinds of messages. Setting a high priority for a given kind of message will ensure that it is placed in the message queue ahead of messages with a lower priority. The default priority of a message is 5. A user-defined priority can be set by using the setPriority method in the MessageManager class.
public void setPriority(String kind, int priority) 
    Sets the message's priority. The default priority for messages is 5
To demonstrate the effect of this method, we will create a variant of the above-mentioned message handling aglet that raises the priority of the message "Three". The objective is ensure that the message "Three" is handled before the messages "One" and "Two". The default priority of a message is 5, so let us raise the priority of the message "Three" to 9. We conveniently raise the priority in the message-handling aglet's (MsgPriorityChild) onCreation method: Full source code: MsgPriorityExample.java and MsgPriorityChild.java.

The execution trace of this example is visualized by the following interaction chart:

  

Parallel Message Handling

The message manager also allows messages to be handled in parallel. Calling the exitMonitor method enables the aglet to continue the current message-handling thread while simultanenously allowing a new message-handling thread to be started.
public void exitMonitor() 
    Exits the current monitor. 
In the MsgParallelChild aglet's handling of the message "Two", we explicitly allow other messages to be handled in parallel. When the aglet starts handling the message "Two", it immediately exits the monitor (exitMonitor()). While it continues to handle the message "Two", it will concurrently start handling the next message in the queue, "Three". Full source code: MsgParallelExample.java and MsgParallelChild.java.

This figure shows the aglet simultaneously handling the messages "Two" and "Three":

 

Interrupt Message Handling

If the handling of a message for some (possibly external) reason cannot proceed, the current thread can be suspended until further notice. In this way, it will not block for the handling of subsequent messages. For this purpose, the message manager defines three methods: waitMessage(), notifyMessage(), and notifyAllMessages().
public void waitMessage() 
Suspends the execution of this message-handling thread. The thread waits until further notice. 
Public void notifyMessage() 
Notifies a single waiting message thread in this message manager. 
Public void notifyAllMessages() 
Notifies all waiting message threads in this message manager. 
This example aglet, MsgWaitingChild, suspends the execution of the message-handling thread of the message "Two". This allows the aglet to handle the message "Three", which subsequently will instruct the message manager to resume execution of the waiting message-handling thread. Full source code: MsgWaitingExample.java and MsgWaitingChild.java.

The interaction chart for this example is shown below.

 

Suspending Message Handling


The aglet may cease to accept any messages by invoking the destroy method on its message manager. This call effectively makes the aglet non-responding. All messages sent to the aglet will be rejected.
public void destroy() 
Destroys the message manager. After invocation of this method, the message manager is no longer valid, and all queued and incoming messages will be rejected. 

Multicasting

So far we have dealt only with peer-to-peer messaging where the message sender has to know the identity of the receiver. You will often find this a limitation when you plan to coordinate activities among multiple aglets. In such cases, each aglet may not be aware of the identities of the other aglets. Just imagine multiple aglets originating from different sources that meet in a specific context. How does an incoming aglet advertise its arrival? Fortunately, the context supports message multicasting within a single context. Message multicasting provides a powerful ways for aglets to interact and collaborate. The basic principle is that aglets (1) subscribe to one or more multicast messages and (2) implement handlers for these messages.

 

Aglets subscribe to specific multicast messages by invoking the subscribeMessage method with the message kind as argument. Aglets can subscribe to multiple multicast messages. To unsubscribe, the aglet has to invoke unsubscribeMessage() for the specific message kind. It can also unsubscribe to all multicast messages by invoking unsubscribeAllMessages(). Anyway, when an aglet leaves its current context it will automatically unsubscribe to all messages.
...
public final void subscribeMessage(String kind) 
Subscribes to one kind of multicast message. 
public final boolean unsubscribeMessage(String kind) 
Unsubscribes to one kind of multicast message. 
public final void unsubscribeAllMessages() 
Unsubscribes to all kinds of multicast messages. 
In this example, an aglet automatically subscribes to "Hello Everybody" when it is created:
public void onCreation(Object o) {
   subscribe("Hello Everybody");
}
The aglet also implements a handler for the "Hello Everybody" multicast message:
public boolean handleMessage(Message msg) {
   if ("Hello Everybody".equals(msg.kind)) {
      ...
      return true;
   }
}
Now, let us switch to the context class that defines the multicastMessage method in the AgletContext class. This method takes a message object as argument and sends it to subscribers to the multicast message in the current context. The message object for multicast messaging is identical to the one used for normal messaging. A notable difference between multicast messaging and normal aglet messaging is that multicast messaging is local to a context. In other words, multicast messaging is not location-transparent.
public void multicastMessage(Message messaqe) 
Sends a multicast message to the subscribers in the current context 
This code example shows how an aglet sends "Hello Everybody" as a multicast message in its current context:
Message msg = new Message("Hello Everybody");
getAgletContext().multicastMessage(msg);
 Full source code: MulticastExample.java and MulticastSubscriber.java.