diff --git a/src/java/com/threerings/presents/peer/data/NodeObject.java b/src/java/com/threerings/presents/peer/data/NodeObject.java index 8dba252fd..2bc88a6d8 100644 --- a/src/java/com/threerings/presents/peer/data/NodeObject.java +++ b/src/java/com/threerings/presents/peer/data/NodeObject.java @@ -21,6 +21,8 @@ package com.threerings.presents.peer.data; +import com.threerings.io.Streamable; + import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.DSet; @@ -29,14 +31,41 @@ import com.threerings.presents.dobj.DSet; */ public class NodeObject extends DObject { + /** Used for informing peers of changes to persistent data. */ + public static class CacheData implements Streamable + { + /** The cache that should be purged. */ + public String cache; + + /** The stale data in the cache. */ + public Streamable data; + + public CacheData () + { + } + + public CacheData (String cache, Streamable data) + { + this.cache = cache; + this.data = data; + } + + } + // AUTO-GENERATED: FIELDS START /** The field name of the clients field. */ public static final String CLIENTS = "clients"; + + /** The field name of the cacheData field. */ + public static final String CACHE_DATA = "cacheData"; // AUTO-GENERATED: FIELDS END /** Contains information on all clients connected to this node. */ public DSet clients = new DSet(); + /** A field we use to broadcast changes to possible cached data. */ + public CacheData cacheData; + // AUTO-GENERATED: METHODS START /** * Requests that the specified entry be added to the @@ -85,5 +114,21 @@ public class NodeObject extends DObject (value == null) ? null : value.typedClone(); this.clients = clone; } + + /** + * Requests that the cacheData field be set to the + * specified value. The local value will be updated immediately and an + * event will be propagated through the system to notify all listeners + * that the attribute did change. Proxied copies of this object (on + * clients) will apply the value change when they received the + * attribute changed notification. + */ + public void setCacheData (NodeObject.CacheData value) + { + NodeObject.CacheData ovalue = this.cacheData; + requestAttributeChange( + CACHE_DATA, value, ovalue); + this.cacheData = value; + } // AUTO-GENERATED: METHODS END } diff --git a/src/java/com/threerings/presents/peer/server/PeerManager.java b/src/java/com/threerings/presents/peer/server/PeerManager.java index 13ebcdb37..d41610dba 100644 --- a/src/java/com/threerings/presents/peer/server/PeerManager.java +++ b/src/java/com/threerings/presents/peer/server/PeerManager.java @@ -30,11 +30,15 @@ import com.samskivert.io.PersistenceException; import com.samskivert.jdbc.ConnectionProvider; import com.samskivert.util.Interval; import com.samskivert.util.Invoker; +import com.samskivert.util.ObserverList; +import com.threerings.io.Streamable; import com.threerings.util.Name; import com.threerings.presents.client.Client; import com.threerings.presents.client.ClientObserver; +import com.threerings.presents.dobj.AttributeChangedEvent; +import com.threerings.presents.dobj.AttributeChangeListener; import com.threerings.presents.dobj.ObjectAccessException; import com.threerings.presents.dobj.Subscriber; import com.threerings.presents.server.ClientManager; @@ -58,6 +62,18 @@ import static com.threerings.presents.Log.log; public class PeerManager implements ClientManager.ClientObserver { + /** + * Used by entities that wish to know when cached data has become stale due to a change on + * one of our peer servers. + */ + public static interface StaleCacheObserver + { + /** + * Called when some possibly cached data has changed on one of our peer servers. + */ + public void changedCacheData (Streamable data); + } + /** * Creates a peer manager which will create a {@link NodeRepository} which will be used to * publish our existence and discover the other nodes. @@ -209,6 +225,42 @@ public class PeerManager log.warning("Session ended for unregistered client [who=" + username + "]."); } + /** + * Registers a stale cache observer. + */ + public void addStaleCacheObserver (String cache, StaleCacheObserver observer) + { + ObserverList list = _cacheobs.get(cache); + if (list == null) { + list = new ObserverList(ObserverList.FAST_UNSAFE_NOTIFY); + _cacheobs.put(cache, list); + } + list.add(observer); + } + + /** + * Removes a stale cache observer registration. + */ + public void removeStaleCacheObserver (String cache, StaleCacheObserver observer) + { + ObserverList list = _cacheobs.get(cache); + if (list == null) { + return; + } + list.remove(observer); + if (list.isEmpty()) { + _cacheobs.remove(cache); + } + } + + /** + * Called when cached data has changed on the local server and needs to inform our peers. + */ + public void broadcastStaleCacheData (String cache, Streamable data) + { + _nodeobj.setCacheData(new NodeObject.CacheData(cache, data)); + } + /** * Called after we have finished our initialization. */ @@ -281,6 +333,25 @@ public class PeerManager return new ClientInfo(); } + /** + * Called when possibly cached data has changed on one of our peer servers. + */ + protected void changedCacheData (String cache, final Streamable data) + { + // see if we have any observers + ObserverList list = _cacheobs.get(cache); + if (list == null) { + return; + } + // if so, notify them + list.apply(new ObserverList.ObserverOp() { + public boolean apply (StaleCacheObserver observer) { + observer.changedCacheData(data); + return true; + } + }); + } + /** * Initializes the supplied client info for the supplied client. */ @@ -301,7 +372,7 @@ public class PeerManager * Contains all runtime information for one of our peer nodes. */ protected class PeerNode - implements ClientObserver, Subscriber + implements ClientObserver, Subscriber, AttributeChangeListener { /** This peer's node object. */ public NodeObject nodeobj; @@ -406,6 +477,7 @@ public class PeerManager public void objectAvailable (NodeObject object) { nodeobj = object; + nodeobj.addListener(this); // TODO: stuff! } @@ -416,6 +488,15 @@ public class PeerManager "[peer=" + _record + ", cause=" + cause + "]."); } + // documentation inherited from interface AttributeChangeListener + public void attributeChanged (AttributeChangedEvent event) + { + if (NodeObject.CACHE_DATA.equals(event.getName())) { + NodeObject.CacheData cd = (NodeObject.CacheData)event.getValue(); + changedCacheData(cd.cache, cd.data); + } + } + protected NodeRecord _record; protected Client _client; protected long _lastConnectStamp; @@ -427,4 +508,8 @@ public class PeerManager protected NodeRepository _noderepo; protected NodeObject _nodeobj; protected HashMap _peers = new HashMap(); + + /** Our stale cache observers. */ + protected HashMap> _cacheobs = + new HashMap>(); }