diff --git a/docs/presents/notes.txt b/docs/presents/notes.txt index 8eceac251..00a225bf4 100644 --- a/docs/presents/notes.txt +++ b/docs/presents/notes.txt @@ -12,6 +12,8 @@ clientWillLogff becomes clientMayLogoff? Look into nbio waking up all sockets when any data comes in. +Wire up Client.sessionDidTerminate; do session termination in general. + * 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 diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java index 63839fa46..4c8fdc671 100644 --- a/src/java/com/threerings/presents/server/ClientManager.java +++ b/src/java/com/threerings/presents/server/ClientManager.java @@ -1,5 +1,5 @@ // -// $Id: ClientManager.java,v 1.8 2001/08/03 02:25:49 mdb Exp $ +// $Id: ClientManager.java,v 1.9 2001/08/04 01:54:34 mdb Exp $ package com.threerings.cocktail.cher.server; @@ -109,7 +109,7 @@ public class ClientManager implements ConnectionObserver // create a new client and stick'em in the table try { client = (CherClient)_clientClass.newInstance(); - client.init(this, username, conn); + client.startSession(this, username, conn); _usermap.put(username, client); } catch (Exception e) { Log.warning("Failed to instantiate client instance to " + diff --git a/src/java/com/threerings/presents/server/PresentsClient.java b/src/java/com/threerings/presents/server/PresentsClient.java index 440418ab4..1fe113b90 100644 --- a/src/java/com/threerings/presents/server/PresentsClient.java +++ b/src/java/com/threerings/presents/server/PresentsClient.java @@ -1,5 +1,5 @@ // -// $Id: PresentsClient.java,v 1.13 2001/08/03 02:25:49 mdb Exp $ +// $Id: PresentsClient.java,v 1.14 2001/08/04 01:54:34 mdb Exp $ package com.threerings.cocktail.cher.server; @@ -29,10 +29,27 @@ import com.threerings.cocktail.cher.server.net.*; public class CherClient implements Subscriber, MessageHandler { /** - * Initializes this client instance with the specified username and - * connection instance. + * Returns the username with which this client instance is associated. */ - public void init (ClientManager cmgr, String username, Connection conn) + public String getUsername () + { + return _username; + } + + /** + * Returns the client object that is associated with this client. + */ + public ClientObject getClientObject () + { + return _clobj; + } + + /** + * Initializes this client instance with the specified username and + * connection instance and begins a client session. + */ + protected void startSession ( + ClientManager cmgr, String username, Connection conn) { _cmgr = cmgr; _username = username; @@ -44,7 +61,7 @@ public class CherClient implements Subscriber, MessageHandler { public void objectAvailable (DObject object) { - _clobj = (ClientObject)object; + setClientObject((ClientObject)object); sessionWillStart(); sendBootstrap(); } @@ -66,19 +83,13 @@ public class CherClient implements Subscriber, MessageHandler } /** - * Returns the username with which this client instance is associated. + * Called when we receive our client object from the dobjmgr. We go + * through this synchronized method to ensure that the client object + * is visible to the conmgr. */ - public String getUsername () + protected synchronized void setClientObject (ClientObject clobj) { - return _username; - } - - /** - * Returns the client object that is associated with this client. - */ - public ClientObject getClientObject () - { - return _clobj; + _clobj = clobj; } /** @@ -86,7 +97,7 @@ public class CherClient implements Subscriber, MessageHandler * authenticates as this already established client. This must only be * called from the congmr thread. */ - public void resumeSession (Connection conn) + protected void resumeSession (Connection conn) { Connection oldconn = getConnection(); @@ -103,6 +114,18 @@ public class CherClient implements Subscriber, MessageHandler // start using the new connection setConnection(conn); + // we need to queue up an event so that we can finalize the + // resumption of the session on the dobjmgr thread + CherServer.omgr.postEvent(new ResumeSessionEvent(_clobj.getOid())); + } + + /** + * This is called from the dobjmgr thread to complete the session + * resumption. We call some call backs and send the bootstrap info to + * the client. + */ + protected void finishResumeSession () + { // let derived classes do any session resuming sessionWillResume(); @@ -118,8 +141,12 @@ public class CherClient implements Subscriber, MessageHandler * has been created at this point and after this method is executed, * the bootstrap information will be sent to the client which will * trigger the start of the session. Derived classes that override - * this function should be sure to call - * super.sessionWillStart(). + * this method should be sure to call + * super.sessionWillStart. + * + *

Note: This function will be called on the dobjmgr + * thread which means that object manipulations are OK, but client + * instance manipulations must done carefully. */ protected void sessionWillStart () { @@ -130,13 +157,30 @@ public class CherClient implements Subscriber, MessageHandler * and reconnected). After this method is executed, the bootstrap * information will be sent to the client which will trigger the * resumption of the session. Derived classes that override this - * function should be sure to call - * super.sessionWillResume(). + * method should be sure to call super.sessionWillResume. + * + *

Note: This function will be called on the dobjmgr + * thread which means that object manipulations are OK, but client + * instance manipulations must done carefully. */ protected void sessionWillResume () { } + /** + * Called when the client session ends (either because the client + * logged off or because the server forcibly terminated the session). + * Derived classes that override this method should be sure to call + * super.sessionDidTerminate. + * + *

Note: This function will be called on the dobjmgr + * thread which means that object manipulations are OK, but client + * instance manipulations must done carefully. + */ + protected void sessionDidTerminate () + { + } + /** * This is called once we have a handle on the client distributed * object. It sends a bootstrap notification to the client with all @@ -167,8 +211,12 @@ public class CherClient implements Subscriber, MessageHandler /** * Derived client classes can override this member to populate the * bootstrap data with additional information. They should be sure to - * call super.populateBootstrapData() before doing their + * call super.populateBootstrapData before doing their * own populating, however. + * + *

Note: This function will be called on the dobjmgr + * thread which means that object manipulations are OK, but client + * instance manipulations must done carefully. */ protected void populateBootstrapData (BootstrapData data) { @@ -184,12 +232,17 @@ public class CherClient implements Subscriber, MessageHandler * fails. This is invoked on the conmgr thread, and should behave * accordingly. */ - public void connectionFailed (IOException fault) + protected void connectionFailed (IOException fault) { // clear out our connection reference setConnection(null); } + /** + * Sets our connection reference in a thread safe way. Also + * establishes the back reference to us as the connection's message + * handler. + */ protected synchronized void setConnection (Connection conn) { // keep a handle to the new connection @@ -260,17 +313,24 @@ public class CherClient implements Subscriber, MessageHandler // documentation inherited from interface public boolean handleEvent (DEvent event, DObject target) { - // forward the event to the client - Connection conn = getConnection(); - if (conn != null) { - // only typed events will be forwarded to the client, so we - // need not worry that a non-typed event would make it here - TypedEvent tevent = (TypedEvent)event; - conn.postMessage(new EventNotification(tevent)); + if (event instanceof ResumeSessionEvent) { + finishResumeSession(); + } else { - Log.info("Dropped event forward notification " + - "[client=" + this + ", event=" + event + "]."); + // forward the event to the client + Connection conn = getConnection(); + if (conn != null) { + // only typed events will be forwarded to the client, so + // we need not worry that a non-typed event would make it + // here + TypedEvent tevent = (TypedEvent)event; + conn.postMessage(new EventNotification(tevent)); + } else { + Log.info("Dropped event forward notification " + + "[client=" + this + ", event=" + event + "]."); + } } + return true; } @@ -386,6 +446,24 @@ public class CherClient implements Subscriber, MessageHandler } } + /** + * Used to inject ourselves onto the dobjmgr thread. + */ + protected static class ResumeSessionEvent extends DEvent + { + public ResumeSessionEvent (int oid) + { + super(oid); + } + + public boolean applyToObject (DObject target) + throws ObjectAccessException + { + // nothing to do here and don't tell our subscribers + return false; + } + } + protected ClientManager _cmgr; protected String _username; protected Connection _conn;