In this chapter we examine applications that use message-passing schemes to distribute information between agents in a distributed system. The first thing we'll do is define what we mean by "message passing," then we'll look at some ways that messages can be handled by a Java agent. We'll build two versions of a message-passing system. The first is based on a homegrown message-handling system, with Message objects being transmitted over I/O streams between MessageHand-lers. Most of our discussion about message passing will be based on these classes. Near the end of the chapter, we'll look at a message-passing system based on the delegation event model built into the JDK and used by the AWT package. In this case, we'll be using EventObjects as our messages, and leveraging the model of event sources and listeners from the JDK to build a message-handling framework.
Before we start discussing message-passing systems, we need to define what we mean by a message. Essentially, a message is a structured piece of information sent from one agent to another over a communication channel. Some messages are requests made to one agent by another, other messages deliver data or notification to another agent. In most applications that we'll discuss, a message consists of a message identifier and, if needed, a set of message arguments. The message identifier tells the receiver the purpose or type of the message. The arguments to the message contain additional information that is interpreted based on the type of message. They may contain the object of an action (e.g., the message "xy" means, "Do x to y and return the result"), or they may contain information used to carry out a request (e.g., "x a b c " means, "Do x, and use a, b, and c to do it").
Message identifiers are usually simple, unique tokens that differentiate one type of message from another. They may even be simple integer values, where the agents on either end use a look-up table of some sort to match the value with its meaning. Message arguments, on the other hand, can be of many types. Some simple message protocols get away with using only basic data types, like integers, strings, and floating-point values, for message arguments. These arguments can be read and written directly using the DataInputStream and DataOutputStream classes. Other protocols need to use more complicated data types and objects as message arguments. These complex data types can be sent as an ordered sequence of simple data types over a DataOutputStream. They can also be transmitted as objects using the Java RMI object serialization support, as discussed in Chapter 3, "Distributing Objects".
In some very well defined and controlled application environments, a message protocol may not need message identifiers at all. The interactions between agents may be so strictly defined that there's no need to specify the type of message being sent because the receiver is already expecting it. Suppose, for example, that we have two chess-playing agents talking to each other. Assuming that they both always make valid moves and that they both continue to play until checkmate results, the only kind of message they need to exchange is one that contains the next move that they want to make, with an optional argument that indicates if the move results in a "check" or a "checkmate." In this case, there's no need for message identifiers--the messages can just contain the chess players' moves.
In a sense, every network standard and protocol we've discussed so far can be boiled down to some form of message passing. HTTP, SSL, even low-level network protocols like TCP/IP are protocols built around some form of message passing. When we speak of message passing in this chapter, however, we are talking about message passing that is done explicitly by the application programmer. While these other protocols are built around some kind of message-passing protocol, that level of the protocol is hidden from the developer by an API of some kind. For example, SSL is utilized by an application programmer through a class library, where method calls on SSL-related objects are automatically broken down into SSL-compliant messages. Likewise, incoming SSL "messages" are processed and mapped into new data objects and method calls on SSL objects. This is what makes these complicated but powerful protocols so useful: the application programmer doesn't need to know the details of the protocol at the lower level. When we speak of message passing in this chapter, we're referring to situations where the message protocol--the generation and the processing of messages--is defined and performed directly at the application level.
Copyright © 2001 O'Reilly & Associates. All rights reserved.