From 7379cf09dba57379558b47ae2eb0d7d7b9bfcf79 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 7 Sep 2006 01:56:41 +0000 Subject: [PATCH] Communicate the client's timezone to the server during the authentication process and keep that information around in case the server ever wants to know. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4356 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/presents/net/AuthRequest.java | 14 + .../presents/server/ClientManager.java | 2 +- .../presents/server/PresentsClient.java | 394 +++++++++--------- 3 files changed, 217 insertions(+), 193 deletions(-) diff --git a/src/java/com/threerings/presents/net/AuthRequest.java b/src/java/com/threerings/presents/net/AuthRequest.java index d08772181..bf7ba3d2c 100644 --- a/src/java/com/threerings/presents/net/AuthRequest.java +++ b/src/java/com/threerings/presents/net/AuthRequest.java @@ -21,6 +21,8 @@ package com.threerings.presents.net; +import java.util.TimeZone; + public class AuthRequest extends UpstreamMessage { /** @@ -39,6 +41,7 @@ public class AuthRequest extends UpstreamMessage { _creds = creds; _version = version; + _zone = TimeZone.getDefault().getID(); } /** @@ -58,6 +61,14 @@ public class AuthRequest extends UpstreamMessage return _version; } + /** + * Returns the timezone in which this client is operating. + */ + public TimeZone getTimeZone () + { + return TimeZone.getTimeZone(_zone); + } + /** * Generates a string representation of this instance. */ @@ -72,4 +83,7 @@ public class AuthRequest extends UpstreamMessage /** The version information associated with the client code. */ protected String _version; + + /** The timezone in which this client is operating. */ + protected String _zone; } diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java index d2ad75fd7..77896bca1 100644 --- a/src/java/com/threerings/presents/server/ClientManager.java +++ b/src/java/com/threerings/presents/server/ClientManager.java @@ -349,7 +349,7 @@ public class ClientManager ", conn=" + conn + "]."); // create a new client and stick'em in the table client = _factory.createClient(req); - client.startSession(this, creds, conn, rsp.authdata); + client.startSession(this, req, conn, rsp.authdata); // map their client instance _usermap.put(username, client); diff --git a/src/java/com/threerings/presents/server/PresentsClient.java b/src/java/com/threerings/presents/server/PresentsClient.java index ab58bcd31..6401f233f 100644 --- a/src/java/com/threerings/presents/server/PresentsClient.java +++ b/src/java/com/threerings/presents/server/PresentsClient.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.net.InetAddress; import java.util.HashMap; import java.util.Iterator; +import java.util.TimeZone; import com.samskivert.util.HashIntMap; import com.samskivert.util.Throttle; @@ -39,6 +40,7 @@ import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.ObjectAccessException; import com.threerings.presents.dobj.ProxySubscriber; +import com.threerings.presents.net.AuthRequest; import com.threerings.presents.net.BootstrapData; import com.threerings.presents.net.BootstrapNotification; import com.threerings.presents.net.Credentials; @@ -105,7 +107,15 @@ public class PresentsClient */ public Credentials getCredentials () { - return _creds; + return _areq.getCredentials(); + } + + /** + * Returns the time zone in which this client is operating. + */ + public TimeZone getTimeZone () + { + return _areq.getTimeZone(); } /** @@ -269,25 +279,6 @@ public class PresentsClient _cmgr.resolveClientObject(username, clr); } - /** - * Called when {@link #setUsername} has been called and the new client - * object is about to be applied to this client. The old client object - * will not yet have been destroyed, so any final events can be sent - * along prior to the new object being put into effect. - */ - protected void clientObjectWillChange ( - ClientObject oldClobj, ClientObject newClobj) - { - } - - /** - * Called after the new client object has been committed to this - * client due to a call to {@link #setUsername}. - */ - protected void clientObjectDidChange (ClientObject newClobj) - { - } - /** * Returns the client object that is associated with this client. */ @@ -297,38 +288,89 @@ public class PresentsClient } /** - * Initializes this client instance with the specified username, - * connection instance and client object and begins a client session. + * Forcibly terminates a client's session. This must be called from + * the dobjmgr thread. */ - protected void startSession ( - ClientManager cmgr, Credentials creds, Connection conn, Object authdata) + public void endSession () { - _cmgr = cmgr; - _creds = creds; - _authdata = authdata; - setConnection(conn); + // queue up a request for our connection to be closed (if we have + // a connection, that is) + Connection conn = getConnection(); + if (conn != null) { + // go ahead and clear out our connection now to prevent + // funniness + setConnection(null); + // have the connection manager close our connection when it is + // next convenient + PresentsServer.conmgr.closeConnection(conn); + } - // obtain our starting username - assignStartingUsername(); + // if we don't have a client object, we failed to resolve in the + // first place, in which case we have to cope as best we can + if (_clobj != null) { + // and clean up after ourselves + try { + sessionDidEnd(); + } catch (Exception e) { + Log.warning("Choked in sessionDidEnd [client=" + this + "]."); + Log.logStackTrace(e); + } - // resolve our client object before we get fully underway - cmgr.resolveClientObject(_username, this); + // release (and destroy) our client object + _cmgr.releaseClientObject(_username); + } - // make a note of our session start time - _sessionStamp = System.currentTimeMillis(); + // let the client manager know that we're audi 5000 + _cmgr.clientSessionDidEnd(this); + + // clear out the client object so that we know the session is over + _clobj = null; } /** - * This is factored out to allow derived classes to use a different - * starting username than the one supplied in the user's credentials. - * Generally one only wants to munge the starting username if the user - * will subsequently choose a "screen name" and it is desirable to - * avoid collision between the authentication user namespace and the - * screen namespace. + * This is called when the server is shut down in the middle of a + * client session. In this circumstance, {@link #endSession} will + * not be called and so any persistent data that might + * normally be flushed at the end of a client's session should likely + * be flushed here. */ - protected void assignStartingUsername () + public void shutdown () { - _username = _creds.getUsername(); + // if the client is connected, we need to fake the computation of + // their final connect time because we won't be closing their + // socket normally + if (getConnection() != null) { + long now = System.currentTimeMillis(); + _connectTime += ((now - _networkStamp) / 1000); + } + } + + /** + * Makes a note that this client is subscribed to this object so that + * we can clean up after ourselves if and when the client goes + * away. This is called by the client internals and needn't be called + * by code outside the client. + */ + public synchronized void mapSubscrip (DObject object) + { + _subscrips.put(object.getOid(), object); + } + + /** + * Makes a note that this client is no longer subscribed to this + * object. The subscription map is used to clean up after the client + * when it goes away. This is called by the client internals and + * needn't be called by code outside the client. + */ + public synchronized void unmapSubscrip (int oid) + { + DObject object = _subscrips.remove(oid); + if (object != null) { + object.removeSubscriber(this); + } else { + Log.warning("Requested to unmap non-existent subscription " + + "[oid=" + oid + "]."); + } } // documentation inherited from interface @@ -356,6 +398,123 @@ public class PresentsClient endSession(); } + // documentation inherited from interface + public void handleMessage (UpstreamMessage message) + { + _messagesIn++; // count 'em up! + + // if the client has been getting crazy with the cheeze whiz, + // stick a fork in them; the 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 + +// ", msg=" + message + "]."); + return; + } else if (_throttle.throttleOp(message.received)) { + Log.warning("Client sent more than 100 messages in 10 seconds, " + + "forcing disconnect " + this + "."); + safeEndSession(); + _throttle = null; + } + + // we dispatch to a message dispatcher that is specialized for the + // particular class of message that we received + MessageDispatcher disp = _disps.get(message.getClass()); + if (disp == null) { + Log.warning("No dispatcher for message [msg=" + message + "]."); + return; + } + + // otherwise pass the message on to the dispatcher + disp.dispatch(this, message); + } + + // documentation inherited from interface + public void objectAvailable (DObject object) + { + if (postMessage(new ObjectResponse(object))) { + // make a note of this new subscription + mapSubscrip(object); + } else { + // if we failed to send the object response, unsubscribe + object.removeSubscriber(this); + } + } + + // documentation inherited from interface + public void requestFailed (int oid, ObjectAccessException cause) + { + postMessage(new FailureResponse(oid)); + } + + // documentation inherited from interface + public void eventReceived (DEvent event) + { + if (event instanceof PresentsDObjectMgr.AccessObjectEvent) { + Log.warning("Ignoring event that shouldn't be forwarded " + + event + "."); + Thread.dumpStack(); + } else { + postMessage(new EventNotification(event)); + } + } + + /** + * Called when {@link #setUsername} has been called and the new client + * object is about to be applied to this client. The old client object + * will not yet have been destroyed, so any final events can be sent + * along prior to the new object being put into effect. + */ + protected void clientObjectWillChange ( + ClientObject oldClobj, ClientObject newClobj) + { + } + + /** + * Called after the new client object has been committed to this + * client due to a call to {@link #setUsername}. + */ + protected void clientObjectDidChange (ClientObject newClobj) + { + } + + /** + * Initializes this client instance with the specified username, connection + * instance and client object and begins a client session. + */ + protected void startSession ( + ClientManager cmgr, AuthRequest req, Connection conn, Object authdata) + { + _cmgr = cmgr; + _areq = req; + _authdata = authdata; + setConnection(conn); + + // obtain our starting username + assignStartingUsername(); + + // resolve our client object before we get fully underway + cmgr.resolveClientObject(_username, this); + + // make a note of our session start time + _sessionStamp = System.currentTimeMillis(); + } + + /** + * This is factored out to allow derived classes to use a different + * starting username than the one supplied in the user's credentials. + * Generally one only wants to munge the starting username if the user + * will subsequently choose a "screen name" and it is desirable to + * avoid collision between the authentication user namespace and the + * screen namespace. + */ + protected void assignStartingUsername () + { + _username = getCredentials().getUsername(); + } + /** * Called by the client manager when a new connection arrives that * authenticates as this already established client. This must only be @@ -418,46 +577,6 @@ public class PresentsClient Log.info("Session resumed " + this + "."); } - /** - * Forcibly terminates a client's session. This must be called from - * the dobjmgr thread. - */ - public void endSession () - { - // queue up a request for our connection to be closed (if we have - // a connection, that is) - Connection conn = getConnection(); - if (conn != null) { - // go ahead and clear out our connection now to prevent - // funniness - setConnection(null); - // have the connection manager close our connection when it is - // next convenient - PresentsServer.conmgr.closeConnection(conn); - } - - // if we don't have a client object, we failed to resolve in the - // first place, in which case we have to cope as best we can - if (_clobj != null) { - // and clean up after ourselves - try { - sessionDidEnd(); - } catch (Exception e) { - Log.warning("Choked in sessionDidEnd [client=" + this + "]."); - Log.logStackTrace(e); - } - - // release (and destroy) our client object - _cmgr.releaseClientObject(_username); - } - - // let the client manager know that we're audi 5000 - _cmgr.clientSessionDidEnd(this); - - // clear out the client object so that we know the session is over - _clobj = null; - } - /** * Queues up a runnable on the object manager thread where we can * safely end the session. @@ -479,52 +598,6 @@ public class PresentsClient }); } - /** - * This is called when the server is shut down in the middle of a - * client session. In this circumstance, {@link #endSession} will - * not be called and so any persistent data that might - * normally be flushed at the end of a client's session should likely - * be flushed here. - */ - public void shutdown () - { - // if the client is connected, we need to fake the computation of - // their final connect time because we won't be closing their - // socket normally - if (getConnection() != null) { - long now = System.currentTimeMillis(); - _connectTime += ((now - _networkStamp) / 1000); - } - } - - /** - * Makes a note that this client is subscribed to this object so that - * we can clean up after ourselves if and when the client goes - * away. This is called by the client internals and needn't be called - * by code outside the client. - */ - public synchronized void mapSubscrip (DObject object) - { - _subscrips.put(object.getOid(), object); - } - - /** - * Makes a note that this client is no longer subscribed to this - * object. The subscription map is used to clean up after the client - * when it goes away. This is called by the client internals and - * needn't be called by code outside the client. - */ - public synchronized void unmapSubscrip (int oid) - { - DObject object = _subscrips.remove(oid); - if (object != null) { - object.removeSubscriber(this); - } else { - Log.warning("Requested to unmap non-existent subscription " + - "[oid=" + oid + "]."); - } - } - /** * Clears out the tracked client subscriptions. Called when the client * goes away and shouldn't be called otherwise. @@ -731,69 +804,6 @@ public class PresentsClient return _conn; } - // documentation inherited from interface - public void handleMessage (UpstreamMessage message) - { - _messagesIn++; // count 'em up! - - // if the client has been getting crazy with the cheeze whiz, - // stick a fork in them; the 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 + -// ", msg=" + message + "]."); - return; - } else if (_throttle.throttleOp(message.received)) { - Log.warning("Client sent more than 100 messages in 10 seconds, " + - "forcing disconnect " + this + "."); - safeEndSession(); - _throttle = null; - } - - // we dispatch to a message dispatcher that is specialized for the - // particular class of message that we received - MessageDispatcher disp = _disps.get(message.getClass()); - if (disp == null) { - Log.warning("No dispatcher for message [msg=" + message + "]."); - return; - } - - // otherwise pass the message on to the dispatcher - disp.dispatch(this, message); - } - - // documentation inherited from interface - public void objectAvailable (DObject object) - { - if (postMessage(new ObjectResponse(object))) { - // make a note of this new subscription - mapSubscrip(object); - } else { - // if we failed to send the object response, unsubscribe - object.removeSubscriber(this); - } - } - - // documentation inherited from interface - public void requestFailed (int oid, ObjectAccessException cause) - { - postMessage(new FailureResponse(oid)); - } - - // documentation inherited from interface - public void eventReceived (DEvent event) - { - if (event instanceof PresentsDObjectMgr.AccessObjectEvent) { - Log.warning("Ignoring event that shouldn't be forwarded " + - event + "."); - Thread.dumpStack(); - } else { - postMessage(new EventNotification(event)); - } - } - /** Callable from non-dobjmgr thread, this queues up a runnable on the * dobjmgr thread to post the supplied message to this client. */ protected final void safePostMessage (final DownstreamMessage msg) @@ -954,7 +964,7 @@ public class PresentsClient } protected ClientManager _cmgr; - protected Credentials _creds; + protected AuthRequest _areq; protected Object _authdata; protected Name _username; protected Connection _conn;