COMP 310
Spring 2019

Lec34:  ChatApp finalization, continued...

Home  Info  Canvas   Java Resources  Eclipse Resources  Piazza

 

For guidance on handling threads, please see the Java Threads Resource page.

Routing of messages to chatrooms isn't necessary!

A common question is how to make sure that messages sent from one chatroom end up at the correct destination chatroom?  This issue comes up in both fully-connected-graph type distributed chatroom architectures as well as star-topology chatroom architectures.   A frequent solution is to tag every message with a chatroom ID, send it (and all other messages from all other rooms) to a common message handler (the RMI server end) on the receiving application, which then first interprets the message to figure out the chatroom ID and then routes the message on to the appropriate room.   There is a false sense of simplicity here because there is only one message receiving object but this is at the cost of added routing complexity that couples the chatrooms together.

But routing is not necessary to achieve the same end.  Since the message is originating from a particular chatroom, why not simply send it to directly the corresponding chatroom on the receiving application?    Instead of having a single message receiver, simply have one receiver per chatroom.   Messages can now be sent directly from one chatroom to its associated room without any routing.   Messages do not have to be tagged with a chatroom ID because they are only ever going to that room.   Plus, the fundamental decoupling of the mini-model architecture of chatrooms is preserved.   No global management of messages is needed.  The cost of  the addtional RMI server objects far outweighs the cost of maintaining the routing management.  By respecting the encapsulation and decoupling of the chatrooms, global management is restricted to only operations of creating and deleting entire chatrooms and nothing concerning anything internal to the chatrooms, such as its messages.

In the following diagrams, we see the differences between routing and non-routing architectures.   Note that the discussion is still valid even if the sending room is not technically the same sort of entity as the receiving room, as would happen in star-topology chatroom implementations.

Message routing architectures

 

Fundamental DataPacket processing

Remember that a DataPacket is an extended visitor host, so processing the message being carried by a datapacket is trivial:  Since the processing of the message is dependent on the type of the message, simply execute the datapacket's visitor, i.e. aDataPacket.execute(aVisitor)

The index value that a datapacket uses to call the appropriate case is the Class object corresponding to the type of the message contained in the datapacket.  (Note that the Class object may not be the exact class of the message but rather, often corresponds to a superclass of the message type.)  This means that the command in the visitor invoked by the datapacket will necessarily be the correct command to process that message.   If the message is an unknown type, then the default command gets invoked.   Thus, in the spirit of HW06, creating the datapacket processing visitor is simply a matter of loading the appropriate commands into the extended visitor object.   

Technically, only one visitor is ever needed to process any datapacket.  If, at run-time, it is desired to process more types, e.g. new types, of messages, one simply adds new commands to the visitor.   An entirely new visitor is NOT required for a new message type, only a new command!  After that, the visitor can now process the new message types without ever needing further modifications, that is, the new command for the new message type only needs to be installed once.   (Note: it is possible to "swap out" an existing command to change the behavior of a particular message type's processing but one must be careful that the class correspond to the new command must be different than the existing one or the dynamic class loader may fail when trying to instantiate the new command.)

That said, some implementors like to separate which commands can be sent to other machines and/or which commands can be added or modified in the system.   There are a couple of ways (at least) to implement this:

  1. Keep track of the command types:  Maintain lists of message types whose commands are allowed or not allowed to be transmitted or mutated and consult those lists when the need arises.
  2. Keep commands in separate visitors:  Keep the "immutable" commands in one visitor and the "mutable" commands in another.   Daisy-chain the visitors together by having the default command of one delegate to the other visitor.   This effectively mimics the operation of a single visitor but with the ease of physically separate storage of the different types of commands.  This is an example of the Chain-of-Responsibility Design Pattern where the processing is delegated down a chain of objects until one object is finally able to do the processing.

Note that if chatrooms are decoupled, then each chatroom can have their own message processing visitor and thus the types of messages each chatroom can process can be completely decoupled from each other.

 

Unknown Data Type Management Protocol Options:

 

Connecting a remotely-sourced command to the local system

An often overlooked issue with installing externally-sourced commands is how those commands are able to interact with the local system into which they are being installed.   It would not be prudent for the local system to expose its entire inner workings to an externally-sourced command.   Only a small, API-specified subset of services should be made available to those commands.    That is, every command needs to have access to an adapter that the local system provides when the command is installed into any visitor on the local system.   This adapter will provide the services the command needs but in a manner that decouples it from the local system.

Since a command being sent over from a remote system has no idea about the environment of the local system into which it is being installed.  For instance, it has no intrinsic means in which to display any information on the local UI or interact with the local user or system.   Thus, the local system must give the incoming command a reference to an adapter to the local system where the adapter implements an interface that is part of the common public API.

The question is "What minimal set of operations on this adapter are needed to give the command the most flexibility but minimize its ability to harm the local system?" 

Note that the attachment of this adapter to the incoming command is only ever done once, when the command is first received from the remote sender.   The method that installs the local system adapter, is thus a perfect place for the command to perform any one-time initializations that it needs to do upon its integration into the local system.

 

QUESTIONS: Should an incoming command

 

Important implementation detail: 

Keeping a field value from being serialized:

As per the above API standard, a ADataPacketAlgoCmd implementaion may have a field of type ICmd2ModelAdapter that holds a reference to current adapter.    When a command is transferred from the sender to the receiver, the command may have been already connected to the sender's model via an ICmd2ModelAdapter implementation created by the sender. 

But when the command is sent to the receiver, the receiver will install its own ICmd2ModelAdapter that connects to its own model and view.   It is not necessary or desirable to serialize and transmit the value of the adapter reference  esp. since such adapters are usually anonymous inner classes. 

To tell Java not to serialize and transmit the value of a field, mark the field with the keyword "transient". 

Example:

private transient ICmd2ModelAdapter cmd2ModelAdpt; 

When this the command is deserialized at the receiver, this field will be null.    Thus, you MUST call setCmd2ModelAdpt() on the new command BEFORE it is installed into the data packet processing visitor (e.g. algo.setCmd())!

 

Adding components to the GUI

For maximum flexibility, most ChatApp API's will have the command to local system adapter provide a service(s) which the command can use to add any sort of component (typically a JComponent) to the local GUI in a location determined by the local system.    This enables flexibility and extensibility in the GUI but without restricting the system to a single technique of displaying new components, e.g. being forced to open a new JFrame for every new component.

There are a number of techniques for providing this GUI modification service but no matter what, one must be very careful because of Java's requirement that ALL GUI modifications and instantiations take place on the GUI ("AWT Event Dispatch") thread.  Please see the Java Resources page on Dynamcially Modifying GUIs for in-depth information on the different options.

Two types of components in ChatApp:  In ChatApp, there are two distinctly different scenarios in which a component may be added to the local GUI.

  1. Scrolling some components:  Some components that are added are considered by the system to be just fancy displays of message contents and in such, just like text messages, are desired to be placed on some sort of scrolling display.
  2. Non-scrolling components:  Some components that are added are actually static modifications of the local GUI to present additional user interaction capabilities.  It would be undesireable for these components to scroll off the screen as other messages arrive; they should have a fixed location on the local GUI.

The ChatApp API designer must make the decision to support one or both of the above scenarios.

 

Avoiding Deadlocks!

To help make your code more robust and to guard against deadlocks caused by your own code or in combination with other people's code, follow these "defensive coding" recommendations:

These won't guarantee that your app won't be deadlock free but it will make it much more robust and far less prone to deadlocking.

Note that deadlocking is far more prevelant in the Final Project than in ChatApp due to the signficantly more complex operations being performed.    So getting these defensive coding practices installed into your ChatApp could save you a lot of headaches in the Final Project!

 

 


© 2019 by Stephen Wong