Doris the refactorasaurus. Restructured the whole client object resolution

process so that it can be done by other entities than just the client
management services. Coordination between these parties is managed so that
no toes are stepped on in the course of loading and unloading clients and
everything is generally much nicer.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1086 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2002-03-05 05:33:25 +00:00
parent 38ca708a4f
commit 9e16e87a69
13 changed files with 456 additions and 228 deletions
@@ -1,45 +1,16 @@
//
// $Id: CrowdClient.java,v 1.7 2002/02/20 23:35:42 mdb Exp $
// $Id: CrowdClient.java,v 1.8 2002/03/05 05:33:25 mdb Exp $
package com.threerings.crowd.server;
import com.threerings.presents.server.PresentsClient;
import com.threerings.crowd.data.BodyObject;
/**
* The crowd client extends the presents client and does some
* initializations necessary for the crowd services.
* The crowd client extends the presents client but doesn't really do
* anything at present. It exists mainly so that implementation systems
* will extend it and ensure that we have the option of adding
* functionality here in the future.
*/
public class CrowdClient extends PresentsClient
{
protected void sessionWillStart (Object authInfo)
{
super.sessionWillStart(authInfo);
// cast our client object to a body object
_bodobj = (BodyObject)_clobj;
// and configure our username
_bodobj.setUsername(_username);
// register our body object mapping
CrowdServer.mapBody(_username, _bodobj);
}
protected void sessionWillResume (Object authInfo)
{
super.sessionWillResume(authInfo);
// nothing to do here presently
}
protected void sessionDidTerminate ()
{
super.sessionDidTerminate();
// unregister our body object mapping
CrowdServer.unmapBody(_username);
}
protected BodyObject _bodobj;
}
@@ -0,0 +1,29 @@
//
// $Id: CrowdClientResolver.java,v 1.1 2002/03/05 05:33:25 mdb Exp $
package com.threerings.crowd.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.ClientResolver;
import com.threerings.crowd.data.BodyObject;
/**
* Used to configure crowd-specific client object data.
*/
public class CrowdClientResolver extends ClientResolver
{
// documentation inherited
public Class getClientObjectClass ()
{
return BodyObject.class;
}
// documentation inherited
protected void resolveClientData (ClientObject clobj)
throws Exception
{
// just fill in the username
((BodyObject)clobj).username = _username;
}
}
@@ -1,5 +1,5 @@
//
// $Id: CrowdServer.java,v 1.9 2001/12/04 01:02:59 mdb Exp $
// $Id: CrowdServer.java,v 1.10 2002/03/05 05:33:25 mdb Exp $
package com.threerings.crowd.server;
@@ -34,11 +34,11 @@ public class CrowdServer extends PresentsServer
// bind the crowd server config into the namespace
config.bindProperties(CONFIG_KEY, CONFIG_PATH, true);
// configure the client to use our crowd client
// configure the client manager to use our client
clmgr.setClientClass(CrowdClient.class);
// configure the client to use the body object
clmgr.setClientObjectClass(BodyObject.class);
// configure the client manager to use our resolver
clmgr.setClientResolverClass(CrowdClientResolver.class);
// create our place registry
plreg = new PlaceRegistry(config, invmgr, omgr);
@@ -50,31 +50,13 @@ public class CrowdServer extends PresentsServer
}
/**
* The crowd server maintains a mapping of username to body object for
* all active users on the server. This should only be called from the
* The server maintains a mapping of username to body object for all
* active users on the server. This should only be called from the
* dobjmgr thread.
*/
public static BodyObject lookupBody (String username)
{
return (BodyObject)_bodymap.get(username);
}
/**
* Called by the crowd client to map a username to a particular body
* object. This should only be called from the dobjmgr thread.
*/
protected static void mapBody (String username, BodyObject bodobj)
{
_bodymap.put(username, bodobj);
}
/**
* Called by the crowd client to unmap a username from a particular
* body object. This should only be called from the dobjmgr thread.
*/
protected static void unmapBody (String username)
{
_bodymap.remove(username);
return (BodyObject)clmgr.getClientObject(username);
}
public static void main (String[] args)
@@ -1,5 +1,5 @@
//
// $Id: SimpleServer.java,v 1.2 2002/02/05 22:57:10 mdb Exp $
// $Id: SimpleServer.java,v 1.3 2002/03/05 05:33:25 mdb Exp $
package com.threerings.micasa.simulator.server;
@@ -23,10 +23,4 @@ public class SimpleServer extends CrowdServer
SimulatorManager simmgr = new SimulatorManager();
simmgr.init(config, invmgr, plreg, clmgr, omgr, this);
}
// documentation inherited
public void fakeBodyMapping (String username, BodyObject bodobj)
{
_bodymap.put(username, bodobj);
}
}
@@ -1,5 +1,5 @@
//
// $Id: SimulatorManager.java,v 1.6 2002/02/05 22:12:42 mdb Exp $
// $Id: SimulatorManager.java,v 1.7 2002/03/05 05:33:25 mdb Exp $
package com.threerings.micasa.simulator.server;
@@ -7,12 +7,10 @@ import java.util.ArrayList;
import com.samskivert.util.Config;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.server.ClientManager;
import com.threerings.presents.server.ClientResolutionListener;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.InvocationProvider;
@@ -116,20 +114,13 @@ public class SimulatorManager
// cast the place to the game object for the game we're creating
_gobj = (GameObject)place;
// create a subscriber to await creation of each simulant body
// object
Subscriber sub = new Subscriber() {
public void objectAvailable (DObject object)
// resolve the simulant body objects
ClientResolutionListener listener = new ClientResolutionListener()
{
public void clientResolved (String username, ClientObject clobj)
{
// set up the simulant's body object
BodyObject bobj = (BodyObject)object;
bobj.username = "simulant" + (_sims.size() + 1);
// map the simulant into the server body set
_simserv.fakeBodyMapping(bobj.username, bobj);
// hold onto it for later game creation
_sims.add(bobj);
// hold onto the body object for later game creation
_sims.add(clobj);
// create the game if we've received all body objects
if (_sims.size() == (_playerCount - 1)) {
@@ -137,18 +128,17 @@ public class SimulatorManager
}
}
public void requestFailed (
int oid, ObjectAccessException cause)
public void resolutionFailed (String username, Exception cause)
{
Log.warning("Unable to create simulant body object " +
"[error=" + cause + "].");
}
};
// fire off simulant body object creation requests
Class simobjClass = _clmgr.getClientObjectClass();
// resolve client objects for all of our simulants
for (int ii = 1; ii < _playerCount; ii++) {
_omgr.createObject(simobjClass, sub);
String username = "simulant" + (_sims.size() + 1);
_clmgr.resolveClientObject(username, listener);
}
}
@@ -1,5 +1,5 @@
//
// $Id: SimulatorServer.java,v 1.5 2002/02/05 22:12:42 mdb Exp $
// $Id: SimulatorServer.java,v 1.6 2002/03/05 05:33:25 mdb Exp $
package com.threerings.micasa.simulator.server;
@@ -25,10 +25,4 @@ public interface SimulatorServer
* primary business.
*/
public void run ();
/**
* Called by the simulator manager to map a username to a particular
* body object. This should only be called from the dobjmgr thread.
*/
public void fakeBodyMapping (String username, BodyObject bodobj);
}
@@ -1,11 +1,14 @@
//
// $Id: Authenticator.java,v 1.4 2002/03/05 03:19:18 mdb Exp $
// $Id: Authenticator.java,v 1.5 2002/03/05 05:33:25 mdb Exp $
package com.threerings.presents.server.net;
package com.threerings.presents.server;
import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.AuthResponseData;
import com.threerings.presents.server.net.AuthingConnection;
import com.threerings.presents.server.net.ConnectionManager;
/**
* The authenticator is a pluggable component of the authentication
* framework. The base class handles the basic mechanics of authentication
@@ -1,5 +1,5 @@
//
// $Id: ClientManager.java,v 1.13 2001/12/03 22:01:57 mdb Exp $
// $Id: ClientManager.java,v 1.14 2002/03/05 05:33:25 mdb Exp $
package com.threerings.presents.server;
@@ -26,6 +26,10 @@ import com.threerings.presents.server.net.*;
*/
public class ClientManager implements ConnectionObserver
{
/**
* Constructs a client manager that will interact with the supplied
* connection manager.
*/
public ClientManager (ConnectionManager conmgr)
{
// register ourselves as a connection observer
@@ -34,10 +38,8 @@ public class ClientManager implements ConnectionObserver
/**
* Instructs the client manager to construct instances of this derived
* class of <code>PresentsClient</code> to managed newly accepted client
* class of {@link PresentsClient} to managed newly accepted client
* connections.
*
* @see PresentsClient
*/
public void setClientClass (Class clientClass)
{
@@ -54,35 +56,103 @@ public class ClientManager implements ConnectionObserver
}
/**
* Instructs the client to create an instance of this
* <code>ClientObject</code> derived class when creating the
* distributed object that corresponds to a particular client session.
*
* @see com.threerings.presents.data.ClientObject
* Instructs the client to use instances of this {@link
* ClientResolver} derived class when resolving clients in preparation
* for starting a client session.
*/
public void setClientObjectClass (Class clobjClass)
public void setClientResolverClass (Class clrClass)
{
// sanity check
if (!ClientObject.class.isAssignableFrom(clobjClass)) {
Log.warning("Requested to use client object class that does " +
"not derive from ClientObject " +
"[class=" + clobjClass.getName() + "].");
return;
}
if (!ClientResolver.class.isAssignableFrom(clrClass)) {
Log.warning("Requested to use client resolver class that does " +
"not derive from ClientResolver " +
"[class=" + clrClass.getName() + "].");
// make a note of it
_clobjClass = clobjClass;
} else {
// make a note of it
_clrClass = clrClass;
}
}
/**
* Returns the class that should be used when creating a distributed
* object to accompany a particular client session. In general, this
* is only used by the <code>PresentsClient</code> object when it is
* setting up a client's session for the first time.
* Returns the client object associated with the specified username.
* This will return null unless the client object is resolved for some
* reason (like they are logged on).
*/
public Class getClientObjectClass ()
public ClientObject getClientObject (String username)
{
return _clobjClass;
return (ClientObject)_objmap.get(username);
}
/**
* Requests that the client object for the specified user be resolved.
* If the client object is already resolved, the request will be
* processed immediately, otherwise the appropriate client object will
* be instantiated and populated by the registered client resolver
* (which may involve talking to databases).
*/
public synchronized void resolveClientObject (
String username, ClientResolutionListener listener)
{
// look to see if the client object is already resolved
ClientObject clobj = (ClientObject)_objmap.get(username);
if (clobj != null) {
listener.clientResolved(username, clobj);
return;
}
// look to see if it's currently being resolved
ClientResolver clr = (ClientResolver)_penders.get(username);
if (clr != null) {
// throw this guy onto the bandwagon
clr.addResolutionListener(listener);
return;
}
try {
// create a client resolver instance which will create our
// client object, populate it and notify the listeners
clr = (ClientResolver)_clrClass.newInstance();
clr.init(username);
clr.addResolutionListener(listener);
// request that the appropriate client object be created by
// the dobject manager which starts the whole business off
PresentsServer.omgr.createObject(clr.getClientObjectClass(), clr);
} catch (Exception e) {
// let the listener know that we're hosed
listener.resolutionFailed(username, e);
}
}
/**
* Called by the {@link ClientResolver} once a client object has been
* resolved.
*/
protected synchronized void mapClientObject (
String username, ClientObject clobj)
{
// stuff the object into the mapping table
_objmap.put(username, clobj);
// and remove the resolution listener
_penders.remove(username);
}
/**
* If an entity resolves a client object outside the scope of a normal
* client session, it should call this to unmap the client object when
* it's finished. This won't actually unmap the client if someone came
* along and started a session for that client in the meanwhile.
*/
public synchronized void unmapClientObject (String username)
{
// we only remove the mapping if there's not a session in progress
// (which is indicated by a mapping in the usermap table)
if (!_usermap.containsKey(username)) {
_objmap.remove(username);
}
}
// documentation inherited
@@ -98,7 +168,7 @@ public class ClientManager implements ConnectionObserver
if (client != null) {
Log.info("Session resumed [username=" + username +
", conn=" + conn + "].");
client.resumeSession(conn, rsp.getAuthInfo());
client.resumeSession(conn);
} else {
Log.info("Session initiated [username=" + username +
@@ -106,8 +176,9 @@ public class ClientManager implements ConnectionObserver
// create a new client and stick'em in the table
try {
client = (PresentsClient)_clientClass.newInstance();
client.startSession(this, username, conn, rsp.getAuthInfo());
client.startSession(this, username, conn);
_usermap.put(username, client);
} catch (Exception e) {
Log.warning("Failed to instantiate client instance to " +
"manage new client connection " +
@@ -162,26 +233,39 @@ public class ClientManager implements ConnectionObserver
*/
synchronized void clientDidEndSession (PresentsClient client)
{
String username = client.getUsername();
// remove the client from the username map
PresentsClient rc = (PresentsClient)_usermap.remove(client.getUsername());
PresentsClient rc = (PresentsClient)_usermap.remove(username);
// and the client object mapping as well
_objmap.remove(username);
// sanity check because we can
// sanity check just because we can
if (rc == null) {
Log.warning("Unregistered client ended session " +
"[client=" + client + "].");
} else if (rc != client) {
Log.warning("Different clients with same username!? " +
"[c1=" + rc + ", c2=" + client + "].");
} else {
Log.info("Ending session [client=" + client + "].");
}
}
/** A mapping from usernames to client instances. */
protected HashMap _usermap = new HashMap();
/** A mapping from connections to client instances. */
protected HashMap _conmap = new HashMap();
/** A mapping from usernames to client object instances. */
protected HashMap _objmap = new HashMap();
/** A mapping of pending client resolvers. */
protected HashMap _penders = new HashMap();
/** The client class in use. */
protected Class _clientClass = PresentsClient.class;
protected Class _clobjClass = ClientObject.class;
/** The client resolver class in use. */
protected Class _clrClass = ClientResolver.class;
}
@@ -0,0 +1,25 @@
//
// $Id: ClientResolutionListener.java,v 1.1 2002/03/05 05:33:25 mdb Exp $
package com.threerings.presents.server;
import com.threerings.presents.data.ClientObject;
/**
* Entites that wish to resolve client objects must implement this
* interface so as to partake in the asynchronous process of client
* object resolution.
*/
public interface ClientResolutionListener
{
/**
* Called when resolution completed successfully.
*/
public void clientResolved (String username, ClientObject clobj);
/**
* Called when resolution fails.
*/
public void resolutionFailed (String username, Exception reason);
}
@@ -0,0 +1,158 @@
//
// $Id: ClientResolver.java,v 1.1 2002/03/05 05:33:25 mdb Exp $
package com.threerings.presents.server;
import java.util.ArrayList;
import com.threerings.presents.Log;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.presents.util.Invoker;
/**
* Used to resolve client data when a user starts a session (or when some
* other entity needs access to a client object). Implementations will
* want to extend this class and override {@link #resolveClientData},
* making the necessary database calls and populating the client object
* appropriately.
*/
public class ClientResolver extends Invoker.Unit
implements Subscriber
{
/**
* Initiailizes this instance.
*
* @param username the username of the user to be resolved.
*/
public void init (String username)
{
_username = username;
}
/**
* Adds a resolution listener to this active resolver.
*/
public void addResolutionListener (ClientResolutionListener listener)
{
_listeners.add(listener);
}
/**
* Returns the {@link ClientObject} derived class that should be
* created to kick off the resolution process.
*/
public Class getClientObjectClass ()
{
return ClientObject.class;
}
// documentation inherited
public void objectAvailable (DObject object)
{
// we've got our object, so shunt ourselves over to the invoker
// thread to perform database loading
_clobj = (ClientObject)object;
PresentsServer.invoker.postUnit(this);
}
// documentation inherited
public void requestFailed (int oid, ObjectAccessException cause)
{
// pass the buck
reportFailure(cause);
}
// documentation inherited
public boolean invoke ()
{
try {
// allow our derived class to do its database loads
resolveClientData(_clobj);
} catch (Exception cause) {
// keep this around until we're back on the dobj thread
_failure = cause;
}
return true;
}
// documentation inherited
public void handleResult ()
{
// if we failed in invoke() report it here
if (_failure != null) {
// destroy the dangling user object
PresentsServer.omgr.destroyObject(_clobj.getOid());
// let our listener know that we're hosed
reportFailure(_failure);
} else {
// otherwise let the client manager know that we're all clear
PresentsServer.clmgr.mapClientObject(_username, _clobj);
// and let the listeners in on the secret as well
int lcount = _listeners.size();
for (int i = 0; i < lcount; i++) {
ClientResolutionListener crl = (ClientResolutionListener)
_listeners.get(i);
try {
crl.clientResolved(_username, _clobj);
} catch (Exception e) {
Log.warning("Client resolution listener choked during " +
"resolution notification [crl=" + crl +
", clobj=" + _clobj + "].");
Log.logStackTrace(e);
}
}
}
}
/**
* This method is called on the invoker thread which means that it can
* do things like blocking database requests and generally whatever is
* necessary to load up all the client data that is desired by the
* implentation system. Any exceptions that are thrown will be caught
* and reported as a failure to the client resolution listener.
*/
protected void resolveClientData (ClientObject clobj)
throws Exception
{
// nothing to do by default
}
/**
* Reports failure to our resolution listeners.
*/
protected void reportFailure (Exception cause)
{
int lcount = _listeners.size();
for (int i = 0; i < lcount; i++) {
ClientResolutionListener crl = (ClientResolutionListener)
_listeners.get(i);
try {
crl.resolutionFailed(_username, cause);
} catch (Exception e) {
Log.warning("Client resolution listener choked during " +
"failure notification [crl=" + crl +
", username=" + _username +
", cause=" + cause + "].");
Log.logStackTrace(e);
}
}
}
/** The name of the user whose client object is being resolved. */
protected String _username;
/** The entities to notify of success or failure. */
protected ArrayList _listeners = new ArrayList();
/** The resolving client object. */
protected ClientObject _clobj;
/** A place to keep an exception around for a moment. */
protected Exception _failure;
}
@@ -1,12 +1,11 @@
//
// $Id: DummyAuthenticator.java,v 1.5 2002/03/05 03:19:18 mdb Exp $
// $Id: DummyAuthenticator.java,v 1.6 2002/03/05 05:33:25 mdb Exp $
package com.threerings.presents.server;
import com.threerings.presents.Log;
import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.AuthResponseData;
import com.threerings.presents.server.net.Authenticator;
import com.threerings.presents.server.net.AuthingConnection;
/**
@@ -1,5 +1,5 @@
//
// $Id: PresentsClient.java,v 1.27 2001/12/13 05:32:28 mdb Exp $
// $Id: PresentsClient.java,v 1.28 2002/03/05 05:33:25 mdb Exp $
package com.threerings.presents.server;
@@ -52,7 +52,8 @@ import com.threerings.presents.server.net.MessageHandler;
* conmgr thread and therefore also need not be synchronized.
*/
public class PresentsClient
implements Subscriber, EventListener, MessageHandler
implements Subscriber, EventListener, MessageHandler,
ClientResolutionListener
{
/**
* Returns the username with which this client instance is associated.
@@ -71,47 +72,40 @@ public class PresentsClient
}
/**
* Initializes this client instance with the specified username and
* connection instance and begins a client session.
* Initializes this client instance with the specified username,
* connection instance and client object and begins a client session.
*/
protected void startSession (
ClientManager cmgr, String username, Connection conn,
final Object authInfo)
ClientManager cmgr, String username, Connection conn)
{
_cmgr = cmgr;
_username = username;
setConnection(conn);
// create our client object and bootstrap the client once we've
// got it
Subscriber sub = new Subscriber ()
{
public void objectAvailable (DObject object)
{
setClientObject((ClientObject)object);
sessionWillStart(authInfo);
sendBootstrap();
}
public void requestFailed (int oid, ObjectAccessException cause)
{
Log.warning("Unable to create client object " +
"[client=" + PresentsClient.this +
", error=" + cause + "].");
}
};
Class clobjClass = _cmgr.getClientObjectClass();
PresentsServer.omgr.createObject(clobjClass, sub);
// resolve our client object before we get fully underway
cmgr.resolveClientObject(username, this);
}
/**
* 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.
*/
protected synchronized void setClientObject (ClientObject clobj)
// documentation inherited from interface
public void clientResolved (String username, ClientObject clobj)
{
// we'll be keeping this bad boy
_clobj = clobj;
// finish up our regular business
sessionWillStart();
sendBootstrap();
}
// documentation inherited from interface
public void resolutionFailed (String username, Exception reason)
{
// urk; nothing to do but complain and get the f**k out of dodge
Log.warning("Unable to resolve client [username=" + username + "].");
Log.logStackTrace(reason);
// end the session now to prevent danglage
endSession();
}
/**
@@ -119,7 +113,7 @@ public class PresentsClient
* authenticates as this already established client. This must only be
* called from the congmr thread.
*/
protected void resumeSession (Connection conn, final Object authInfo)
protected void resumeSession (Connection conn)
{
Connection oldconn = getConnection();
@@ -145,7 +139,7 @@ public class PresentsClient
{
// now that we're on the dobjmgr thread we can resume our
// session resumption
finishResumeSession(authInfo);
finishResumeSession();
return false;
}
};
@@ -157,10 +151,10 @@ public class PresentsClient
* resumption. We call some call backs and send the bootstrap info to
* the client.
*/
protected void finishResumeSession (Object authInfo)
protected void finishResumeSession ()
{
// let derived classes do any session resuming
sessionWillResume(authInfo);
sessionWillResume();
// send off a bootstrap notification immediately because we've
// already got our client object
@@ -170,38 +164,20 @@ public class PresentsClient
}
/**
* Requests that the client session be terminated. This is normally
* invoked as a result of a logoff request by the client. It should
* only be invoked from the conmgr thread.
* Forcibly terminates a client's session. This must be called from
* the dobjmgr thread.
*/
public void endSession ()
{
// close our connection (which results in everything being
// properly unregistered)
// queue up a request for our connection to be closed (if we have
// a connection, that is)
Connection conn = getConnection();
if (conn != null) {
conn.close();
} else {
Log.info("Unable to close connection for logoff request " +
"[client=" + this + "].");
PresentsServer.conmgr.closeConnection(conn);
}
// then let the client manager know what's up
_cmgr.clientDidEndSession(this);
// we need to get onto the distributed object thread so that we
// can finalize the ending of the session. we do so by posting a
// special event
DEvent event = new DEvent(0)
{
public boolean applyToObject (DObject target)
{
// now that we're on the dobjmgr thread we can call into
// our derived classes to finish the session termination
sessionDidTerminate();
return false;
}
};
PresentsServer.omgr.postEvent(event);
// and clean up after ourselves
sessionDidEnd();
}
/**
@@ -260,11 +236,8 @@ public class PresentsClient
* <p><em>Note:</em> This function will be called on the dobjmgr
* thread which means that object manipulations are OK, but client
* instance manipulations must done carefully.
*
* @param authInfo optional information provided by the authenticator
* that it obtained during the authentication phase.
*/
protected void sessionWillStart (Object authInfo)
protected void sessionWillStart ()
{
}
@@ -278,11 +251,8 @@ public class PresentsClient
* <p><em>Note:</em> This function will be called on the dobjmgr
* thread which means that object manipulations are OK, but client
* instance manipulations must done carefully.
*
* @param authInfo optional information provided by the authenticator
* that it obtained during the authentication phase.
*/
protected void sessionWillResume (Object authInfo)
protected void sessionWillResume ()
{
}
@@ -290,14 +260,17 @@ public class PresentsClient
* 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
* <code>super.sessionDidTerminate</code>.
* <code>super.sessionDidEnd</code>.
*
* <p><em>Note:</em> 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 ()
protected void sessionDidEnd ()
{
// then let the client manager know what's up
_cmgr.clientDidEndSession(this);
// destroy the client object
PresentsServer.omgr.destroyObject(_clobj.getOid());
}
@@ -363,20 +336,15 @@ public class PresentsClient
setConnection(null);
// clear out our subscriptions. we need to do this on the dobjmgr
// thread. so we do so by posting a special event. it is important
// that we do this *after* we clear out our connection reference.
// once the connection ref is null, no more subscriptions will be
// processed (even those that were queued up before the connection
// went away)
DEvent event = new DEvent(0)
{
public boolean applyToObject (DObject target)
{
// thread. it is important that we do this *after* we clear out
// our connection reference. once the connection ref is null, no
// more subscriptions will be processed (even those that were
// queued up before the connection went away)
PresentsServer.omgr.postUnit(new Runnable() {
public void run () {
clearSubscrips();
return false;
}
};
PresentsServer.omgr.postEvent(event);
});
}
/**
@@ -595,15 +563,26 @@ public class PresentsClient
/**
* Processes logoff requests.
*/
protected static class LogoffDispatcher implements MessageDispatcher
protected static class LogoffDispatcher
implements MessageDispatcher, Runnable
{
public void dispatch (PresentsClient client, UpstreamMessage msg)
{
Log.info("Client requested logoff " +
"[client=" + client + "].");
// request that the client end the session
client.endSession();
_client = client;
// queue ourselves up to be run on the object manager thread
// where we can safely end the session
PresentsServer.omgr.postUnit(this);
}
public void run ()
{
// end the session in a civilized manner
_client.endSession();
}
protected PresentsClient _client;
}
protected ClientManager _cmgr;
@@ -1,5 +1,5 @@
//
// $Id: ConnectionManager.java,v 1.15 2002/03/05 03:19:18 mdb Exp $
// $Id: ConnectionManager.java,v 1.16 2002/03/05 05:33:25 mdb Exp $
package com.threerings.presents.server.net;
@@ -12,11 +12,15 @@ import com.samskivert.util.*;
import com.threerings.presents.Log;
import com.threerings.presents.client.Client;
import com.threerings.presents.io.FramingOutputStream;
import com.threerings.presents.io.TypedObjectFactory;
import com.threerings.presents.net.AuthRequest;
import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.DownstreamMessage;
import com.threerings.presents.server.Authenticator;
import com.threerings.presents.server.PresentsServer;
/**
@@ -105,6 +109,24 @@ public class ConnectionManager extends LoopingThread
}
}
/**
* Queues a connection up to be closed on the conmgr thread.
*/
public void closeConnection (Connection conn)
{
_deathq.append(conn);
}
/**
* Called by the authenticator to indicate that a connection was
* successfully authenticated.
*/
public void connectionDidAuthenticate (Connection conn)
{
// slap this sucker onto the authenticated connections queue
_authq.append(conn);
}
/**
* Notifies the connection observers of a connection event. Used
* internally.
@@ -146,6 +168,12 @@ public class ConnectionManager extends LoopingThread
*/
protected void iterate ()
{
// close any connections that have been queued up to die
Connection dconn;
while ((dconn = (Connection)_deathq.getNonBlocking()) != null) {
dconn.close();
}
// send any messages that are waiting on the outgoing queue
Tuple tup;
while ((tup = (Tuple)_outq.getNonBlocking()) != null) {
@@ -303,16 +331,6 @@ public class ConnectionManager extends LoopingThread
notifyObservers(CONNECTION_CLOSED, conn, null, null);
}
/**
* Called by the auth manager to indicate that a connection was
* successfully authenticated.
*/
void connectionDidAuthenticate (Connection conn)
{
// slap this sucker onto the authenticated connections queue
_authq.append(conn);
}
protected int _port;
protected Authenticator _author;
protected SelectSet _selset;
@@ -320,6 +338,8 @@ public class ConnectionManager extends LoopingThread
protected NonblockingServerSocket _listener;
protected SelectItem _litem;
protected Queue _deathq = new Queue();
protected Queue _outq = new Queue();
protected FramingOutputStream _framer;
protected DataOutputStream _dout;