Presents Notes -*- outline -*-

* TODO
- Pass cause back to client somehow via FailureResponse in
  Client.requestFailed

- clientWillLogff becomes clientMayLogoff?

- Look into nbio waking up all sockets when any data comes in.

- (maybe) Allow piggybacking of object subscription onto service defined
  responses (like moveTo request).

- (maybe) Allow better server side control of subscription management (to
  ensure that clients don't remain subscribed to objects they should no
  longer be susbcribed to; like scenes they've departed from).

- Sort out support for server-side modifiable only fields to DObject.

- Create a CompoundEvent that allows packaging up of multiple events to be
  dispatched in unison. Build dobj source generator and have it add
  versions of all update methods that take a compound event to which to
  append the event rather than dispatching them directly.

- Maybe make AuthResponseData a Streamable instead of a DObject.

- Have the LocationRegistry register the LocationProvider rather than
  doing it via a config file. Perhaps lose the config file element
  altogether.

- Think about Subscriber business and whether or not DObject needs a list
  of subscribers or if there's a better way to handle
  removedLastSubscriber() on the client side and not taint the server side
  with all dat crap.

* Server-side event concentrator
- The client objects will not subscribe directly, but will subscribe
  through the concentrator so that, at least, it can create a single
  ForwardEventNotification for each Event being dispatched to a group of
  clients. Optimally, it would be able to flatten that notification as
  well and the byte array can be written to the socket of each of the
  individual clients rather than creating a separate byte array for each
  client. This will require a special "flattened notification" that can be
  inserted into the queue to preserve message ordering but then is simply
  sent rather than flattened and sent.

* Marshaller
- Consider how the dobject marshaller deals with classes loaded and
  reloaded using flushable classloaders.

  Also consider whether access to the marshaller cache needs to be
  synchronized.

* Check into "connection closed by peer" thread exiting on client

* TypedObjectFactory
- Maybe modify so that types are assigned automatically even if everything
  has to be registered in a single place, since it pretty much does
  anyway.

* Client network mgmt
- Client perform all network ops on own thread, will call back to main
  code through Observer interface to notify of state changes in the
  authentication process/connectedness:

  public interface ClientObserver
  {
      public void didConnect ();
      public void connectionFailed ();
      public void didLogon ();
      public void logonFailed ();
      public void didDisconnect ();
      public void didLogoff ();
  }

* DObject class generation
- Distributed objects are defined like a class with a set of public data
  members which is then converted into an actual class with get/set
  methods for each member.

  public dclass GameObject
  {
      public int[] players;
      public String description;
  }

  becomes

  public class GameObject extends DObject
  {
       public void setPlayers (int[] players);
       public void setPlayersAt (int index, int value);
       public int[] getPlayers ();
       public int getPlayersAt (int index);

       public void setDescription (String description);
       public String getDescription ();
  }

* 5/27/2002
** Synchronizing time between client and server
- After authentication, client begins a process of establishing the time
  differential between the client clock and the server clock.

- The client sends a PING packet, noting the time immediately prior to
  delivering the packet over the network.

- The server notes the at which the PING packet was unserialized. It
  supplies that time to the constructed PONG packet which then notes the
  time immediately prior to serialization and uses that to deliver to the
  client the server time and the number of milliseconds that passed
  between the unserialization of the PING packet and the serialization
  (and subsequent network delivery) of the PONG packet.

- The client can then subtract the server processing time from the total
  round trip delay, divide the round trip delay by two, adjust the server
  time stamp accordingly and then obtain the client/server time
  differential.

- The client then repeats this process some small number of times (five)
  to attempt to account for spurious differences in upstream
  vs. downstream transmission times and finally settles on a dT that will
  be used for the duration of the session.

- We assume that the session will not last long enough for clock drift on
  either the client or server to become significant when compared to the
  error in the original time differential measurement.

- We'll want to use the high-precision timing services once we have those
  because we don't want unnecessary error introduced into our ping and
  pong time stamps by the unreliable granularity of
  System.currentTimeMillis().

* 7/8/2002
** Improved invocation services
- Adding remote method call support to distributed objects doesn't allow
  us to separate interface from implementation; DObject classes are shared
  on the client and server by definition; whereas we would want an
  interface that could be known on the client and the interface and
  implementation known on the server

- Perhaps RMI could somehow be rolled into the DObject system... more
  likely we'd want to automate the process of instantiating the
  implementation on the server and the proxy on the client and wouldn't
  want to use their object registry; we'd also want to do method dispatch
  on the omgr thread and use our messaging services

* 7/15/2002
** BEEP!
- Look into replacing low-level network protocol with BEEP (and rolling
  our performance enhancements into BEEP's implementation if necessary)

* 7/18/2002
** PRMI (Presents remote method invocation)
- PRMI ends up looking somewhat like RMI with a few critical differences:

  + it uses the same message passing infrastructure as the distributed
    object system to accomplish its calls and responses

  + it requires asynchronous response delivery (return values from
    remotely invoked methods are prohibited)

- It all starts with an interface that defines the remotely callable
  methods and remotely callable response interfaces:

  public interface LocationService extends InvocationService
  {
      /**
       * Used to communicate responses to {@link #moveTo} requests.
       */
      public static interface MoveListener extends InvocationListener
      {
          /**
           * Called in response to a successful {@link #moveTo} request.
           */
          public void moveSucceeded (PlaceConfig config);
      }

      /**
       * Requests that this client's body be moved to the specified
       * location.
       *
       * @param placeId the object id of the place object to which the
       * body should be moved.
       * @param listener the listener that will be informed of success or
       * failure.
       */
      public void moveTo (int placeId, MoveListener listener);
  }

  Note again that remotely callable methods cannot return values.
  Responses must be communicated asynchronously via listener parameters.

  The InvocationListener interface provides a standard method for handling
  request failure:

  public InvocationListener
  {
      public void requestFailed (InvocationException cause);
  }

  This will be used to report unexpected failure and can also be used to
  report expected failures by the remotely callable method implementations
  if they so desire. This is accomplished by their throwing exceptions
  that extend InvocationException. Non-InvocationException exceptions
  thrown by the remotely callable methods will be wrapped in an
  InvocationException and then passed on to the appropriate listener.

  For methods that declare multiple result listeners (a design choice that
  is not recommended), the first listener in the argument list will be the
  one to which caught exceptions are delivered.

- An InvocationProvider interface is generated from the InvocationService
  interface:

  public interface LocationProvider extends InvocationProvider
  {
      /**
       * Requests that this client's body be moved to the specified
       * location.
       *
       * @param caller the client object of the client that invoked this
       * remotely callable method.
       * @param placeId the object id of the place object to which the
       * body should be moved.
       * @param listener the listener that will be informed of success or
       * failure.
       */
      public void moveTo (ClientObject caller, int placeId,
                          InvocationService.MoveListener listener)
          throws InvocationException;
  }

  This InvocationProvider interface is what is implemented by a server
  entity to provider an actual implementation of the services.

- From the InvocationService interface, marshaller implementations are
  generated for the service interface and all listener interfaces
  contained therein:

  public class LocationMarshaller implements LocationService
  {
      // ...

      public int marshallerId;

      public void moveTo (int placeId, MoveListener listener)
      {
          // pass the request to the invocation services for dispatch
          // over the network
      }
  }

  On the server, an InvocationProvider is registered with the invocation
  services which will return an InvocationMarshaller to be used to provide
  access to those services. The InvocationMarshaller instance can then be
  passed around the distributed object system as any other object. It can
  be made a member of a distributed object or delivered in a distributed
  object event.

  When the InvocationMarshaller is used on the client, it will marshall
  the request parameters and send them over the network to the server --
  where they will be dispatched to the implementation -- any response from
  which will be communicated back through InvocationListener proxies which
  marshall the response and deliver it to the calling client, which then
  unpacks the response and delivers it to the original InvocationListener.

  Access to the services on the server would generally be accomplished by
  obtaining a reference directly to the provider that implements the
  services and calling the methods via normal means. This allows the
  server entities to provide whichever "caller" is appropriate.

- Notification services? Client provides "marshaller" in ClientObject,
  server calls down to client through said marshaller object. How to
  register implementations on the client end?

- Possible to integrate useful general purpose security?

* 8/6/2002
** PRMI notification services
- An interface is defined for the notification services:

  /**
   * Used to communicate asynchronous tell messages to the client.
   */
  public interface ChatReceiver extends InvocationReceiver
  {
      /**
       * Called when another user has sent us a tell message.
       *
       * @param source the username of the teller.
       * @param message the text of the tell message.
       */
      public void receivedTell (String source, String message);
  }

  An entity on the client implements this interface and registers with the
  invocation director as the recipient for such invocation notifications
  like so:

  /**
   * Coordinates chat related functionality on the client.
   */
  public class ChatDirector implements ChatReceiver
  {
      public ChatDirector (PresentsContext ctx)
      {
          InvocationDirector invdir = ctx.getClient().getInvocationDirector();
          invdir.registerReceiver(new ChatDecoder(this));
      }

      // documentation inherited from interface
      public void receivedTell (String source, String message)
      {
          // deal with notification...
      }
  }

  The decoder class referenced above is generated from the interface
  specification and is responsible for converting notification events into
  the appropriate method calls on the receiver implementation with which
  it is constructed.

  [Note: should only one receiver be allowed or should a list of receivers
  be allowed, each receiving the notification when appropriate?]

  A sender class is generated from the interface specification for use on
  the server:

  /**
   * Provides facilities for delivering notifications as defined by the
   * {@link ChatReceiver} interface.
   */
  public class ChatSender extends InvocationSender
  {
      /**
       * Dispatches a {@link ChatReceiver#receivedTell} notification to
       * the specified client.
       */
      public static void sendTell (
          ClientObject target, String source, String message)
      {
          // code to marshall the parameters and dispatch an event to the
          // target client...
      }
  }

  Server code can then simply call the ChatSender.sendTell() method with
  the appropriate client object to dispatch the associated notification to
  that client.

  Notice that the ChatReceiver.receivedTell method was renamed to
  ChatSender.sendTell in the sender class. Methods starting with
  "received" are converted to start with "send". If the interface method
  does not start with "received" its name will not be changed in the
  sender class definition.

* 8/13/2002
** True Names
- Invocation -> Remote? That'd be a hairy pain...

* 8/15/2002
** Invocation service TODO
X genservice + genreceiver need to slurp and replace "Id"

* 9/18/2002
** Client DObjectManager enhancement
- Make a note of recently unsubscribed oids so that we can avoid bogus
  "Unable to dispatch event on non-proxied object" reports that inevitably
  come in when the client has pitched a dobj but the server hasn't yet
  processed the unsubscribe request.

* 10/2/2002
** Compound events/DObject transactions
- They are broken apart prior to dispatch on the server and thus
  completely fail to accomplish their intended goal of providing efficient
  network delivery to the client; attempt to resolve this in some way

* 10/8/2002
** Possible renamability improvements
- Generate field code rather than name for all DObject classes; send code
  over wire; somehow map code to Field instance for setting value (this
  could be problematic)

* 11/14/2002
** Possible eventual optimizations
- Have the connection manager maintain a pool of buffers into which
  network data is read, when a connection receives the start of a new
  message, it grabs a buffer and uses it until it has read its entire
  message at which point it releases the buffer back into the pool;
  otherwise we have a buffer assigned to each connection whether it needs
  it or not and if they grow to, say, 4k per client connection, we could
  easily get up to multiple megabytes of memory dedicated to input buffers

- Have PresentClient subscribe to objects through a proxy which serializes
  messages to be dispatched to multiple clients immediately and uses the
  same buffer to deliver the message to all subscribed clients (currently
  the message is serialized once for each client before network delivery
  and the serialization is done by the conmgr thread at send time;
  ungood). Hrm, on second thought, we may not be able to do that because
  each client connection maintains its own custom
  ObjectOutputStream/ObjectInputStream pair which may not be in the same
  state as another client's. Dooh!
