Author: al
Date: Tue Aug 27 18:44:07 2013
New Revision: 1517913

URL: http://svn.apache.org/r1517913
Log:
Assorted fixes to re-enable federation

https://reviews.apache.org/r/12725/

Contains the following commits:

commit 64024d5e781a6d0583d1ca32e609a2f659c8eb0c
Author: Ali Lown <[email protected]>
Date:   Fri Aug 16 12:52:49 2013 +0100

    Address Yuri's comments on federation fixes.

commit e4e8c32350e161edbe9f882e2d5ba85ceeff00df
Author: Ali Lown <[email protected]>
Date:   Thu Jul 18 16:43:03 2013 +0100

    Collection of fixes to bring re-enable federation

    commit 252214fd13bee7f946118eba4df5b822b93f92dc
    Author: Ali Lown <[email protected]>
    Date:   Wed Jul 17 22:13:19 2013 +0100

        Fix test-suite following these assorted fed fixes.

    commit 1a7078230b9ed8c84a91e4bf4a2fe9982f4cfca8
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 8 20:54:14 2013 +0100

        Revert "Revert "Fixes WAVE-354: SimpleSearch.DigestProxy should update 
only the opened""

        This reverts commit 35313df5546622d9b10ac53b60b022317cebdeb6.

        Although this commit no longer causes shiny's with federated waves, it
        seems to be making the search much 'laggier' than it used to be. So, it
        looks like it needs looking in to some more.

    commit 8e8ca730c148269fa7107e1be7f3407b6f296645
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 8 20:48:51 2013 +0100

        Fix Wavelet Root Conversation id generation

        Means that getRoot() works for federated waves now.

    commit a161dbf32bb6493742e4ac253e98c267bd83e5e1
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 8 20:48:41 2013 +0100

        Undo debugging data

    commit 185a903d4b61803a72490985ef4234329a02978e
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 8 16:08:12 2013 +0100

        Shiny fixing: Put title-fetching in TitleHelper.

    commit 8f3f07b8e65a4890a6eb184bcafb14d6d14dc65e
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 8 14:49:24 2013 +0100

        Revert "Disable WindowTitleHandler (not federation capable)"

        This reverts commit 001356a30b485a857140f783b54e794be0190572.

    commit eba868a89419c904b58b2ac7b54905a5a4d6f1c5
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 8 14:43:47 2013 +0100

        Notify listeners and try to sync anyway...

    commit 9805380864d0eeea3dcec634faa7afaa7a4fcabb
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 8 14:17:27 2013 +0100

        Patch over a shiny from attempting to mark as read

        Occurs when the wave is not fully loaded before trying to change
        supplemented state.

    commit 0de5a6fa02c14b06ba852ae2f7001590c9b83ea0
    Author: Ali Lown <[email protected]>
    Date:   Mon Jun 3 23:35:06 2013 +0100

        Disable WindowTitleHandler (not federation capable)

    commit 014827117125a5d21b3068d4b9368a81b0a732ae
    Author: Ali Lown <[email protected]>
    Date:   Mon Jun 3 22:17:47 2013 +0100

        No point calling the same function twice.

    commit 54c600c6a838878051b5383d9a8ca2754e881a8b
    Author: Ali Lown <[email protected]>
    Date:   Mon Jun 3 12:54:48 2013 +0100

        During search, check remote wavelets first.

    commit 5683ba4a7b6832b994ef332a80f133e1c8817815
    Author: Ali Lown <[email protected]>
    Date:   Mon Jun 3 12:52:05 2013 +0100

        Add extra logging for search+wavelet paths

    commit 1809e26db374d5d03ac2f0f48a68ac083355feac
    Author: Ali Lown <[email protected]>
    Date:   Mon Jun 3 12:48:24 2013 +0100

        Fix copy-paste error?

    commit f2ac77df78d7ff5089197bf6d75459cc8f1b9b70
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 22:15:08 2013 +0100

        Revert "Fixes WAVE-354: SimpleSearch.DigestProxy should update only the 
opened"

        This reverts commit bf760c93b77daa5893016f7ac8a4979728cfa0a5.

    commit 5dce24fde5e36452416ca7cf989784407866b300
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 15:08:48 2013 +0100

        Add some more logging.

    commit abaa4e76548699501479270e0e97d33a94227eab
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 15:08:37 2013 +0100

        Add NPE check to getCurrentVersion

    commit cab6e9cda4404d8181c1460ce885ec4bb5c2aa39
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 15:07:55 2013 +0100

        Create wavelet, pass submit even if not fetched.

    commit 0ea2c20972f0b2cf5d3047ec456e4f4f5e19ebc3
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 15:07:01 2013 +0100

        Wait for frontend loading before commit/update

    commit bc28198aeb47769b60e37cbbc56b965a6d25460a
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 15:06:37 2013 +0100

        Track only largest pending commit

    commit 5a75628d3d6d10b8efb7cf097c523b9a38802172
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 15:05:16 2013 +0100

        Don't allow update when frontend not initialized

    commit c2ea97c0e1069a8221486b62b73b58ef0d8fd8ab
    Author: Ali Lown <[email protected]>
    Date:   Sun Jun 2 00:00:19 2013 +0100

        Track requested commits for not-yet-got versions.

    commit 34f35c598cb4e51e2365956db1a5b41e3c3bb248
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 1 23:06:56 2013 +0100

        Ignore commit if not wavelet not yet up-to-date.

    commit 3d4ebc2415dd5e355a528066d1253a558a5c2300
    Author: Ali Lown <[email protected]>
    Date:   Sat Jun 1 19:13:47 2013 +0100

        Prevent requested history:non-empty,non-contiguous

        This would cause a SEVERE, but doesn't seem to actually warant an extra
        behaviour on the receivers part beyond simply requestingHistory which
        already happened.

Modified:
    
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/WaveletInfo.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java
    incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/Wave.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java
    
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java
    incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/TitleHelper.java
    incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGenerator.java
    incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java
    
incubator/wave/trunk/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java
    
incubator/wave/trunk/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java
 Tue Aug 27 18:44:07 2013
@@ -229,6 +229,9 @@ public class ClientFrontendImpl implemen
    */
   private void participantUpdate(WaveletName waveletName, ParticipantId 
participant,
       DeltaSequence newDeltas, boolean add, boolean remove) {
+    if(LOG.isFineLoggable()) {
+      LOG.fine("Notifying " + participant + " for " + waveletName);
+    }
     if (add) {
       waveletInfo.notifyAddedExplicitWaveletParticipant(waveletName, 
participant);
     }
@@ -249,6 +252,11 @@ public class ClientFrontendImpl implemen
     }
 
     WaveletName waveletName = WaveletName.of(wavelet.getWaveId(), 
wavelet.getWaveletId());
+
+    if(waveletInfo.getCurrentWaveletVersion(waveletName).getVersion() == 0 && 
LOG.isWarningLoggable()) {
+      LOG.warning("Wavelet does not appear to have been initialized by client. 
Continuing anyway.");
+    }
+
     waveletInfo.syncWaveletVersion(waveletName, newDeltas);
 
     Set<ParticipantId> remainingparticipants =

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/WaveletInfo.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/WaveletInfo.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/WaveletInfo.java 
(original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/frontend/WaveletInfo.java 
Tue Aug 27 18:44:07 2013
@@ -35,6 +35,7 @@ import org.waveprotocol.wave.model.versi
 import org.waveprotocol.wave.model.version.HashedVersionFactory;
 import org.waveprotocol.wave.model.wave.ParticipantId;
 import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
+import org.waveprotocol.wave.util.logging.Log;
 
 import java.util.Map;
 import java.util.Map.Entry;
@@ -48,6 +49,7 @@ import java.util.Set;
  * @see ClientFrontendImpl
  */
 public class WaveletInfo {
+  private static final Log LOG = Log.get(WaveletInfo.class);
 
   /** Information we hold in memory for each wavelet. */
   private static class PerWavelet {
@@ -135,6 +137,10 @@ public class WaveletInfo {
    * Initializes front-end information from the wave store, if necessary.
    */
   public void initialiseWave(WaveId waveId) throws WaveServerException {
+    if(LOG.isFineLoggable()) {
+      LOG.fine("frontend initialiseWave(" + waveId +")");
+    }
+
     if (!perWavelet.containsKey(waveId)) {
       Map<WaveletId, PerWavelet> wavelets = perWavelet.get(waveId);
       for (WaveletId waveletId : waveletProvider.getWaveletIds(waveId)) {
@@ -144,6 +150,9 @@ public class WaveletInfo {
         PerWavelet waveletInfo = wavelets.get(waveletId);
         synchronized (waveletInfo) {
           waveletInfo.currentVersion = wavelet.getHashedVersion();
+          if(LOG.isFineLoggable()) {
+            LOG.fine("frontend wavelet " + waveletId + " @" + 
wavelet.getHashedVersion().getVersion());
+          }
           waveletInfo.explicitParticipants.addAll(wavelet.getParticipants());
         }
       }

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java
 Tue Aug 27 18:44:07 2013
@@ -38,7 +38,7 @@ import org.waveprotocol.wave.model.versi
 interface LocalWaveletContainer extends WaveletContainer {
 
   /**
-   * Manufactures remote wavelet containers.
+   * Manufactures local wavelet containers.
    */
   interface Factory extends WaveletContainer.Factory<LocalWaveletContainer> { }
 

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java
 Tue Aug 27 18:44:07 2013
@@ -97,7 +97,10 @@ public class MemoryPerUserWaveViewHandle
       Multimap<WaveId, WaveletId> perUserView = 
explicitPerUserWaveViews.get(user);
       if (!perUserView.containsEntry(waveletName.waveId, 
waveletName.waveletId)) {
         perUserView.put(waveletName.waveId, waveletName.waveletId);
-        LOG.fine("Added wavelet: " + waveletName + " to the view of user: " + 
user.getAddress());
+        if(LOG.isFineLoggable()) {
+          LOG.fine("Added wavelet: " + waveletName + " to the view of user: " 
+ user.getAddress());
+          LOG.fine("View size is now: " + perUserView.size());
+        }
       }
     }
     SettableFuture<Void> task = SettableFuture.create();

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java
 Tue Aug 27 18:44:07 2013
@@ -30,6 +30,7 @@ import org.waveprotocol.wave.model.opera
 import org.waveprotocol.wave.model.version.HashedVersion;
 import org.waveprotocol.wave.model.wave.ParticipantId;
 import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
+import org.waveprotocol.wave.util.logging.Log;
 
 import java.util.concurrent.CopyOnWriteArraySet;
 
@@ -39,6 +40,7 @@ import java.util.concurrent.CopyOnWriteA
  * @author [email protected] (Yuri Zelikov)
  */
 public class PerUserWaveViewDistpatcher implements WaveBus.Subscriber, 
PerUserWaveViewBus {
+  private static final Log LOG = Log.get(PerUserWaveViewDistpatcher.class);
 
   private static final CopyOnWriteArraySet<PerUserWaveViewBus.Listener> 
listeners =
       new CopyOnWriteArraySet<PerUserWaveViewBus.Listener>();
@@ -48,11 +50,19 @@ public class PerUserWaveViewDistpatcher 
     WaveletId waveletId = wavelet.getWaveletId();
     WaveId waveId = wavelet.getWaveId();
     WaveletName waveletName = WaveletName.of(waveId, waveletId);
+    if(LOG.isInfoLoggable()) {
+      LOG.info("Got update for " + waveId + " " + waveletId);
+    }
+
     // Find whether participants were added/removed and update the views
     // accordingly.
     for (TransformedWaveletDelta delta : deltas) {
       for (WaveletOperation op : delta) {
         if (op instanceof AddParticipant) {
+          if(LOG.isInfoLoggable()) {
+            LOG.info("Update contains AddParticipant for " + 
((AddParticipant)op).getParticipantId());
+          }
+
           ParticipantId user = ((AddParticipant) op).getParticipantId();
           // Check first if we need to update views for this user.
           for (Listener listener : listeners) {
@@ -82,4 +92,4 @@ public class PerUserWaveViewDistpatcher 
   public void removeListener(Listener listener) {
     listeners.remove(listener);
   }
-}
\ No newline at end of file
+}

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java
 Tue Aug 27 18:44:07 2013
@@ -51,7 +51,9 @@ import org.waveprotocol.wave.model.opera
 import org.waveprotocol.wave.model.version.HashedVersion;
 import org.waveprotocol.wave.util.logging.Log;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.NavigableMap;
 import java.util.concurrent.Executor;
@@ -73,6 +75,13 @@ class RemoteWaveletContainerImpl extends
       pendingDeltas = Maps.newTreeMap();
 
   /**
+   * Tracks the highest version commit notice received, which can not be 
performed
+   * due to not yet having the required deltas. This must only be access under 
writeLock.
+   */
+  private boolean pendingCommit = false;
+  private HashedVersion pendingCommitVersion;
+
+  /**
    * Create a new RemoteWaveletContainerImpl. Just pass through to the parent
    * constructor.
    */
@@ -95,14 +104,50 @@ class RemoteWaveletContainerImpl extends
 
   @Override
   public void commit(HashedVersion version) {
+    try {
+      awaitLoad();
+    }
+    catch(WaveletStateException ex) {
+      LOG.warning("Failed to load " + getWaveletName() + " to perform 
commit.", ex);
+      acquireWriteLock();
+      markStateCorrupted();
+      releaseWriteLock();
+      return;
+    }
+
     acquireWriteLock();
     try {
-      persist(version, ImmutableSet.<String>of());
+      attemptCommit(version);
     } finally {
       releaseWriteLock();
     }
   }
 
+  /**
+   * Attempts to commit at the given version.
+   * This will only succeed if we are actually up to date.
+   * If not, then the history is assumed to be coming, and so we can just skip 
the whole task.
+   * */
+  private void attemptCommit(HashedVersion version) {
+    HashedVersion expectedVersion = getCurrentVersion();
+    if(expectedVersion == null || version.getVersion() == 
expectedVersion.getVersion()) {
+      LOG.info("Committed " + getWaveletName() + " at version " + 
version.getVersion());
+      persist(version, ImmutableSet.<String>of());
+      if(pendingCommitVersion == null || (version.getVersion() >= 
pendingCommitVersion.getVersion())) {
+        pendingCommit = false;
+      }
+    } else {
+      LOG.info("Ignoring commit request at " + version.getVersion() +
+          " since only at " + expectedVersion.getVersion());
+      if(pendingCommitVersion == null ||
+          (pendingCommitVersion != null && pendingCommitVersion.getVersion() < 
version.getVersion())) {
+        pendingCommitVersion = version;
+      }
+      LOG.info("pendingCommitVersion is now " + 
pendingCommitVersion.getVersion());
+      pendingCommit = true;
+    }
+  }
+
   private void internalUpdate(final List<ByteString> deltas,
       final String domain, final WaveletFederationProvider federationProvider,
       final CertificateManager certificateManager, final SettableFuture<Void> 
futureResult) {
@@ -176,6 +221,18 @@ class RemoteWaveletContainerImpl extends
       List<ByteStringMessage<ProtocolAppliedWaveletDelta>> appliedDeltas,
       final String domain, final WaveletFederationProvider federationProvider,
       final CertificateManager certificateManager, final SettableFuture<Void> 
futureResult) {
+
+    try {
+      awaitLoad();
+    }
+    catch(WaveletStateException ex) {
+      LOG.warning("Failed to load " + getWaveletName() + " to perform 
update.", ex);
+      acquireWriteLock();
+      markStateCorrupted();
+      releaseWriteLock();
+      return;
+    }
+
     LOG.info("Passed signer info check, now applying all " + 
appliedDeltas.size() + " deltas");
     acquireWriteLock();
     try {
@@ -230,6 +287,11 @@ class RemoteWaveletContainerImpl extends
         HashedVersion appliedAt = first.getKey();
         ByteStringMessage<ProtocolAppliedWaveletDelta> appliedDelta = 
first.getValue();
 
+        if(LOG.isInfoLoggable()) {
+          LOG.info("pendingDeltas.size(): " + 
Integer.toString(pendingDeltas.size()));
+          LOG.info("current appliedAt: " + appliedAt.getVersion() + " 
expected: " + expectedVersion.getVersion());
+        }
+
         // If we don't have the right version it implies there is a history we 
need, so set up a
         // callback to request it and fall out of this update
         if (appliedAt.getVersion() > expectedVersion.getVersion()) {
@@ -310,14 +372,8 @@ class RemoteWaveletContainerImpl extends
         pendingDeltas.remove(appliedAt);
       }
 
-      if (!haveRequestedHistory) {
-        notifyOfDeltas(resultingDeltas.build(), ImmutableSet.<String>of());
-        futureResult.set(null);
-      } else if (!resultingDeltas.build().isEmpty()) {
-        LOG.severe("History requested but non-empty result, non-contiguous 
deltas?");
-      } else {
-        LOG.info("History requested, ignoring callback");
-      }
+      commitAndNotifyResultingDeltas(resultingDeltas, futureResult);
+
     } catch (WaveServerException e) {
       LOG.warning("Update failure", e);
       // TODO(soren): make everyone throw FederationException instead
@@ -330,6 +386,30 @@ class RemoteWaveletContainerImpl extends
   }
 
   /**
+   * Commits the resulting deltas, notifying the server of them.
+   * Assumes that everything in resultingDeltas is now in-order, since
+   * even if the original stream was non-contiguous, we have requestedHistory.
+   * Even if not, it is still safe to commit up to the fragmented point.
+   */
+  private void commitAndNotifyResultingDeltas(
+      ImmutableList.Builder<WaveletDeltaRecord> resultingDeltas,
+      final SettableFuture<Void> futureResult) {
+    if(!resultingDeltas.build().isEmpty()) {
+      notifyOfDeltas(resultingDeltas.build(), ImmutableSet.<String>of());
+      futureResult.set(null);
+
+      //Attempt to run any pending commit
+      if(pendingCommit) {
+        releaseWriteLock();
+        commit(pendingCommitVersion);
+        acquireWriteLock();
+      }
+    } else {
+      LOG.info("No deltas in list (fetching history?), ignoring callback");
+    }
+  }
+
+  /**
    * Apply a serialised applied delta to a remote wavelet. This assumes the
    * caller has validated that the delta is at the correct version and can be
    * applied to the wavelet. Must be called with writelock held.

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java
 Tue Aug 27 18:44:07 2013
@@ -111,6 +111,13 @@ public class SimpleSearchProviderImpl im
     Function<ReadableWaveletData, Boolean> filterWaveletsFunction =
         createFilterWaveletsFunction(user, isAllQuery, withParticipantIds, 
creatorParticipantIds);
     Map<WaveId, WaveViewData> results = 
filterWavesViewBySearchCriteria(filterWaveletsFunction, currentUserWavesView);
+
+    if(LOG.isFineLoggable()) {
+      for(Map.Entry e : results.entrySet()) {
+        LOG.fine("filtered results contains: " + e.getKey());
+      }
+    }
+
     Collection<WaveViewData> searchResult =
         computeSearchResult(user, startAt, numResults, queryParams, results);
     LOG.info("Search response to '" + query + "': " + searchResult.size() + " 
results, user: "
@@ -128,6 +135,13 @@ public class SimpleSearchProviderImpl im
       // shared domain participant.
       
currentUserWavesView.putAll(waveViewProvider.retrievePerUserWaveView(sharedDomainParticipantId));
     }
+
+    if(LOG.isFineLoggable()) {
+      for(Map.Entry e : currentUserWavesView.entries()) {
+        LOG.fine("unfiltered view contains: " + e.getKey() + " " + 
e.getValue());
+      }
+    }
+
     return currentUserWavesView;
   }
 
@@ -167,16 +181,39 @@ public class SimpleSearchProviderImpl im
       for (WaveletId waveletId : waveletIds) {
         WaveletContainer waveletContainer = null;
         WaveletName waveletname = WaveletName.of(waveId, waveletId);
+
+        // TODO (alown): Find some way to use isLocalWavelet to do this 
properly!
         try {
-          waveletContainer = waveMap.getLocalWavelet(waveletname);
+          if(LOG.isFineLoggable()) {
+            LOG.fine("Trying as a remote wavelet");
+          }
+          waveletContainer = waveMap.getRemoteWavelet(waveletname);
         } catch (WaveletStateException e) {
-          LOG.severe(String.format("Failed to get local wavelet %s", 
waveletname.toString()), e);
+          LOG.severe(String.format("Failed to get remote wavelet %s", 
waveletname.toString()), e);
+        } catch (NullPointerException e) {
+          // This is a fairly normal case of it being a local-only wave.
+          // Yet this only seems to appear in the test suite.
+          // Continuing is completely harmless here.
+          LOG.info(String.format("%s is definitely not a remote wavelet. (Null 
key)", waveletname.toString()), e);
         }
+
+        if(waveletContainer == null) {
+          try {
+            if(LOG.isFineLoggable()) {
+                LOG.fine("Trying as a local wavelet");
+            }
+            waveletContainer = waveMap.getLocalWavelet(waveletname);
+          } catch (WaveletStateException e) {
+            LOG.severe(String.format("Failed to get local wavelet %s", 
waveletname.toString()), e);
+          }
+        }
+
         // TODO (Yuri Z.) This loop collects all the wavelets that match the
         // query, so the view is determined by the query. Instead we should
         // look at the user's wave view and determine if the view matches the 
query.
         try {
           if (waveletContainer == null || 
!waveletContainer.applyFunction(matchesFunction)) {
+            LOG.fine("----doesn't match: " + waveletContainer);
             continue;
           }
           if (view == null) {

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/Wave.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/Wave.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/Wave.java 
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/Wave.java 
Tue Aug 27 18:44:07 2013
@@ -31,6 +31,7 @@ import org.waveprotocol.box.server.persi
 import org.waveprotocol.wave.model.id.WaveId;
 import org.waveprotocol.wave.model.id.WaveletId;
 import org.waveprotocol.wave.model.id.WaveletName;
+import org.waveprotocol.wave.util.logging.Log;
 
 import java.util.Iterator;
 import java.util.concurrent.ConcurrentMap;
@@ -41,6 +42,8 @@ import java.util.concurrent.ConcurrentMa
  * @author [email protected] (Soren Lassen)
  */
 final class Wave implements Iterable<WaveletContainer> {
+  private static final Log LOG = Log.get(Wave.class);
+
   private class WaveletCreator<T extends WaveletContainer> implements 
Function<WaveletId, T> {
     private final WaveletContainer.Factory<T> factory;
 
@@ -120,6 +123,18 @@ final class Wave implements Iterable<Wav
       throw new WaveletStateException(
           "Interrupted looking up wavelet " + WaveletName.of(waveId, 
waveletId), e);
     }
+
+    if(LOG.isFineLoggable()) {
+      if(storedWavelets != null) {
+        if(storedWavelets.contains(waveletId)) {
+          LOG.fine("Wavelet is in storedWavelets");
+        }
+        if(waveletsMap.containsKey(waveletId)) {
+          LOG.fine("Wavelet is in wavletsMap");
+        }
+      }
+    }
+
     // Since waveletsMap is a computing map, we must call 
containsKey(waveletId)
     // to tell if waveletId is mapped, we cannot test if get(waveletId) 
returns null.
     if (storedWavelets != null && !storedWavelets.contains(waveletId)

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java
 Tue Aug 27 18:44:07 2013
@@ -138,7 +138,7 @@ public class WaveServerImpl implements W
 
         if (isLocalWavelet(waveletName)) {
           LOG.warning("Got commit update for local wavelet " + waveletName);
-          callback.onFailure(FederationErrors.badRequest("Received comit 
update to local wavelet"));
+          callback.onFailure(FederationErrors.badRequest("Received commit 
update to local wavelet"));
           return;
         }
 
@@ -153,17 +153,29 @@ public class WaveServerImpl implements W
         if (wavelet != null) {
           
wavelet.commit(CoreWaveletOperationSerializer.deserialize(committedVersion));
         } else {
-          // TODO(soren): This should really be changed to create the wavelet 
if it doesn't
-          // already exist and go get history up committedVersion. Moreover, 
when the
-          // protocol is enhanced to deliver commit updates reliably, we will 
probably need
-          // to only return success when we successfully retrieved history and 
persisted it all.
-         LOG.info("Got commit update for missing wavelet " + waveletName);
+          if(LOG.isInfoLoggable()) {
+            LOG.info("Got commit update for missing wavelet " + waveletName);
+          }
+          createAndCommitRemoteWavelet(waveletName, committedVersion);
         }
         callback.onSuccess();
       }
     };
   }
 
+  /**
+   * Creates the non-existent remote wavelet container at this server and 
commits it.
+   * Calling commit at this known version, forces the history to be fetched up 
to this point.
+   * TODO (alown): Possible race condition here with update? (Though I don't 
think it would result in
+   * anything more serious than repeated history fetches.)
+   */
+  private void createAndCommitRemoteWavelet(WaveletName waveletName, 
ProtocolHashedVersion committedVersion) {
+    RemoteWaveletContainer wavelet = getOrCreateRemoteWavelet(waveletName);
+    HashedVersion v = 
CoreWaveletOperationSerializer.deserialize(committedVersion);
+    wavelet.commit(v);
+    LOG.info("Passed commit message for version " + v.getVersion() + " to 
RemoteWavelet");
+  }
+
   //
   // WaveletFederationProvider implementation.
   //

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java
 Tue Aug 27 18:44:07 2013
@@ -57,6 +57,8 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import javax.annotation.Nullable;
+
 /**
  * Contains the history of a wavelet - applied and transformed deltas plus the
  * content of the wavelet.
@@ -484,7 +486,7 @@ abstract class WaveletContainerImpl impl
 
   @Override
   public void requestHistory(HashedVersion startVersion, HashedVersion 
endVersion,
-      Receiver<ByteStringMessage<ProtocolAppliedWaveletDelta>> receiver) 
+      Receiver<ByteStringMessage<ProtocolAppliedWaveletDelta>> receiver)
       throws AccessControlException, WaveletStateException {
     acquireReadLock();
     try {
@@ -548,7 +550,11 @@ abstract class WaveletContainerImpl impl
     }
   }
 
+  @Nullable
   protected HashedVersion getCurrentVersion() {
+    if(waveletState == null)
+      return null;
+
     return waveletState.getCurrentVersion();
   }
 

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java
 Tue Aug 27 18:44:07 2013
@@ -57,10 +57,8 @@ public final class WindowTitleHandler im
 
   @Override
   public void onOpened(WaveContext wave) {
-    Document document =
-        
wave.getConversations().getRoot().getRootThread().getFirstBlip().getContent();
-    String waveTitle = TitleHelper.extractTitle(document);
-     String windowTitle = formatTitle(waveTitle);
+    String waveTitle = TitleHelper.getTitle(wave);
+    String windowTitle = formatTitle(waveTitle);
     if (waveTitle == null || waveTitle.isEmpty()) {
       windowTitle = DEFAULT_TITLE;
     }
@@ -76,4 +74,4 @@ public final class WindowTitleHandler im
   private String formatTitle(String title) {
     return  title + " - " + Session.get().getAddress() + " - " + APP_NAME;
   }
-}
\ No newline at end of file
+}

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java
 Tue Aug 27 18:44:07 2013
@@ -179,8 +179,7 @@ public final class WaveBasedDigest
 
   @Override
   public String getTitle() {
-    return TitleHelper.extractTitle(
-        
wave.getConversations().getRoot().getRootThread().getFirstBlip().getContent());
+    return TitleHelper.getTitle(wave);
   }
 
   @Override

Modified: incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java 
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java Tue Aug 
27 18:44:07 2013
@@ -463,7 +463,7 @@ public interface StageTwo {
         }
       };
       WaveViewImpl<OpBasedWavelet> wave =
-          WaveViewImpl.create(waveletFactory, getWaveData().getWaveId(), 
getIdGenerator(),
+          WaveViewImpl.create(waveletFactory, snapshot.getWaveId(), 
getIdGenerator(),
               getSignedInUser(), WaveletConfigurator.ADD_CREATOR);
 
       // Populate the initial state.

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
 Tue Aug 27 18:44:07 2013
@@ -90,7 +90,10 @@ public final class Reader implements Foc
   }
 
   public boolean isRead(BlipView blipUi) {
-    return !supplement.isUnread(models.getBlip(blipUi));
+    ConversationBlip blip = models.getBlip(blipUi);
+    if(blip != null)
+      return !supplement.isUnread(blip);
+    return false;
   }
 
   //

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/TitleHelper.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/TitleHelper.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/TitleHelper.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/TitleHelper.java
 Tue Aug 27 18:44:07 2013
@@ -19,7 +19,9 @@
 
 package org.waveprotocol.wave.model.conversation;
 
-
+//TODO (alown): should the WaveContext live under model instead?
+import org.waveprotocol.box.webclient.search.WaveContext;
+import org.waveprotocol.wave.model.document.Document;
 import org.waveprotocol.wave.model.document.MutableDocument;
 import org.waveprotocol.wave.model.document.ReadableWDocument;
 import org.waveprotocol.wave.model.document.operation.Attributes;
@@ -239,6 +241,50 @@ public final class TitleHelper {
     }
   }
 
+  /**
+   * An error-absorbing title retrieving convenience function.
+   * Please use this rather than doing:
+   * 
wave.getConversations().getRoot().getRootThread().getFirstBlip().getContent()
+   * as that is the cause of _many_ shiny's in the client.
+   * In the event something went wrong, or no title exists will return the 
empty string.
+   * This is deliberate, so that it doesn't propagate an error for a rather 
non-critical
+   * code path.
+   */
+  public static String getTitle(WaveContext waveCtx) {
+    ObservableConversationView conversations = waveCtx.getConversations();
+    if(conversations == null) {
+      return "";
+    }
+
+    ObservableConversation rootConversation = conversations.getRoot();
+    if(rootConversation == null) {
+      return "";
+    }
+
+    ObservableConversationThread rootThread = rootConversation.getRootThread();
+    if(rootThread == null) {
+      return "";
+    }
+
+    ObservableConversationBlip firstBlip = rootThread.getFirstBlip();
+    if(firstBlip == null) {
+      return "";
+    }
+
+    Document doc = firstBlip.getContent();
+    if(doc == null) {
+      return "";
+    }
+
+    String title = extractTitle(doc);
+    if(title == null) {
+      return "";
+    }
+
+    return title;
+  }
+
   private TitleHelper() {
   }
 }
+

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGenerator.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGenerator.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGenerator.java 
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGenerator.java 
Tue Aug 27 18:44:07 2013
@@ -61,6 +61,13 @@ public interface IdGenerator {
   WaveletId newConversationRootWaveletId();
 
   /**
+   * Creates/gets a root wavelet id for the given waveId
+   * (Federation happy version of newConversationRootWaveletId)
+   */
+
+  WaveletId buildConversationRootWaveletId(WaveId waveId);
+
+  /**
    * Creates a user data wavelet id.
    *
    * Per-user data wavelets are specified by a leading token "user" followed by

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java 
(original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java 
Tue Aug 27 18:44:07 2013
@@ -75,6 +75,7 @@ public class IdGeneratorImpl implements 
     return build(IdConstants.BLIP_PREFIX, peekUniqueToken());
   }
 
+  //NOTE: These are _NOT_ federation happy. Ensure that your caller is!
   @Override
   public WaveId newWaveId() {
     return WaveId.of(defaultDomain, newId(WAVE_PREFIX));
@@ -91,6 +92,11 @@ public class IdGeneratorImpl implements 
   }
 
   @Override
+  public WaveletId buildConversationRootWaveletId(WaveId waveId) {
+    return WaveletId.of(waveId.getDomain(), CONVERSATION_ROOT_WAVELET);
+  }
+
+  @Override
   public WaveletId newUserDataWaveletId(String address) {
     // TODO(anorth): Take ParticipantId as a parameter after moving it
     // into model.id package.

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java
 Tue Aug 27 18:44:07 2013
@@ -131,7 +131,7 @@ public final class WaveViewImpl<T extend
     this.viewer = viewer;
     this.idGenerator = idGenerator;
     this.configurator = configurator;
-    this.rootId = idGenerator.newConversationRootWaveletId();
+    this.rootId = idGenerator.buildConversationRootWaveletId(waveId);
     this.userDataId = idGenerator.newUserDataWaveletId(viewer.getAddress());
   }
 

Modified: 
incubator/wave/trunk/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java
 (original)
+++ 
incubator/wave/trunk/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java
 Tue Aug 27 18:44:07 2013
@@ -207,7 +207,7 @@ public class SimpleSearchProviderImplTes
 
     waveMap =
         new WaveMap(waveletStore, notifiee, notifiee, 
localWaveletContainerFactory,
-            remoteWaveletContainerFactory, "example.com", lookupExecutor);
+            remoteWaveletContainerFactory, DOMAIN, lookupExecutor);
     searchProvider = new SimpleSearchProviderImpl(DOMAIN, digester, waveMap, 
waveViewProvider);
   }
 

Modified: 
incubator/wave/trunk/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java?rev=1517913&r1=1517912&r2=1517913&view=diff
==============================================================================
--- 
incubator/wave/trunk/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java
 (original)
+++ 
incubator/wave/trunk/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java
 Tue Aug 27 18:44:07 2013
@@ -65,7 +65,7 @@ public class CcBasedWaveViewTest extends
   private static final WaveId WAVE_ID = WaveId.of("example.com", "waveId_1");
   private static final WaveletId GENERATED_WAVELET_ID = 
WaveletId.of("example.com", "some_id");
   private static final WaveletId ROOT_WAVELET_ID
-      = new IdGeneratorImpl("example.com", 
null).newConversationRootWaveletId();
+      = new IdGeneratorImpl("example.com", 
null).buildConversationRootWaveletId(WAVE_ID);
   private static final String GENERATED_BLIP_ID = "some blip id";
   private static final ParticipantId USER_ID = new 
ParticipantId("[email protected]");
   private static final SchemaProvider SCHEMAS = SchemaCollection.empty();
@@ -214,6 +214,11 @@ public class CcBasedWaveViewTest extends
     }
 
     @Override
+    public WaveletId buildConversationRootWaveletId(WaveId waveId) {
+      return ROOT_WAVELET_ID;
+    }
+
+    @Override
     public String newDataDocumentId() {
       throw new UnsupportedOperationException("Unsupported for test");
     }


Reply via email to