Handle the client connecting and disconnecting before client object resolution

completed, or connecting, disconnecting and reconnceting before client object
resolution completes a little more gracefully.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4889 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2007-12-03 18:28:07 +00:00
parent 362e506fd6
commit c935c46612
2 changed files with 45 additions and 44 deletions
@@ -24,7 +24,6 @@ package com.threerings.crowd.server;
import com.threerings.presents.dobj.AccessController;
import com.threerings.presents.server.PresentsClient;
import com.threerings.crowd.Log;
import com.threerings.crowd.chat.server.SpeakUtil;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.OccupantInfo;
@@ -52,14 +51,9 @@ public class CrowdClient extends PresentsClient
{
super.sessionWillResume();
if (_clobj != null) {
// note that the user's active once more
BodyObject bobj = (BodyObject)_clobj;
BodyProvider.updateOccupantStatus(bobj, bobj.location, OccupantInfo.ACTIVE);
} else {
Log.warning("Session resumed but we have no client object!? [client=" + this + "].");
}
// note that the user's active once more
BodyObject bobj = (BodyObject)_clobj;
BodyProvider.updateOccupantStatus(bobj, bobj.location, OccupantInfo.ACTIVE);
}
// documentation inherited
@@ -26,6 +26,7 @@ import java.net.InetAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TimeZone;
import java.util.logging.Level;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.ResultListener;
@@ -33,7 +34,6 @@ import com.samskivert.util.Throttle;
import com.threerings.util.Name;
import com.threerings.util.StreamableArrayList;
import com.threerings.presents.Log;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
@@ -64,6 +64,8 @@ import com.threerings.presents.net.UnsubscribeResponse;
import com.threerings.presents.server.net.Connection;
import com.threerings.presents.server.net.MessageHandler;
import static com.threerings.presents.Log.log;
/**
* Represents a client session in the server. It is associated with a connection instance (while
* the client is connected) and acts as the intermediary for the remote client in terms of passing
@@ -193,7 +195,7 @@ public class PresentsClient
// if they old client object is gone by now, they ended their session while we were
// switching, so freak out
if (_clobj == null) {
Log.warning("Client disappeared before we could complete the switch to a " +
log.warning("Client disappeared before we could complete the switch to a " +
"new client object [ousername=" + _username +
", nusername=" + username + "].");
_cmgr.releaseClientObject(username);
@@ -246,9 +248,8 @@ public class PresentsClient
}
public void resolutionFailed (Name username, Exception reason) {
Log.warning("Unable to resolve new client object [oldname=" + _username +
", newname=" + username + ", reason=" + reason + "].");
Log.logStackTrace(reason);
log.log(Level.WARNING, "Unable to resolve new client object [oldname=" + _username +
", newname=" + username + ", reason=" + reason + "].", reason);
// let our listener know we're hosed
if (ucl != null) {
@@ -290,8 +291,7 @@ public class PresentsClient
try {
sessionDidEnd();
} catch (Exception e) {
Log.warning("Choked in sessionDidEnd [client=" + this + "].");
Log.logStackTrace(e);
log.log(Level.WARNING, "Choked in sessionDidEnd " + this + ".", e);
}
// release (and destroy) our client object
@@ -324,6 +324,13 @@ public class PresentsClient
// from interface ClientResolutionListener
public void clientResolved (Name username, ClientObject clobj)
{
// if our connection was closed while we were resolving our client object, then just
// abandon ship
if (getConnection() == null) {
log.info("Session ended before client object could be resolved: " + this + ".");
return;
}
// we'll be keeping this bad boy
_clobj = clobj;
@@ -339,8 +346,7 @@ public class PresentsClient
public void resolutionFailed (Name 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);
log.log(Level.WARNING, "Unable to resolve client [username=" + username + "].", reason);
// end the session now to prevent danglage
endSession();
@@ -355,7 +361,7 @@ public class PresentsClient
// first time through we end our session, subsequently _throttle is null and we just drop
// any messages that come in until we've fully shutdown
if (_throttle == null) {
// Log.info("Dropping message from force-quit client [conn=" + _conn +
// log.info("Dropping message from force-quit client [conn=" + _conn +
// ", msg=" + message + "].");
return;
@@ -367,7 +373,7 @@ public class PresentsClient
// message that we received
MessageDispatcher disp = _disps.get(message.getClass());
if (disp == null) {
Log.warning("No dispatcher for message [msg=" + message + "].");
log.warning("No dispatcher for message [msg=" + message + "].");
return;
}
@@ -435,7 +441,7 @@ public class PresentsClient
// check to see if we've already got a connection object, in which case it's probably stale
Connection oldconn = getConnection();
if (oldconn != null && !oldconn.isClosed()) {
Log.info("Closing stale connection [old=" + oldconn + ", new=" + conn + "].");
log.info("Closing stale connection [old=" + oldconn + ", new=" + conn + "].");
// close the old connection (which results in everything being properly unregistered)
oldconn.close();
}
@@ -452,8 +458,8 @@ public class PresentsClient
// and let the original session establishment code take care of initializing this resumed
// session
if (_clobj == null) {
Log.warning("Rapid-fire reconnect caused us to arrive in resumeSession() before the " +
"original session resolved its client object? " + this + ".");
log.info("Rapid-fire reconnect caused us to arrive in resumeSession() before the " +
"original session resolved its client object: " + this + ".");
return;
}
@@ -472,13 +478,20 @@ public class PresentsClient
*/
protected void finishResumeSession ()
{
// if we have no client object for whatever reason; we're hosed, shut ourselves down
if (_clobj == null) {
log.info("Missing client object for resuming session. Killing session.");
endSession();
return;
}
// let derived classes do any session resuming
sessionWillResume();
// send off a bootstrap notification immediately as we've already got our client object
sendBootstrap();
Log.info("Session resumed " + this + ".");
log.info("Session resumed " + this + ".");
}
/**
@@ -488,13 +501,7 @@ public class PresentsClient
{
PresentsServer.omgr.postRunnable(new Runnable() {
public void run () {
if (getClientObject() == null) {
// refuse to end the session unless the client is fully resolved
Log.warning("Refusing logoff from still-resolving client " + this + ".");
} else {
// end the session in a civilized manner
endSession();
}
endSession();
}
});
}
@@ -513,7 +520,7 @@ public class PresentsClient
*/
protected void handleThrottleExceeded ()
{
Log.warning("Client sent more than 100 messages in 10 seconds, disconnecting " + this + ".");
log.warning("Client sent more than 100 messages in 10 seconds, disconnecting " + this + ".");
safeEndSession();
_throttle = null;
}
@@ -531,7 +538,7 @@ public class PresentsClient
if (rec != null) {
rec.unsubscribe();
} else {
Log.warning("Requested to unmap non-existent subscription [oid=" + oid + "].");
log.warning("Requested to unmap non-existent subscription [oid=" + oid + "].");
}
}
@@ -543,7 +550,7 @@ public class PresentsClient
{
for (ClientProxy rec : _subscrips.values()) {
if (verbose) {
Log.info("Clearing subscription [client=" + this +
log.info("Clearing subscription [client=" + this +
", obj=" + rec.object.getOid() + "].");
}
rec.unsubscribe();
@@ -615,7 +622,7 @@ public class PresentsClient
*/
protected void sendBootstrap ()
{
// Log.info("Sending bootstrap " + this + ".");
// log.info("Sending bootstrap " + this + ".");
// create and populate our bootstrap data
BootstrapData data = createBootstrapData();
@@ -649,7 +656,7 @@ public class PresentsClient
// fill in the list of bootstrap services
if (_areq.getBootGroups() == null) {
Log.warning("Client provided no invocation service boot groups? " + this);
log.warning("Client provided no invocation service boot groups? " + this);
data.services = new StreamableArrayList<InvocationMarshaller>();
} else {
data.services = PresentsServer.invmgr.getBootstrapServices(_areq.getBootGroups());
@@ -766,13 +773,13 @@ public class PresentsClient
// don't log dropped messages unless we're dropping a lot of them (meaning something is
// still queueing messages up for this dead client even though it shouldn't be)
if (++_messagesDropped % 50 == 0) {
Log.warning("Dropping many messages? [client=" + this +
log.warning("Dropping many messages? [client=" + this +
", count=" + _messagesDropped + "].");
}
// make darned sure we don't have any remaining subscriptions
if (_subscrips.size() > 0) {
// Log.warning("Clearing stale subscriptions [client=" + this +
// log.warning("Clearing stale subscriptions [client=" + this +
// ", subscrips=" + _subscrips.size() + "].");
clearSubscrips(_messagesDropped > 10);
}
@@ -848,7 +855,7 @@ public class PresentsClient
public void eventReceived (DEvent event)
{
if (event instanceof PresentsDObjectMgr.AccessObjectEvent) {
Log.warning("Ignoring event that shouldn't be forwarded " + event + ".");
log.warning("Ignoring event that shouldn't be forwarded " + event + ".");
Thread.dumpStack();
return;
}
@@ -888,7 +895,7 @@ public class PresentsClient
public void dispatch (PresentsClient client, UpstreamMessage msg)
{
SubscribeRequest req = (SubscribeRequest)msg;
// Log.info("Subscribing [client=" + client + ", oid=" + req.getOid() + "].");
// log.info("Subscribing [client=" + client + ", oid=" + req.getOid() + "].");
// forward the subscribe request to the omgr for processing
PresentsServer.omgr.subscribeToObject(req.getOid(), client.createProxySubscriber());
@@ -904,7 +911,7 @@ public class PresentsClient
{
UnsubscribeRequest req = (UnsubscribeRequest)msg;
int oid = req.getOid();
// Log.info("Unsubscribing " + client + " [oid=" + oid + "].");
// log.info("Unsubscribing " + client + " [oid=" + oid + "].");
// unsubscribe from the object and clear out our proxy
client.unmapSubscrip(oid);
@@ -928,14 +935,14 @@ public class PresentsClient
// freak not out if a message arrives from a client just after their session ended
ClientObject clobj = client.getClientObject();
if (clobj == null) {
Log.info("Dropping event that arrived after client disconnected " + fevt + ".");
log.info("Dropping event that arrived after client disconnected " + fevt + ".");
return;
}
// fill in the proper source oid
fevt.setSourceOid(clobj.getOid());
// Log.info("Forwarding event [client=" + client + ", event=" + fevt + "].");
// log.info("Forwarding event [client=" + client + ", event=" + fevt + "].");
// forward the event to the omgr for processing
PresentsServer.omgr.postEvent(fevt);
@@ -962,7 +969,7 @@ public class PresentsClient
{
public void dispatch (final PresentsClient client, UpstreamMessage msg)
{
Log.debug("Client requested logoff " + client + ".");
log.fine("Client requested logoff " + client + ".");
client.safeEndSession();
}
}