Notes on writing a communication strategy

Author: Jeff Dalton

Overview

"Strategy" is too grand, but "method" could be confusing in Java.

From the point of view of the rest of the system, a communication strategy is responsible only for sending and receiving messages, and it's ordinary Java objects that are sent and received. The rest of the system knows nothing about how an object might be encoded when it's sent, and it doesn't have to construct special message objects in order to send something.

However, there are some restrictions. A comm strategy doesn't have to be able to handle every kind of Object. For example, the "simple" strategy requires that the objects be Serializable, and the simple "xml" strategy requires that they be defined in ways that can be handled by the I-X XML-translation code. (They have to have get- and set-methods for the fields that need to be encoded, for example.)

However, all strategies have to be able to handle the objects that we're actually going to send: instances of Issue, Activity, etc. This list will get longer, so special-case code should not be used. However, these objects will all be ones that the XML translator can handle, and that means there's support for extracting the relevant fields, etc, which can be used independently of the specifically XML-related code. They will also all be serializable.

Moreover, if a new strategy wants to use an encoding that we'd like to support (an example might be the Java 1.4 XML serialization for beans), the class definitions of the objects we want to send will be adjusted as required, or else some kind of translation support would be developed.

(A strategy that wants to use an XML encoding should use the usual I-X translation code unless there's a good reason not to.)

A strategy normally supports only one way of doing things and so can be used only with agents that are using a compatible approach. A kind of "meta strategy" that picks the required strategy for each case could be created, but so far this has not been necessary.

Some interfaces

Many objects that we send will implement the Sendable interface, which presently contains

    public Name getSenderId();
    public void setSenderId(Name id);
    public Object clone() throws CloneNotSupportedException;

It is not required that they implement that interface, but if they do, the comm strategy might find the sender-id useful in error messages or for other purposes.

The main strategy-related interfaces have "IPC." at the front of their name, because they are all defined inside the ix.util.IPC class.

The strategy itself must implement the IPC.CommunicatonStrategy interface which contains:

    public void sendObject(Object destination, Object contents);
    public void setupServer(Object destination,
	                    IPC.MessageListener listener);

When a new message arrives, it should be passed to a message-listener object that implements the IPC.MessageListener interface:

    public void messageReceived(IPC.InputMessage message);

IPC.InputMessage is another interface, containing:

    public Object getContents();

So there are special message objects on the *receiving* side. It is also possible for them to be created *inside* the sendObject method. This allows the communication strategy to include any information it wants to "wrap" around the message contents when sending and, on the receiving end, to give this information to the message-listener in case it is useful for debugging output or some other purpose. Of course, further "wrapping" may also be used, but it would be invisible to the message-listener as well as to the code that calls sendObject.

A minimal implementation of IPC.InputMessage, IPC.BasicInputMessage, is provided. It has a 1-argument constructor that takes the contents object as a parameter.

Sending

    public void sendObject(Object destination, Object contents);

The sendObject method is responsible for sending the contents to the destination. So in that case, the destination identifies the remote agent we're sending to. Code that calls this method will expect to be able to catch an exception if anything goes wrong. sendObject should not return until the send has (so far as it knows) completed. If for some reason a strategy would need to do some of the sending in another thread, and it's impractical to wait until that finishes, we will have to decide on a case-by-case basis what should be done. It is possible that a new mechanism for reporting such problems would be created (and could then be used by other strategies).

In practice, the destinations will all be Strings, but it's best for the comm strategy not to depend on that.

The sendObject method will often be called from the AWT / Swing event-dispatching thread, but that should not be assumed. (See the JDK documentation for SwingUtilities.invokeAndWait(Runnable) and SwingUtilities.invokeLater(Runnable) to see how to deal with such situations.)

However, within the comm strategy, sending should not normally try to do anything with the GUI; it should just report problems by throwing exceptions.

Note that these destination objects are meant to be meaningful at the user level, and the same ones will be used with all comm strategies. They are not whatever low-level name or address the comm strategy ultimately uses. Some comm strategies use an agent-communication package that can use "destinations" directly as agent names (at least the destination objects we actually use). In other cases, some way of translating between I-X "destinations" and lower-level entities must be provided.

Receiving

    public void setupServer(Object destination,
	                    IPC.MessageListener listener);

The setupServer method is responsible for doing whatever is required to allow this agent to receive messages. In this case, the "destination" parameter is the object that should be used to refer to this agent when remote agents want to send to it. It is not strictly necessary to communicate this destination object to the remote agents, just so long as they are somehow able to send to us, but it will typically be used in some sort of registration process for this agent and thus become known to the remote ones.

The setupServer method is called exactly once. It is called from the main thread, during the initialisation of the I-X agent, and before the agent's GUI has been created.

In any case, setupServer must return once it thinks it has things set up, having created any additional threads needed to handle the actual receiving of messages.

Those threads are meant to run independently of the rest of the system and to report problems to the user via the SwingUtilities.invokeAndWait method.

The IPC.MessageListener used by I-X agents has a synchronized messageReceived method in case multiple receiving threads want to call it. It also handles the transition to the thread that is running the main body of the agent.

When an existing agent-communication package / system is used as the basis of a communication strategy, it may not be visible in the source code written to use that package that any threads are being created; but they must be for the rest of the I-X agent to be able to run independently. You may therefore need to be aware of this when writing the strategy.

If the package does not create any message-receiving threads, you will have to do it yourself.

Using / testing a strategy

The strategy is specified when an I-X agent is run. It can be specified on the command line or in a .props file. The details of this will depend on what scripts you are using to run I-X agents, but the aim is to give a strategy name as the value of the "ipc" parameter. For example, using a script that approximates the normal "java" command (but with the class-path set up for I-X):

   scrips/unix/ix-java ix.ip2.Ip2 -ipc=simple

A strategy may require or allow additional parameters, for example:

   scrips/unix/ix-java ix.ip2.Ip2 -ipc=simple -run-name-server

These should tyically be examined in the setupServer method. For information on how to get parameter values, see the javadoc for the class ix.util.Parameters. It is not necessary that any additional parameters be supported, however. "run-name-server" is specific to the "xml" and "simple" strategies.

The "-" in "-ipc" is part of the syntax for command-line arguments, not part of the parameter name, which is just "ipc". So in a .props file, the "-" would not appear.

The above description is not meant to be enough unless you already know how to run I-X agents and to specify such "parameters", but it should give you the basic idea, namely that there is a parameter called "ipc", specified when the agent is started, that has a value that is the name of the communication strategy to be used.

Now, this strategy-name can be a class name, or it can be an abbreviation. "simple", "xml", and "grid" are examples of abbreviations. If the class name (and package) of your new communication strategy take a certain form, an abbreviation is automatically defined. Otherwise, the full, package-qualified, class name will have to be used. For an abbreviation, name, the full class name is ix.name.NameCommunicationStrategy. For instance the abbreviation "grid" corresponds to

   ix.grid.GridCommunicationStrategy

You might wonder how you can get something into the "ix" package without making it part of the main I-X code. The answer is that any "ix" directory can be used, so long as it is on the class-path.

Note that when an I-X agent's "Messenger" is used to send to "me", the message does not go through the communication strategy at all; but if you send to the agent's own "destination", it does. All communication strategies should handle an agent sending to itself in that way.

The destination an agent uses for itself (and thus for other agents to send to it) can be specified via the "symbol-name" parameter. For example,

scrips/unix/ix-java ix.ip2.Ip2 -symbol-name=jack -ipc=xml -run-name-server

Finally, note that some debugging messages that appear with all communication strategies print XML representations of objecs using the standard I-X encoding. Don't be misled by this if you are using a different XML encoding or are not using XML at all.


Jeff Dalton <J.Dalton@ed.ac.uk>