|
This purpose of this page is provide a basic overview of this
Package. For further information, please refer to the accompanying documents, Javadocs, or
even the source. Or, ask the guru...
- Package Overview
- Models & Documents
- Examples
- Comments & Notes
- For more Information...
The org.jcon.util.msg package is a Messaging
subsystem. The primary purpose is to allow a system to send Messages from one Part to
another Part.
Models
- Message Subsystem Model
Interfaces:
- MessageSource - Indicates the
implementer is capable of generating messages and thus can allow MessageListeners to
register to receive Messages.
- MessageListener - Indicates the
implementer is listening for certain types of messages.
- MessageService - Indicates
the implementer is capable of responding to Messages which are sent up the system tree
from a ContainerServicesUser.
Classes:
- Message - A reusable packet of data originally
designed to be used in an event router.
- MessageRouter - This class allows event
sources and listeners to collaborate with loose coupling using Messages.
- MessageRouterRegistry - This
class contains a collection of MessageRouters.
- MessageSourceStd - An abstract class
providing a default implementation of MessageSource.
- MessageDef - Defines a Message event by
containing the message event name, source class name and properties.
- MessageLink - Represents one unique
source/event/listener combination.
- MessageTranslator - Translates
Messages by changing message and/or property names.
Dependencies:
- Message - org.jcon.util.PropMap
- MessageRouter - org.jcon.util.DataLib
- MessageTranslator - org.jcon.util.GenLib
Part-to-Part Messages - How to send
a Message from one part to another. This process can also be done via DK.
Part A implements MessageSource (ie. it can generate
Messages)
public class PartA implements MessageSource {
private MessageRouter messageRouter = new MessageRouter();
//---------- MessageSource Implementation ----------------
public void addMessageListener(String eventName, MessageListener listener) {
messageRouter.addListener(eventName, listener);
}
public void removeMessageListener(String eventName, MessageListener listener) {
messageRouter.removeListener(eventName, listener);
}
public MessageRouter getMessageRouter() {
return messageRouter;
}
public Vector getMessageDefs() {
Vector defs = new Vector();
MessageDef def;
def = new MessageDef("Test1", this);
defs.addElement(def);
return defs;
}
}
Part B implements MessageListener (so it can receive
messages)
public class PartB implements MessageListener {
//---------- MessageListener Implementation --------------
public void processMessage(Message message) {
String messageName = message.getName();
if (messageName == "Test1") {
System.out.println (messageName + " received");
} else {
System.out.println (".processMessage() - Unknown messageName
'" + messageName + "'");
}
}
public String[] loadMessageInterests() {
return new String[] {"Test1"};
}
}
Part C ties it all together by instantiating Part A
and PartB, registering PartB as a listener to PartA, and then firing a message from PartA
to PartB.
public class PartC {
public PartC () {
PartA partA = new PartA();
PartB partB = new PartB();
partA.addMessageListener ("Test1", partB);
partA.getMessageRouter().fire(new
Message("Test1"));
}
}
Part-to-Service Messages - How to
send a message from one part to another by using the ContainerServices approach (promotes
looser coupling because the parts don't have to know about each other's existance).
PartA implements MessageService (so it can receive
Messages passed up the Container heirarchy)
public class PartA implements MessageService {
//---------- MessageService Implementation --------------
public void serviceMessage(Message message) {
String messageName = message.getName();
if (messageName == "ValidateName") {
print(" - Received ValidateName message, name = " +
message.getString("Name"));
message.setTrue("IsValid");
} else {
print(".serviceMessage() - Unknown messageName '" + messageName +
"'");
}
}
public MessageDef[] getServiceMessageDefs() {
// Do this later
return null;
}
PartB implements ContainerServicesUser (so that it
can send Messages up the tree)
public class PartB implements ContainerServicesUser {
private ContainerServices containerServices;
//---------- ContainerServicesUser Implementation --------
public void setContainerServices(ContainerServices services) {
containerServices = services;
}
public String[] getContainerServicesInterests() {
return new String[] {"Test1"};
}
}
PartB can then send a Message up the tree like
this...
containerServices.sendMessage(testMessage);
Voila! The message will be delivered! And PartB has no
knowledge of PartA. The message becomes the interface.
This section fleshes out the outline given above.
There are 3 main interfaces:
MessageSource
- Indicates the implementer is capable of generating messages via MessageRouter.fire(msg), and thus can allow MessageListeners to
express an interest in receiving certain types of Messages.
MessageListener - Indicates the implementer is
listening for certain types of messages. Messages are received via processMessage(msg).
MessageService
- Indicates the implementer is capable of responding to Messages
which are sent up the system tree from a ContainerServicesUser sent via ContainerServices.sendMessage(msg)
There are 7 key classes:
Message
- A packet of data to handle basic communication needs between parts.
Notes:
Extends org.jcon.util.PropMap,
implements Serializable
In the BA, messages are closely linked to the concept
of events. Consequently, messages always have a name (String).
Questions / Issues:
Why does the Message class
refer to eventName? Perhaps a
more meaningful naming convention would be messageName, since there may well be messages which are in no way related to events.
I recognize this is a minor point, but as long as we're striving for consistency... :-)
Current use of .intern() requires parts which use
Message _not_ to pass in a message name using new String("blah"). This is not
obvious, and may result in errors where are very difficult to track down
There are also issues whereby .intern()'d Strings don't get GC'd...this becomes a memory
liability then for systems which create a large number of Messages or run for a long
period of time. I need to verify this to be sure its accurate (I ran into it this
problem in another life)...
It appears that the primary purpose of the .intern() is to allow the use of == when
comparing messageNames. While == is slightly faster, it goes against everything
people learn when moving to Java, and it opens up the possibility for making very subtle
errors.
Why is eventName
protected instead of private?
Why is there a static send method
in Message? Shouldn't we always be sending messages throught he MessageRouter?
MessageRouter
- This object is obtained from a MessageSource and is used to actually route a Message to
any interested listeners.
Note that under the BA approach it, its generally a Part's responsibility to grab the
router and tell it to broadcast the Message. Also note that the MessageRouter is
smart...it determines which Listeners should actually receive the message (as opposed to
broadcasting the Message to all available listeners and letting them decide whether or not
they're interested).
This is much more efficient.
Notes:
Questions / Issues:
Why does MessageSourceStd implement
addMessageListener(), and then turn around and actually add the Message to the
MessageRouter via addListener()? In other words, what's to prevent someone from just
bypassing MessageSourceStd and adding listeners directly to the MessageRouter?
Perhaps a better way of putting this is...what is the reasoning behind separating
MessageSourceStd and MessageRouter into two separate classes? They appear to always be
used together.
Assuming parts should always use MessageSource to
add/remove listeners, why not limit the scope of those methods in MessageRouter to just
package level access?
For consistency sake, it might be nice to use sendMessage() instead of fire()
MessageRouterRegistry
- This class allows us to create collections of MessageRouters. Note that it
implements a singleton pattern (getSingleton()) to provide a global RouterRegistry.
Notes:
Questions / Issues:
The documentation states that most clients will use
the "master" registry. This is probably a very bad idea because this instance
will be shared across the JVM, which will cause problems when the clients are running
within a) servlets or b) applets in multiple browsers silmutaneously.
We might want to consider implenting the concept of NameSpaces and ObjectRepositories to
solve this issue.
How many places are currently using the "master" registry?
As a very minor point, we might want to consider
putting off the master registry instantiation until its actually requested. No point
keeping extra memory allocated when we don't need to.
MessageSourceStd
- This is a default implementation of the MessageSource interface. Note that it really
does very little, besides allow you to get to the underlying MessageRouter (see prev
questions).
MessageDef
- I don't understand this yet...I see that its used by MessageService, but I'm not sure
what its actually trying to accomplish. Jack could you provide a
little explanation here?
MessageLink
- Represents one unique source/event/listener combination. This is
another one I'm not fully sure of yet...what's this little guy's purpose in life?
MessageTranslator
- This class is basically designed to act as a translator. It keeps track of old/new
Message names, and can translate back and forth between the two. This is very useful when
you'd like to interface two systems which use the same messages, but they go by different
names in each system.
It will also handle translating property names.
Notes:
Questions / Issues:
Looking briefly at the source, it appears that
translation is actually only one way...is this correct, Jack?
This is a minor point, but maybe a
better solution than using GenLib.error() would be to make this class implement
ContainerServicesUser and to pass a generic Message up the tree...if a service exists to
display the error message it would do so. If not, everything would just keep functioning.
This would better insulate this package from any other packages.
Misc Questions / Issues:
Consistency/Simplification
- Normal messages are sent from a MessageSource to MessageListeners like this:
messageRouter.fire(testMessage);
In the case of the MessageService approach, messages are sent from a ContainerServicesUser
(A) up the tree to a (potentially waiting) MessageService implementer (B) like this:
containerServices.sendMessage(testMessage);
Q: Why sendMessage() in one and fire() in another?
A: They should probably be the same... :-)
Q: Why use a separate ServiceMessage interface at all? Why not just have
B register as a listener for the Message, then the messageRouter could have a method
called fireUpTree (or maybe sendMessage/SendMessageUpTree). A invokes
messageRouter.sendMessageUpTree(testMessage)
and the MessageRouter simply traverses up the tree looking for a MessageListener
interested in that particular Message...when it finds B it delivers it and stops, if the
listener handles it. If the listener doesn't handle it, it could continue up the
tree until it finds someone who can handle it or it reaches the tree root.
It seems like this approach would require fewer classes to accomplish the same goal. Of
course, there probably is a reason for doing it this way...which is why I'm asking.
A: Jack's answer...A MessageService has different responsiblities from a
MessageListener. If a MessageService is not interested in handling a Message, it must pass
it up the tree unless in the root. If a MessageListener is not interested in handling a
Message, it does nothing.
Just a question for verification purposes...both
MessageSource and MessageRouterRegistry define static final String constants; these can't
be referenced through DK, right???
If you are a developer and make changes to any of the models or
source in this package, remember that you must update this document as well!
If you have questions, comments, or suggestions for improvement,
please feel free to contact one of the folks listed below.
Package Author/Guru: Jack Harich
Last Updated: Christian Cryder, 5/19/99 |