diff --git a/src/java/com/threerings/presents/dobj/DEvent.java b/src/java/com/threerings/presents/dobj/DEvent.java index f748572d7..3e8ec2544 100644 --- a/src/java/com/threerings/presents/dobj/DEvent.java +++ b/src/java/com/threerings/presents/dobj/DEvent.java @@ -71,8 +71,8 @@ public abstract class DEvent implements Streamable * @exception ObjectAccessException thrown if there is any problem applying the event to the * object (invalid attribute, etc.). * - * @return true if the object manager should go on to notify the object's subscribers of this - * event, false if the event should be treated silently and the subscribers should not be + * @return true if the object manager should go on to notify the object's listeners of this + * event, false if the event should be treated silently and the listeners should not be * notified. */ public abstract boolean applyToObject (DObject target) diff --git a/src/java/com/threerings/presents/dobj/EntryAddedEvent.java b/src/java/com/threerings/presents/dobj/EntryAddedEvent.java index 28595de1d..ba9796cd9 100644 --- a/src/java/com/threerings/presents/dobj/EntryAddedEvent.java +++ b/src/java/com/threerings/presents/dobj/EntryAddedEvent.java @@ -26,22 +26,20 @@ import com.samskivert.util.StringUtil; import com.threerings.presents.Log; /** - * An entry added event is dispatched when an entry is added to a {@link - * DSet} attribute of a distributed entry. It can also be constructed to - * request the addition of an entry to a set and posted to the dobjmgr. + * An entry added event is dispatched when an entry is added to a {@link DSet} attribute of a + * distributed entry. It can also be constructed to request the addition of an entry to a set and + * posted to the dobjmgr. * * @see DObjectManager#postEvent */ public class EntryAddedEvent extends NamedEvent { /** - * Constructs a new entry added event on the specified target object - * with the supplied set attribute name and entry to add. + * Constructs a new entry added event on the specified target object with the supplied set + * attribute name and entry to add. * - * @param targetOid the object id of the object to whose set we will - * add an entry. - * @param name the name of the attribute to which to add the specified - * entry. + * @param targetOid the object id of the object to whose set we will add an entry. + * @param name the name of the attribute to which to add the specified entry. * @param entry the entry to add to the set attribute. */ public EntryAddedEvent (int targetOid, String name, T entry) @@ -50,11 +48,9 @@ public class EntryAddedEvent extends NamedEvent } /** - * Used when the distributed object already added the entry before - * generating the event. + * Used when the distributed object already added the entry before generating the event. */ - public EntryAddedEvent (int targetOid, String name, T entry, - boolean alreadyApplied) + public EntryAddedEvent (int targetOid, String name, T entry, boolean alreadyApplied) { super(targetOid, name); _entry = entry; @@ -62,8 +58,8 @@ public class EntryAddedEvent extends NamedEvent } /** - * Constructs a blank instance of this event in preparation for - * unserialization from the network. + * Constructs a blank instance of this event in preparation for unserialization from the + * network. */ public EntryAddedEvent () { @@ -86,7 +82,31 @@ public class EntryAddedEvent extends NamedEvent if (!_alreadyApplied) { boolean added = target.getSet(_name).add(_entry); if (!added) { - Log.warning("Duplicate entry found [event=" + this + "]."); + // If this entry is already in the set, then don't notify our listeners of the + // event add, because nothing was actually added; the DSet will have already + // complained; this happens occasionally due to a race condition between client + // object subscription and set addition, for example, the follow sequence of events + // can take place: + // + // 1. SUBSCRIBE arrives from client, AccessObjectEvent posted; + // + // 2. addToSet() called on server, value immediately added to set and + // EntryAddedEvent is posted; + // + // 3. AccessObjectEvent processed; client is added to proxy subscriber list, + // DObject serialized and sent to client; client receives object which already has + // the entry in the set; + // + // 4. EntryAddedEvent processed, client is a subscriber so it is forwarded along; + // when it arrives at the client, we find ourselves in this situation. + // + // Fixing this would require either a) giving up immediate adding to the DSet when + // an event is posted, but we add/update immediately because it makes our lives + // *vastly* simpler in a thousand other cases, or b) doing some magic in the DSet + // where the server "sees" the new entry added, but if the DSet is serialized + // before the EntryAddedEvent comes through to "commit" that entry it is not + // included. This is probably a good idea and maybe we'll do it sometime. + return false; } } return true; @@ -111,8 +131,7 @@ public class EntryAddedEvent extends NamedEvent protected T _entry; - /** Used when this event is generated on the authoritative server - * where object changes are made immediately. This lets us know not to - * apply ourselves when we're actually dispatched. */ + /** Used when this event is generated on the authoritative server where object changes are made + * immediately. This lets us know not to apply ourselves when we're actually dispatched. */ protected transient boolean _alreadyApplied; } diff --git a/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java b/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java index 42314d9eb..26d6008d3 100644 --- a/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java +++ b/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java @@ -24,28 +24,24 @@ package com.threerings.presents.dobj; import com.threerings.presents.Log; /** - * An entry removed event is dispatched when an entry is removed from a - * {@link DSet} attribute of a distributed object. It can also be - * constructed to request the removal of an entry from a set and posted to - * the dobjmgr. + * An entry removed event is dispatched when an entry is removed from a {@link DSet} attribute of a + * distributed object. It can also be constructed to request the removal of an entry from a set and + * posted to the dobjmgr. * * @see DObjectManager#postEvent */ public class EntryRemovedEvent extends NamedEvent { /** - * Constructs a new entry removed event on the specified target object - * with the supplied set attribute name and entry key to remove. + * Constructs a new entry removed event on the specified target object with the supplied set + * attribute name and entry key to remove. * - * @param targetOid the object id of the object from whose set we will - * remove an entry. - * @param name the name of the attribute from which to remove the - * specified entry. + * @param targetOid the object id of the object from whose set we will remove an entry. + * @param name the name of the attribute from which to remove the specified entry. * @param key the entry key that identifies the entry to remove. * @param oldEntry the previous value of the entry. */ - public EntryRemovedEvent (int targetOid, String name, Comparable key, - T oldEntry) + public EntryRemovedEvent (int targetOid, String name, Comparable key, T oldEntry) { super(targetOid, name); _key = key; @@ -53,8 +49,8 @@ public class EntryRemovedEvent extends NamedEvent } /** - * Constructs a blank instance of this event in preparation for - * unserialization from the network. + * Constructs a blank instance of this event in preparation for unserialization from the + * network. */ public EntryRemovedEvent () { @@ -88,8 +84,7 @@ public class EntryRemovedEvent extends NamedEvent _oldEntry = set.removeKey(_key); if (_oldEntry == null) { // complain if there was actually nothing there - Log.warning("No matching entry to remove [key=" + _key + - ", set=" + set + "]."); + Log.warning("No matching entry to remove [key=" + _key + ", set=" + set + "]."); return false; } } @@ -113,6 +108,7 @@ public class EntryRemovedEvent extends NamedEvent } protected Comparable _key; + @SuppressWarnings("unchecked") protected transient T _oldEntry = (T)UNSET_OLD_ENTRY; } diff --git a/src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java b/src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java index 66ce76c1e..55053ea46 100644 --- a/src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java +++ b/src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java @@ -26,22 +26,19 @@ import com.samskivert.util.StringUtil; import com.threerings.presents.Log; /** - * An entry updated event is dispatched when an entry of a {@link DSet} is - * updated. It can also be constructed to request the update of an entry - * and posted to the dobjmgr. + * An entry updated event is dispatched when an entry of a {@link DSet} is updated. It can also be + * constructed to request the update of an entry and posted to the dobjmgr. * * @see DObjectManager#postEvent */ public class EntryUpdatedEvent extends NamedEvent { /** - * Constructs a new entry updated event on the specified target object - * for the specified set name and with the supplied updated entry. + * Constructs a new entry updated event on the specified target object for the specified set + * name and with the supplied updated entry. * - * @param targetOid the object id of the object to whose set we will - * add an entry. - * @param name the name of the attribute in which to update the - * specified entry. + * @param targetOid the object id of the object to whose set we will add an entry. + * @param name the name of the attribute in which to update the specified entry. * @param entry the entry to update. * @param oldEntry the previous value of the entry. */ @@ -53,8 +50,8 @@ public class EntryUpdatedEvent extends NamedEvent } /** - * Constructs a blank instance of this event in preparation for - * unserialization from the network. + * Constructs a blank instance of this event in preparation for unserialization from the + * network. */ public EntryUpdatedEvent () { @@ -89,8 +86,7 @@ public class EntryUpdatedEvent extends NamedEvent _oldEntry = set.update(_entry); if (_oldEntry == null) { // complain if we didn't update anything - Log.warning("No matching entry to update [entry=" + this + - ", set=" + set + "]."); + Log.warning("No matching entry to update [entry=" + this + ", set=" + set + "]."); return false; } } @@ -115,6 +111,7 @@ public class EntryUpdatedEvent extends NamedEvent } protected T _entry; + @SuppressWarnings("unchecked") protected transient T _oldEntry = (T)UNSET_OLD_ENTRY; }