Bit the bullet and implemented invocation service groups so that our various
MetaSOY clients (Swiftly, World, and soon Admin Dashboard) don't have to know about a bunch of unrelated crap. Fricking complexity++, grumble. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4551 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -28,12 +28,14 @@ import java.util.Iterator;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.Throttle;
|
||||
import com.samskivert.util.ResultListener;
|
||||
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;
|
||||
|
||||
import com.threerings.presents.dobj.DEvent;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
@@ -63,18 +65,15 @@ import com.threerings.presents.server.net.Connection;
|
||||
import com.threerings.presents.server.net.MessageHandler;
|
||||
|
||||
/**
|
||||
* A client object 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
|
||||
* along events forwarded by the client, ensuring that subscriptions are
|
||||
* maintained on behalf of the client and that events are forwarded to the
|
||||
* client.
|
||||
* 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
|
||||
* along events forwarded by the client, ensuring that subscriptions are maintained on behalf of
|
||||
* the client and that events are forwarded to the client.
|
||||
*
|
||||
* <p><em>A note on synchronization:</em> the client object is structured
|
||||
* so that its <code>Subscriber</code> implementation (which is called
|
||||
* from the dobjmgr thread) can proceed without synchronization. This does
|
||||
* not overlap with its other client duties which are called from the
|
||||
* conmgr thread and therefore also need not be synchronized.
|
||||
* <p><em>A note on synchronization:</em> the client object is structured so that its
|
||||
* <code>Subscriber</code> implementation (which is called from the dobjmgr thread) can proceed
|
||||
* without synchronization. This does not overlap with its other client duties which are called
|
||||
* from the conmgr thread and therefore also need not be synchronized.
|
||||
*/
|
||||
public class PresentsClient
|
||||
implements ProxySubscriber, MessageHandler, ClientResolutionListener
|
||||
@@ -82,23 +81,19 @@ public class PresentsClient
|
||||
/** Used by {@link #setUsername} to report success or failure. */
|
||||
public static interface UserChangeListener
|
||||
{
|
||||
/** Called when the new client object has been resolved and the
|
||||
* new client object reported to the client, but the old one has
|
||||
* not yet been destroyed. Any events delivered on this callback
|
||||
* to the old client object will be delivered.
|
||||
/** Called when the new client object has been resolved and the new client object reported
|
||||
* to the client, but the old one has not yet been destroyed. Any events delivered on this
|
||||
* callback to the old client object will be delivered.
|
||||
*
|
||||
* @param rl when this method is finished with its business and
|
||||
* the old client object can be destroyed, the result listener
|
||||
* should be called.
|
||||
* */
|
||||
* @param rl when this method is finished with its business and the old client object can
|
||||
* be destroyed, the result listener should be called. */
|
||||
public void changeReported (ClientObject newObji, ResultListener rl);
|
||||
|
||||
/** Called when the user change is completed, the old client
|
||||
* object is destroyed and all updates are committed. */
|
||||
/** Called when the user change is completed, the old client object is destroyed and all
|
||||
* updates are committed. */
|
||||
public void changeCompleted (ClientObject newObj);
|
||||
|
||||
/** Called if some failure occurs during the user change
|
||||
* process. */
|
||||
/** Called if some failure occurs during the user change process. */
|
||||
public void changeFailed (Exception cause);
|
||||
}
|
||||
|
||||
@@ -119,8 +114,8 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this client has been disconnected for sufficiently
|
||||
* long that its session should be forcibly ended.
|
||||
* Returns true if this client has been disconnected for sufficiently long that its session
|
||||
* should be forcibly ended.
|
||||
*/
|
||||
public boolean checkExpired (long now)
|
||||
{
|
||||
@@ -128,8 +123,7 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which this client started their network
|
||||
* session.
|
||||
* Returns the time at which this client started their network session.
|
||||
*/
|
||||
public long getSessionStamp ()
|
||||
{
|
||||
@@ -137,8 +131,7 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which this client most recently connected or
|
||||
* disconnected.
|
||||
* Returns the time at which this client most recently connected or disconnected.
|
||||
*/
|
||||
public long getNetworkStamp ()
|
||||
{
|
||||
@@ -154,8 +147,7 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the connected client or null if this client
|
||||
* is not connected.
|
||||
* Returns the address of the connected client or null if this client is not connected.
|
||||
*/
|
||||
public InetAddress getInetAddress ()
|
||||
{
|
||||
@@ -164,8 +156,8 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this client with a custom class loader that will be used
|
||||
* when unserializing classes from the network.
|
||||
* Configures this client with a custom class loader that will be used when unserializing
|
||||
* classes from the network.
|
||||
*/
|
||||
public void setClassLoader (ClassLoader loader)
|
||||
{
|
||||
@@ -177,38 +169,32 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>Danger:</em> this method is not for general consumption. This
|
||||
* changes the username of the client, but should only be done very
|
||||
* early in a user's session, when you know that no one has mapped the
|
||||
* user based on their username or has in any other way made use of
|
||||
* their username in a way that will break. However, it should not be
|
||||
* done <em>too</em> early in the session. The client must be fully
|
||||
* resolved.
|
||||
* <em>Danger:</em> this method is not for general consumption. This changes the username of
|
||||
* the client, but should only be done very early in a user's session, when you know that no
|
||||
* one has mapped the user based on their username or has in any other way made use of their
|
||||
* username in a way that will break. However, it should not be done <em>too</em> early in the
|
||||
* session. The client must be fully resolved.
|
||||
*
|
||||
* <p> It exists to support systems wherein a user logs in with an
|
||||
* account username and then chooses a "screen name" by which they
|
||||
* will play (often from a small set of available "characters"
|
||||
* available per account). This will take care of remapping the
|
||||
* username to client object mappings that were made by the Presents
|
||||
* services when the user logs on, but anything else that has had its
|
||||
* grubby mits on the username will be left to its own devices, hence
|
||||
* the care that must be exercised when using this method.
|
||||
* <p> It exists to support systems wherein a user logs in with an account username and then
|
||||
* chooses a "screen name" by which they will play (often from a small set of available
|
||||
* "characters" available per account). This will take care of remapping the username to client
|
||||
* object mappings that were made by the Presents services when the user logs on, but anything
|
||||
* else that has had its grubby mits on the username will be left to its own devices, hence the
|
||||
* care that must be exercised when using this method.
|
||||
*
|
||||
* @param ucl an entity that will (optionally) be notified when the
|
||||
* username conversion process is complete.
|
||||
* @param ucl an entity that will (optionally) be notified when the username conversion process
|
||||
* is complete.
|
||||
*/
|
||||
public void setUsername (Name username, final UserChangeListener ucl)
|
||||
{
|
||||
ClientResolutionListener clr = new ClientResolutionListener() {
|
||||
public void clientResolved (final Name username,
|
||||
final ClientObject clobj)
|
||||
public void clientResolved (final Name username, final ClientObject clobj)
|
||||
{
|
||||
// if they old client object is gone by now, they ended
|
||||
// their session while we were switching, so freak out
|
||||
// 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 new client " +
|
||||
"object [ousername=" + _username +
|
||||
Log.warning("Client disappeared before we could complete the switch to a " +
|
||||
"new client object [ousername=" + _username +
|
||||
", nusername=" + username + "].");
|
||||
_cmgr.releaseClientObject(username);
|
||||
Exception error = new Exception("Early withdrawal");
|
||||
@@ -216,10 +202,8 @@ public class PresentsClient
|
||||
return;
|
||||
}
|
||||
|
||||
// let the client know that the rug has been yanked out
|
||||
// from under their ass
|
||||
Object[] args = new Object[] {
|
||||
Integer.valueOf(clobj.getOid()) };
|
||||
// let the client know that the rug has been yanked out from under their ass
|
||||
Object[] args = new Object[] { Integer.valueOf(clobj.getOid()) };
|
||||
_clobj.postMessage(ClientObject.CLOBJ_CHANGED, args);
|
||||
|
||||
// call down to any derived classes
|
||||
@@ -231,7 +215,6 @@ public class PresentsClient
|
||||
public void requestCompleted (Object result) {
|
||||
finishResolved(username, clobj);
|
||||
}
|
||||
|
||||
public void requestFailed (Exception cause) {
|
||||
finishResolved(username, clobj);
|
||||
}
|
||||
@@ -263,9 +246,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.warning("Unable to resolve new client object [oldname=" + _username +
|
||||
", newname=" + username + ", reason=" + reason + "].");
|
||||
Log.logStackTrace(reason);
|
||||
|
||||
// let our listener know we're hosed
|
||||
@@ -288,25 +270,21 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly terminates a client's session. This must be called from
|
||||
* the dobjmgr thread.
|
||||
* 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)
|
||||
// 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
|
||||
// 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
|
||||
// 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 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 {
|
||||
@@ -328,17 +306,15 @@ 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
|
||||
* <em>not</em> 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.
|
||||
* This is called when the server is shut down in the middle of a client session. In this
|
||||
* circumstance, {@link #endSession} will <em>not</em> 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 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);
|
||||
@@ -346,10 +322,9 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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)
|
||||
{
|
||||
@@ -357,10 +332,9 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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)
|
||||
{
|
||||
@@ -368,8 +342,7 @@ public class PresentsClient
|
||||
if (object != null) {
|
||||
object.removeSubscriber(this);
|
||||
} else {
|
||||
Log.warning("Requested to unmap non-existent subscription " +
|
||||
"[oid=" + oid + "].");
|
||||
Log.warning("Requested to unmap non-existent subscription [oid=" + oid + "].");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,24 +376,23 @@ public class PresentsClient
|
||||
{
|
||||
_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 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 +
|
||||
// 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 + ".");
|
||||
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
|
||||
// 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 + "].");
|
||||
@@ -453,8 +425,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();
|
||||
} else {
|
||||
postMessage(new EventNotification(event));
|
||||
@@ -462,10 +433,9 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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)
|
||||
@@ -473,16 +443,16 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the new client object has been committed to this
|
||||
* client due to a call to {@link #setUsername}.
|
||||
* 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.
|
||||
* 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)
|
||||
@@ -503,12 +473,10 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 ()
|
||||
{
|
||||
@@ -516,80 +484,67 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client manager when a new connection arrives that
|
||||
* authenticates as this already established client. This must only be
|
||||
* called from the congmr thread.
|
||||
* Called by the client manager when a new connection arrives that authenticates as this
|
||||
* already established client. This must only be called from the congmr thread.
|
||||
*/
|
||||
protected void resumeSession (Connection conn)
|
||||
{
|
||||
// check to see if we've already got a connection object, in which
|
||||
// case it's probably stale
|
||||
// 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 + "].");
|
||||
// close the old connection (which results in everything being
|
||||
// properly unregistered)
|
||||
Log.info("Closing stale connection [old=" + oldconn + ", new=" + conn + "].");
|
||||
// close the old connection (which results in everything being properly unregistered)
|
||||
oldconn.close();
|
||||
}
|
||||
|
||||
// start using the new connection
|
||||
setConnection(conn);
|
||||
|
||||
// if a client connects, drops the connection and reconnects
|
||||
// within the span of a very short period of time, we'll find
|
||||
// ourselves in resumeSession() before their client object was
|
||||
// resolved from the initial connection; in such a case, we can
|
||||
// simply bail out here and let the original session establishment
|
||||
// code take care of initializing this resumed session
|
||||
// if a client connects, drops the connection and reconnects within the span of a very
|
||||
// short period of time, we'll find ourselves in resumeSession() before their client object
|
||||
// was resolved from the initial connection; in such a case, we can simply bail out here
|
||||
// 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.warning("Rapid-fire reconnect caused us to arrive in resumeSession() before the " +
|
||||
"original session resolved its client object? " + this + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to get onto the distributed object thread so that we
|
||||
// can finalize the resumption of the session.
|
||||
// we need to get onto the dobj thread so that we can finalize resumption of the session
|
||||
PresentsServer.omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
// now that we're on the dobjmgr thread we can resume our
|
||||
// session resumption
|
||||
// now that we're on the dobjmgr thread we can resume our session resumption
|
||||
finishResumeSession();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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();
|
||||
|
||||
// send off a bootstrap notification immediately because we've
|
||||
// already got our client object
|
||||
// send off a bootstrap notification immediately as we've already got our client object
|
||||
sendBootstrap();
|
||||
|
||||
Log.info("Session resumed " + this + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up a runnable on the object manager thread where we can
|
||||
* safely end the session.
|
||||
* Queues up a runnable on the object manager thread where we can safely end the session.
|
||||
*/
|
||||
protected void safeEndSession ()
|
||||
{
|
||||
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 request from " +
|
||||
"still-resolving client " + this + ".");
|
||||
// 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();
|
||||
@@ -599,8 +554,8 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the tracked client subscriptions. Called when the client
|
||||
* goes away and shouldn't be called otherwise.
|
||||
* Clears out the tracked client subscriptions. Called when the client goes away and shouldn't
|
||||
* be called otherwise.
|
||||
*/
|
||||
protected void clearSubscrips (boolean verbose)
|
||||
{
|
||||
@@ -615,58 +570,50 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the client session is first started. The client object
|
||||
* 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 method should be sure to call
|
||||
* <code>super.sessionWillStart</code>.
|
||||
* Called when the client session is first started. The client object 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
|
||||
* method should be sure to call <code>super.sessionWillStart</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.
|
||||
* <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 sessionWillStart ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the client resumes a session (after having disconnected
|
||||
* 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
|
||||
* method should be sure to call <code>super.sessionWillResume</code>.
|
||||
* Called when the client resumes a session (after having disconnected 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 method should be
|
||||
* sure to call <code>super.sessionWillResume</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.
|
||||
* <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 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
|
||||
* <code>super.sessionDidEnd</code>.
|
||||
* 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.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.
|
||||
* <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 sessionDidEnd ()
|
||||
{
|
||||
// clear out our subscriptions so that we don't get a complaint
|
||||
// about inability to forward the object destroyed event we're
|
||||
// about to generate
|
||||
// clear out our subscriptions so that we don't get a complaint about inability to forward
|
||||
// the object destroyed event we're about to generate
|
||||
clearSubscrips(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called once we have a handle on the client distributed
|
||||
* object. It sends a bootstrap notification to the client with all
|
||||
* the information it will need to interact with the server.
|
||||
* This is called once we have a handle on the client distributed object. It sends a bootstrap
|
||||
* notification to the client with all the information it will need to interact with the
|
||||
* server.
|
||||
*/
|
||||
protected void sendBootstrap ()
|
||||
{
|
||||
@@ -681,9 +628,8 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived client classes can override this member to create derived
|
||||
* bootstrap data classes that contain extra bootstrap information, if
|
||||
* desired.
|
||||
* Derived client classes can override this member to create derived bootstrap data classes
|
||||
* that contain extra bootstrap information, if desired.
|
||||
*/
|
||||
protected BootstrapData createBootstrapData ()
|
||||
{
|
||||
@@ -691,14 +637,12 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived client classes can override this member to populate the
|
||||
* bootstrap data with additional information. They should be sure to
|
||||
* call <code>super.populateBootstrapData</code> before doing their
|
||||
* own populating, however.
|
||||
* Derived client classes can override this member to populate the bootstrap data with
|
||||
* additional information. They should be sure to call <code>super.populateBootstrapData</code>
|
||||
* before doing their own populating, however.
|
||||
*
|
||||
* <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 be done carefully.
|
||||
* <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 be done carefully.
|
||||
*/
|
||||
protected void populateBootstrapData (BootstrapData data)
|
||||
{
|
||||
@@ -706,28 +650,36 @@ public class PresentsClient
|
||||
data.clientOid = _clobj.getOid();
|
||||
|
||||
// fill in the list of bootstrap services
|
||||
data.services = PresentsServer.invmgr.bootlist;
|
||||
data.services = new StreamableArrayList<InvocationMarshaller>();
|
||||
StreamableArrayList<InvocationMarshaller> list =
|
||||
PresentsServer.invmgr.bootlists.get(InvocationManager.GLOBAL_BOOTSTRAP_GROUP);
|
||||
if (list != null) {
|
||||
data.services.addAll(list);
|
||||
}
|
||||
for (String group : _areq.getBootGroups()) {
|
||||
list = PresentsServer.invmgr.bootlists.get(group);
|
||||
if (list != null) {
|
||||
data.services.addAll(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the connection manager when this client's connection is
|
||||
* unmapped. That may be because of a connection failure (in which
|
||||
* case this call will be followed up by a call to
|
||||
* <code>connectionFailed</code>) or it may be because of an orderly
|
||||
* closing of the connection. In either case, the client can deal with
|
||||
* its lack of a connection in this method. This is invoked by the
|
||||
* conmgr thread and should behave accordingly.
|
||||
* Called by the connection manager when this client's connection is unmapped. That may be
|
||||
* because of a connection failure (in which case this call will be followed up by a call to
|
||||
* <code>connectionFailed</code>) or it may be because of an orderly closing of the
|
||||
* connection. In either case, the client can deal with its lack of a connection in this
|
||||
* method. This is invoked by the conmgr thread and should behave accordingly.
|
||||
*/
|
||||
protected void wasUnmapped ()
|
||||
{
|
||||
// clear out our connection reference
|
||||
setConnection(null);
|
||||
|
||||
// clear out our subscriptions. we need to do this on the dobjmgr
|
||||
// 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)
|
||||
// clear out our subscriptions. we need to do this on the dobjmgr 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.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
sessionConnectionClosed();
|
||||
@@ -736,38 +688,33 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the dobjmgr thread when the connection associated with
|
||||
* this session has been closed and unmapped. If the user logged off
|
||||
* before closing their connection, this will be preceded by a call to
|
||||
* {@link #sessionDidEnd}.
|
||||
* Called on the dobjmgr thread when the connection associated with this session has been
|
||||
* closed and unmapped. If the user logged off before closing their connection, this will be
|
||||
* preceded by a call to {@link #sessionDidEnd}.
|
||||
*/
|
||||
protected void sessionConnectionClosed ()
|
||||
{
|
||||
// clear out our dobj subscriptions in case they weren't cleared
|
||||
// by a call to sessionDidEnd
|
||||
// clear out our dobj subscriptions in case they weren't cleared by a call to sessionDidEnd
|
||||
clearSubscrips(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the connection manager when this client's connection
|
||||
* fails. This is invoked on the conmgr thread and should behave
|
||||
* accordingly.
|
||||
* Called by the connection manager when this client's connection fails. This is invoked on the
|
||||
* conmgr thread and should behave accordingly.
|
||||
*/
|
||||
protected void connectionFailed (IOException fault)
|
||||
{
|
||||
// nothing to do here presently. the client manager already
|
||||
// complained about the failed connection
|
||||
// nothing to do here, the client manager already complained about the failed connection
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets our connection reference in a thread safe way. Also
|
||||
* establishes the back reference to us as the connection's message
|
||||
* handler.
|
||||
* 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)
|
||||
{
|
||||
// if our connection is being cleared out, record the amount of
|
||||
// time we were connected to our total connected time
|
||||
// if our connection is being cleared out, record the amount of time we were connected to
|
||||
// our total connected time
|
||||
long now = System.currentTimeMillis();
|
||||
if (_conn != null && conn == null) {
|
||||
_connectTime += ((now - _networkStamp) / 1000);
|
||||
@@ -776,8 +723,8 @@ public class PresentsClient
|
||||
// keep a handle to the new connection
|
||||
_conn = conn;
|
||||
|
||||
// tell the connection to pass messages on to us (if we're setting
|
||||
// a connection rather than clearing one out)
|
||||
// tell the connection to pass messages on to us (if we're setting a connection rather than
|
||||
// clearing one out)
|
||||
if (_conn != null) {
|
||||
_conn.setMessageHandler(this);
|
||||
|
||||
@@ -792,20 +739,19 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* The connection instance must be accessed via this member function
|
||||
* because it is read from both the dobjmgr and conmgr threads and is
|
||||
* modified by the conmgr thread.
|
||||
* The connection instance must be accessed via this member function because it is read from
|
||||
* both the dobjmgr and conmgr threads and is modified by the conmgr thread.
|
||||
*
|
||||
* @return The connection instance associated with this client or null
|
||||
* if the client is not currently connected.
|
||||
* @return The connection instance associated with this client or null if the client is not
|
||||
* currently connected.
|
||||
*/
|
||||
protected synchronized Connection getConnection ()
|
||||
{
|
||||
return _conn;
|
||||
}
|
||||
|
||||
/** Callable from non-dobjmgr thread, this queues up a runnable on the
|
||||
* dobjmgr thread to post the supplied message to this client. */
|
||||
/** 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)
|
||||
{
|
||||
PresentsServer.omgr.postRunnable(new Runnable() {
|
||||
@@ -825,9 +771,8 @@ public class PresentsClient
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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)
|
||||
// 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 +
|
||||
", count=" + _messagesDropped + "].");
|
||||
@@ -859,17 +804,15 @@ public class PresentsClient
|
||||
{
|
||||
buf.append("username=").append(_username);
|
||||
buf.append(", conn=").append(_conn);
|
||||
buf.append(", cloid=").append(
|
||||
(_clobj == null) ? -1 : _clobj.getOid());
|
||||
buf.append(", cloid=").append((_clobj == null) ? -1 : _clobj.getOid());
|
||||
buf.append(", in=").append(_messagesIn);
|
||||
buf.append(", out=").append(_messagesOut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Message dispatchers are used to dispatch each different type of
|
||||
* upstream message. We can look the dispatcher up in a table and
|
||||
* invoke it through an overloaded member which is faster (so we
|
||||
* think) than doing a bunch of instanceofs.
|
||||
* Message dispatchers are used to dispatch each different type of upstream message. We can
|
||||
* look the dispatcher up in a table and invoke it through an overloaded member which is faster
|
||||
* (so we think) than doing a bunch of instanceofs.
|
||||
*/
|
||||
protected static interface MessageDispatcher
|
||||
{
|
||||
@@ -887,8 +830,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);
|
||||
@@ -911,8 +853,8 @@ public class PresentsClient
|
||||
// update our subscription tracking table
|
||||
client.unmapSubscrip(oid);
|
||||
|
||||
// post a response to the client letting them know that we
|
||||
// will no longer send them events regarding this object
|
||||
// post a response to the client letting them know that we will no longer send them
|
||||
// events regarding this object
|
||||
client.safePostMessage(new UnsubscribeResponse(oid));
|
||||
}
|
||||
}
|
||||
@@ -930,8 +872,7 @@ public class PresentsClient
|
||||
// fill in the proper source oid
|
||||
fevt.setSourceOid(client.getClientObject().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);
|
||||
@@ -975,16 +916,15 @@ public class PresentsClient
|
||||
/** The time at which this client started their session. */
|
||||
protected long _sessionStamp;
|
||||
|
||||
/** The time at which this client most recently connected or
|
||||
* disconnected. */
|
||||
/** The time at which this client most recently connected or disconnected. */
|
||||
protected long _networkStamp;
|
||||
|
||||
/** The total number of seconds for which the user was connected to
|
||||
* the server in this session. */
|
||||
/** The total number of seconds for which the user was connected to the server in this
|
||||
* session. */
|
||||
protected int _connectTime;
|
||||
|
||||
/** Prevent the client from sending too many messages too frequently.
|
||||
* 100 messages in 10 seconds and you're audi. */
|
||||
/** Prevent the client from sending too many messages too frequently. 100 messages in 10
|
||||
* seconds and you're audi like 5000. */
|
||||
protected Throttle _throttle = new Throttle(100, 10 * 1000L);
|
||||
|
||||
// keep these for kicks and giggles
|
||||
@@ -996,8 +936,8 @@ public class PresentsClient
|
||||
protected static HashMap<Class<?>,MessageDispatcher> _disps =
|
||||
new HashMap<Class<?>,MessageDispatcher>();
|
||||
|
||||
/** The amount of time after disconnection a user is allowed before
|
||||
* their session is forcibly ended. */
|
||||
/** The amount of time after disconnection a user is allowed before their session is forcibly
|
||||
* ended. */
|
||||
protected static final long FLUSH_TIME = 7 * 60 * 1000L;
|
||||
|
||||
// register our message dispatchers
|
||||
|
||||
Reference in New Issue
Block a user