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"); }
