org.jcon.util.msg
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...
Package Overview
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 & Documents
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
Examples
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.
Comments & Notes
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: DataLib.sortStringVectorToArray(namesVector) is only external dependency. 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: Watch out for the statics!!! 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: GenLib.error(methodID, message) is only external dependency. 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???
For more Information...
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