Presents Design -*- outline -*- * Why Presents? The basic function of this layer is to provide a network presence (as you can see, we love to pun; don't complain, this package used to be called Cher) and to allow the sharing of information among different nodes in the network. * Overview The Presents layer implements the distributed object services described in the Narya design document. It does this within the context of an extensible client/server application framework. Presents provides services that can be integrated into your distributed application to share information between a set of clients and entities operating on the server. * Authentication and bootstrapping The client initially transmits an authentication request to the server and receives an authentication response in return. If successfully authenticated, the client will then start up the distributed object machinery, but will remain in a less functional state until the server delivers the bootstrap notification. The bootstrap notification contains the client's distributed object oid as well as the invocation services oid. With this information the client can complete initialization and attain a fully operational state. The motivation for splitting up the authentication response and the bootstrap notification is because the authentication process completes separately from the server's client management initialization. The authentication machinery replies to the client and sets the client management machinery in motion. When that completes (and a client object has been created for the client), the bootstrap notification is sent off and normal operation ensues. * Invocation services To facilitate the client invoking code on the server (in a request/response arrangement) and the server invoking code on the client (in an asynchronous notification arrangement), the invocation services are provided. We make use of reflection to make the invocation services feel a bit like remote procedure calls. All invocation traffic is managed by the invocation manager, part of which resides on the client and part on the server. There are three classes involved when fully using the services. They are the service class, the provider class and the receiver class. ** Service class The service class provides the client-side API to the request/response component. For example: public class ChatService { public void requestTell (String username, String message, Object rsptarget); } The tell() implementation would wrap the arguments up and pass them off to the invocation manager for delivery to the server. If provided, the response target object will receive a callback when the response comes in from the server. The callback will come in the form of a call to a reflected method on that object. The exact name of the method depends on the implementation of the provider class which runs on the server. ** Provider class The provider class implements the server-end of the service and is registered with the invocation manager on the server to handle a particular class of invocations. Continuing with our example: public class ChatProvider { public void handleTellRequest (int invid, String username, String message); } The handleTellRequest() function will process the request and then generate a response which is passed on to the invocation manager for delivery to the client. The response will be named and the name of the response will dictate the method that is invoked on the response target object. The arguments that go along with the name must correspond to the signature of that method. For example: invmgr.respond(invid, "TellFailed", new Object[] { "m.no_such_user" }); will result in: public void handleTellFailed (String reason); being called on the response target object. Because the response target method is looked up only by name, all responses using the same name must use the argument signature and the response target object may only have one method with that particular name and its signature must match exactly the signature dictated by the arguments. ** Receiver class For asynchronous messages from the server to the client, there exists the receiver class. The receiver is registered with the client invocation manager to handle messages of a particular type (much like the provider class is registered on the server) which is identified by a string name. The receiver class provides methods named like so: public class ChatReceiver { public void handleTellNotification (String from, String msg); } where the arguments to the method again correlate with the arguments in the invocation message. The server wrapper that generates the corresponding invocation message for delivery to the client will likely also reside in the ChatProvider class already described. For example: public class ChatProvider { public void sendTell (BodyObject to, String from, String msg); } This function will simply wrap up the arguments and pass them to the invocation manager for delivery to the appropriate client. * Server configuration The Presents server binds a properties file into the configuration name space under the key "presents". This properties file lives in rsrc/config/presents/server.properties and is loaded from the jar file in which the Presents server code is provided. Values specified in this properties file can be overridden by a mechanism that will be provided by the configuration utilities used by the server. This is accomplished by placing a properties file earlier in the classpath than the one supplied with the Presents code. The values in that earlier properties file will override the ones in the standard file. Values not supplied in the override file will be retrieved from the standard file. Derived server classes should place their server properties into the classpath with a path along the lines of rsrc/config//server.properties and should bind it into the config namespace with their own identifier. If they desire to override values in the Presents server configuration, they should provide a rsrc/config/presents/server.properties in their jar file ensure that their jar file occurs earlier in the classpath than the Presents server jar file. * A note on thread-safety Distributed objects are designed only to be accessed from one thread. On the server, there is a distributed object dispatch thread on which 95% of all activity takes place anyway. It would be questionable to require that thread to access distributed object members through synchronized members just so that the few places where it is convenient to access dobjs off of the dobjmgr thread are simplified. Instead we've opted for the performance and care must be taken not to access distributed objects outside of the dobjmgr thread. Events can be generated from any thread, but values should not be read from the distributed object on other threads because they are subject to change at any time and could be half changed when some other thread goes to read them. On the client, care is taken to combine the AWT and dobj threads so that life is simple from a synchronization standpoint. None the less, the same care should be taken when other threads are introduced (IntervalManager for example) not to read values from a distributed object on those other threads. This is easy enough to do. Simply copy the values you care about out of the object before passing the information on to another thread (take care to copy non-primitive values like arrays and OidLists). If you find the need to fetch values from a distributed object after another thread has already started, you'll just have to rethink your approach. * Client components ** DObjectManager Manages object proxies; converts value change requests into events, forwards them via the iomgr; dispatches events on incoming queue; reaps proxies when last subscriber goes away ** UI (AWT/Swing) Standard AWT/Swing UI ** UI (Controller)? Provides a paradigm of controllers and commands; code can post commands back to the controller queue for later execution; UI elements structured to automatically generate commands; will probably opt not to use this in favor of Swing's built-in paradigms ** I/O Manager *** Reader Reads incoming data from the socket; decodes messages; posts events to domgr queue; notifies object subscription penders (this should be done asychronously) *** Writer Encodes object subscription and event forwarding requests; writes them to the outgoing socket ** Client object Informs exo-client about connection state changes; provides interface to connection + authentication (logon) and disconnection (logoff); provides access to omgr and client dobj * Server components ** Connection Manager Listens on accepting socket; creates and manages connection objects; informs connection observer of state changes; handles all network traffic on own thread; ** Auth Manager Processes auth requests on own thread; uses pluggable Authenticator to perform actual authentication; ** Client Manager Registers with connection manager; manages authentication; maps connections to existing client objects or creates new client objects for newly connecting clients;