Author: al
Date: Tue Aug 27 19:21:36 2013
New Revision: 1517940

URL: http://svn.apache.org/r1517940
Log:
Merge branch 'trunk-staging' into 0.4-release-staging

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

Modified: 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/ClientFrontendImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/WaveletInfo.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/WaveletInfo.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/WaveletInfo.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/frontend/WaveletInfo.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/LocalWaveletContainer.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/MemoryPerUserWaveViewHandlerImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/PerUserWaveViewDistpatcher.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/RemoteWaveletContainerImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/Wave.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/Wave.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/Wave.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/Wave.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveServerImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/server/waveserver/WaveletContainerImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/client/WindowTitleHandler.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/box/webclient/search/WaveBasedDigest.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/wave/client/StageTwo.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/client/StageTwo.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/client/StageTwo.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/client/StageTwo.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/wave/model/conversation/TitleHelper.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/conversation/TitleHelper.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/conversation/TitleHelper.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/conversation/TitleHelper.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGenerator.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGenerator.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGenerator.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGenerator.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/id/IdGeneratorImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/src/org/waveprotocol/wave/model/wave/opbased/WaveViewImpl.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/test/org/waveprotocol/box/server/waveserver/SimpleSearchProviderImplTest.java
 Tue Aug 27 19:21:36 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/branches/wave-0.4-release/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/branches/wave-0.4-release/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java?rev=1517940&r1=1517939&r2=1517940&view=diff
==============================================================================
--- 
incubator/wave/branches/wave-0.4-release/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java
 (original)
+++ 
incubator/wave/branches/wave-0.4-release/test/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWaveViewTest.java
 Tue Aug 27 19:21:36 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