/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.waveprotocol.box.server.frontend.testing; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Maps; import org.waveprotocol.box.common.DeltaSequence; import org.waveprotocol.box.common.comms.WaveClientRpc; import org.waveprotocol.box.server.common.CoreWaveletOperationSerializer; import org.waveprotocol.box.server.util.WaveletDataUtil; import org.waveprotocol.box.server.waveserver.WaveletProvider.SubmitRequestListener; import org.waveprotocol.wave.federation.Proto.ProtocolWaveletDelta; import org.waveprotocol.wave.model.id.IdFilter; 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.model.operation.wave.TransformedWaveletDelta; import org.waveprotocol.wave.model.version.HashedVersion; import org.waveprotocol.wave.model.wave.ParticipantId; import org.waveprotocol.wave.model.wave.data.WaveletData; import java.util.Collection; import java.util.Map; import javax.annotation.Nullable; /** * A fake single-user wave server which only echoes back submitted deltas and * corresponding index wave deltas. * * @author mk.mateng@gmail.com (Michael Kuntzman) */ public class FakeWaveServer extends FakeClientFrontend { /** Fake application timestamp for confirming a successful submit. */ private static final long APP_TIMESTAMP = 0; /** Known wavelet states, excluding index wavelets. */ private final Map<WaveId, Map<WaveletId, WaveletData>> waves = Maps.newHashMap(); /** A history of submitted deltas, per wavelet. Does not store generated index deltas. */ private final ListMultimap<WaveletName, TransformedWaveletDelta> deltas = ArrayListMultimap.create(); /** The current versions of the user's wavelets, including index wavelets */ private final Map<WaveletName, HashedVersion> versions = Maps.newHashMap(); /** The user that is connected to this server */ private ParticipantId user = null; @Override public void openRequest(ParticipantId participant, WaveId waveId, IdFilter waveletIdFilter, Collection<WaveClientRpc.WaveletVersion> knownWavelets, OpenListener openListener) { if (user == null) { user = participant; } else { Preconditions.checkArgument(participant.equals(user), "Unexpected user"); } super.openRequest(participant, waveId, waveletIdFilter, knownWavelets, openListener); Map<WaveletId, WaveletData> wavelets = waves.get(waveId); if (wavelets != null) { // Send any deltas we have in this wave to the client, in the order we got // them. for (WaveletData wavelet : wavelets.values()) { WaveletName name = WaveletName.of(wavelet.getWaveId(), wavelet.getWaveletId()); waveletUpdate(wavelet, DeltaSequence.of(deltas.get(name))); } } } @Override public void submitRequest(ParticipantId loggedInUser, WaveletName waveletName, ProtocolWaveletDelta delta, @Nullable String channelId, SubmitRequestListener listener) { super.submitRequest(loggedInUser, waveletName, delta, channelId, listener); Map<WaveletId, WaveletData> wavelets = waves.get(waveletName.waveId); if (wavelets == null) { wavelets = Maps.newHashMap(); waves.put(waveletName.waveId, wavelets); } WaveletData wavelet = wavelets.get(waveletName.waveletId); if (wavelet == null) { long dummyCreationTime = System.currentTimeMillis(); wavelet = WaveletDataUtil.createEmptyWavelet( waveletName, ParticipantId.ofUnsafe(delta.getAuthor()), HashedVersion.unsigned(0), dummyCreationTime); wavelets.put(waveletName.waveletId, wavelet); } // Add the delta to the history and update the wavelet's version. HashedVersion resultingVersion = updateAndGetVersion(waveletName, delta.getOperationCount()); TransformedWaveletDelta versionedDelta = CoreWaveletOperationSerializer.deserialize(delta, resultingVersion, APP_TIMESTAMP); deltas.put(waveletName, versionedDelta); // Confirm submit success. doSubmitSuccess(waveletName, resultingVersion, APP_TIMESTAMP); // Send an update echoing the submitted delta. Note: the document state is // ignored. waveletUpdate(wavelet, DeltaSequence.of(versionedDelta)); // Send a corresponding update of the index wave. } /** * Updates and returns the version of a given wavelet. * * @param waveletName of the wavelet whose version to update. * @param operationsCount applied to the wavelet. * @return the new hashed version of the wavelet. */ private HashedVersion updateAndGetVersion(WaveletName waveletName, int operationsCount) { // Get the current version. HashedVersion version = versions.get(waveletName); // Calculate the new version. if (version != null) { version = HashedVersion.unsigned(version.getVersion() + operationsCount); } else { version = HashedVersion.unsigned(operationsCount); } // Store and return the new version. versions.put(waveletName, version); return version; } }