Added support for registering, on a per class basis, a delay which will be

observed after the last subscriber of a matching distributed object has
been removed. Our client's subscription will not be flushed until after
the delay has expired, and only then if no other subscriber has come along
requesting the object in the meanwhile.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2303 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2003-03-11 04:43:14 +00:00
parent ef525ebad9
commit 91146a6805
2 changed files with 122 additions and 11 deletions
@@ -1,5 +1,5 @@
//
// $Id: Client.java,v 1.36 2003/01/28 04:24:16 mdb Exp $
// $Id: Client.java,v 1.37 2003/03/11 04:43:14 mdb Exp $
package com.threerings.presents.client;
@@ -184,6 +184,25 @@ public class Client
return (_comm != null) ? _comm.getDObjectManager() : null;
}
/**
* Instructs the distributed object manager associated with this
* client to allow objects of the specified class to linger around the
* specified number of milliseconds after their last subscriber has
* been removed before the client finally removes its object proxy and
* flushes the object. Normally, objects are flushed immediately
* following the removal of their last subscriber.
*
* <p><em>Note:</em> the delay will be applied to derived classes as
* well as exact matches. <em>Note also:</em> this method cannot be
* called until after the client has established a connection with the
* server and the distributed object manager is available.
*/
public void registerFlushDelay (Class objclass, long delay)
{
ClientDObjectMgr omgr = (ClientDObjectMgr)getDObjectManager();
omgr.registerFlushDelay(objclass, delay);
}
/**
* Returns the oid of the client object associated with this session.
* It is only valid for the duration of the session.
@@ -1,11 +1,12 @@
//
// $Id: ClientDObjectMgr.java,v 1.21 2003/03/10 18:29:54 mdb Exp $
// $Id: ClientDObjectMgr.java,v 1.22 2003/03/11 04:43:14 mdb Exp $
package com.threerings.presents.client;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -13,8 +14,10 @@ import com.samskivert.util.DebugChords;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.Queue;
import com.samskivert.util.StringUtil;
import com.samskivert.util.IntervalManager;
import com.threerings.presents.Log;
import com.threerings.presents.client.util.SafeInterval;
import com.threerings.presents.dobj.*;
import com.threerings.presents.net.*;
@@ -45,6 +48,13 @@ public class ClientDObjectMgr
// distributed object table
DebugChords.registerHook(
DUMP_OTABLE_MODMASK, DUMP_OTABLE_KEYCODE, DUMP_OTABLE_HOOK);
// register a flush interval
IntervalManager.register(new SafeInterval(client) {
public void run () {
flushObjects();
}
}, FLUSH_INTERVAL, null, true);
}
// documentation inherited from interface
@@ -98,16 +108,33 @@ public class ClientDObjectMgr
// inherit documentation from the interface
public void removedLastSubscriber (DObject obj)
{
// move this object into the dead pool so that we don't claim to
// have it around anymore; once our unsubscribe message is
// processed, it'll be 86ed
int ooid = obj.getOid();
_ocache.remove(ooid);
_dead.put(ooid, obj);
// if this object has a registered flush delay, don't can it just
// yet, just slip it onto the flush queue
Class oclass = obj.getClass();
for (Iterator iter = _delays.keySet().iterator(); iter.hasNext(); ) {
Class dclass = (Class)iter.next();
if (dclass.isAssignableFrom(oclass)) {
long expire = System.currentTimeMillis() +
((Long)_delays.get(dclass)).longValue();
_flushes.put(obj.getOid(), new FlushRecord(obj, expire));
// Log.info("Flushing " + obj.getOid() + " at " +
// new java.util.Date(expire));
return;
}
}
// ship off an unsubscribe message to the server; we'll remove the
// object from our table when we get the unsub ack
_comm.postMessage(new UnsubscribeRequest(ooid));
// if we didn't find a delay registration, flush immediately
flushObject(obj);
}
/**
* Registers an object flush delay.
*
* @see Client#registerFlushDelay
*/
public void registerFlushDelay (Class objclass, long delay)
{
_delays.put(objclass, new Long(delay));
}
/**
@@ -285,6 +312,10 @@ public class ClientDObjectMgr
// first see if we've already got the object in our table
DObject obj = (DObject)_ocache.get(oid);
if (obj != null) {
// clear the object out of the flush table if it's in there
if (_flushes.remove(oid) != null) {
// Log.info("Resurrected " + oid + ".");
}
// add the subscriber and call them back straight away
obj.addSubscriber(target);
target.objectAvailable(obj);
@@ -327,6 +358,42 @@ public class ClientDObjectMgr
}
}
/**
* Flushes a distributed object subscription, issuing an unsubscribe
* request to the server.
*/
protected void flushObject (DObject obj)
{
// move this object into the dead pool so that we don't claim to
// have it around anymore; once our unsubscribe message is
// processed, it'll be 86ed
int ooid = obj.getOid();
_ocache.remove(ooid);
_dead.put(ooid, obj);
// ship off an unsubscribe message to the server; we'll remove the
// object from our table when we get the unsub ack
_comm.postMessage(new UnsubscribeRequest(ooid));
}
/**
* Called periodically to flush any objects that have been lingering
* due to a previously enacted flush delay.
*/
protected void flushObjects ()
{
long now = System.currentTimeMillis();
for (Iterator iter = _flushes.keySet().iterator(); iter.hasNext(); ) {
int oid = ((Integer)iter.next()).intValue();
FlushRecord rec = (FlushRecord)_flushes.get(oid);
if (rec.expire <= now) {
iter.remove();
flushObject(rec.object);
// Log.info("Flushed object " + oid + ".");
}
}
}
/**
* The object action is used to queue up a subscribe or unsubscribe
* request.
@@ -366,6 +433,22 @@ public class ClientDObjectMgr
}
}
/** Used to manage pending object flushes. */
protected static final class FlushRecord
{
/** The object to be flushed. */
public DObject object;
/** The time at which we flush it. */
public long expire;
public FlushRecord (DObject object, long expire)
{
this.object = object;
this.expire = expire;
}
}
/** A reference to the communicator that sends and receives messages
* for this client. */
protected Communicator _comm;
@@ -385,6 +468,12 @@ public class ClientDObjectMgr
/** Pending object subscriptions. */
protected HashIntMap _penders = new HashIntMap();
/** A mapping from distributed object class to flush delay. */
protected HashMap _delays = new HashMap();
/** A set of objects waiting to be flushed. */
protected HashIntMap _flushes = new HashIntMap();
/** A debug hook that allows the dumping of all objects in the object
* table out to the log. */
protected DebugChords.Hook DUMP_OTABLE_HOOK = new DebugChords.Hook() {
@@ -404,4 +493,7 @@ public class ClientDObjectMgr
/** The key code for our dump table debug hook (o). */
protected static int DUMP_OTABLE_KEYCODE = KeyEvent.VK_O;
/** Flush expired objects every 30 seconds. */
protected static final long FLUSH_INTERVAL = 30 * 1000L;
}