8a4c46badc
changed to Presents and Party changed to Crowd. Whee! git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@431 542714f4-19e9-0310-aa3c-eee0fc999fb1
217 lines
9.0 KiB
Plaintext
217 lines
9.0 KiB
Plaintext
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/<ident>/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;
|