Package org.waveprotocol.wave.client.concurrencycontrol

Source Code of org.waveprotocol.wave.client.concurrencycontrol.LiveChannelBinder

/**
* 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.wave.client.concurrencycontrol;

import com.google.common.base.Preconditions;
import com.google.gwt.user.client.Command;

import org.waveprotocol.wave.client.wave.WaveDocuments;
import org.waveprotocol.wave.concurrencycontrol.channel.Accessibility;
import org.waveprotocol.wave.concurrencycontrol.channel.OperationChannel;
import org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexer;
import org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexer.KnownWavelet;
import org.waveprotocol.wave.concurrencycontrol.common.CorruptionDetail;
import org.waveprotocol.wave.concurrencycontrol.wave.CcDocument;
import org.waveprotocol.wave.model.id.IdFilter;
import org.waveprotocol.wave.model.id.ModernIdSerialiser;
import org.waveprotocol.wave.model.id.WaveletId;
import org.waveprotocol.wave.model.util.CollectionUtils;
import org.waveprotocol.wave.model.util.StringMap;
import org.waveprotocol.wave.model.wave.ObservableWavelet;
import org.waveprotocol.wave.model.wave.WaveViewListener;
import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet;
import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl;

import java.util.Collection;

/**
* Binds operation channels from a {@link OperationChannelMultiplexer mux} with
* the output sinks of wavelets, and keeps binding matching channel/wavelet
* pairs while live.
*
* @author hearnden@google.com (David Hearnden)
*/
public final class LiveChannelBinder
    implements WaveViewListener, OperationChannelMultiplexer.Listener {

  private final StaticChannelBinder binder;
  private final WaveletOperationalizer operationalizer;
  private final WaveViewImpl<OpBasedWavelet> wave;
  private final OperationChannelMultiplexer mux;
  private final Command whenOpened;

  /**
   * Operation channels waiting to be bound. This map is populated from {@link
   * #onOperationChannelCreated(OperationChannel, ObservableWaveletData,
   * Accessibility)}, and depleted by {@link #connect(String)}.
   */
  private final StringMap<OperationChannel> channels = CollectionUtils.createStringMap();

  //
  // The binding flow is not completely trivial, because it has to work with
  // two directions of control flow:
  //
  // Client-created wavelets:
  // 1. wavelet shows up in the model
  // 2. this binder tells mux to create op channel,
  // 3. its operation channel shows up, then
  // 4. wavelet and op channel are bound together.
  //
  // Server-created wavelets:
  // 1. op channel shows up in the mux,
  // 2. this binder builds wavelet and puts it in the model,
  // 3. wavelet shows up in the model, then
  // 4. wavelet and op channel are bound together.
  //
  // Also, the initial set of operation channels when opening a wave with known
  // wavelet states is just like the server-created wavelet flow, except without
  // step 2.
  //

  private LiveChannelBinder(StaticChannelBinder binder, WaveletOperationalizer operationalizer,
      WaveViewImpl<OpBasedWavelet> wave, OperationChannelMultiplexer mux, Command whenOpened) {
    this.binder = binder;
    this.operationalizer = operationalizer;
    this.wave = wave;
    this.mux = mux;
    this.whenOpened = whenOpened;
  }

  /**
   * Opens a mux, binding its operation channels with operation-supporting
   * wavelets.
   */
  public static void openAndBind(WaveletOperationalizer operationalizer,
      WaveViewImpl<OpBasedWavelet> wave,
      WaveDocuments<? extends CcDocument> docRegistry,
      OperationChannelMultiplexer mux,
      IdFilter filter,
      Command whenOpened) {
    StaticChannelBinder staticBinder = new StaticChannelBinder(operationalizer, docRegistry);
    LiveChannelBinder liveBinder =
        new LiveChannelBinder(staticBinder, operationalizer, wave, mux, whenOpened);

    final Collection<KnownWavelet> remoteWavelets = CollectionUtils.createQueue();
    final Collection<ObservableWaveletData> localWavelets = CollectionUtils.createQueue();
    for (ObservableWaveletData wavelet : operationalizer.getWavelets()) {
      // Version 0 wavelets must be wavelets that the client has created in this
      // session. They are not to be included in the known-wavelet collection,
      // because the server does not know about them.
      if (wavelet.getVersion() > 0) {
        remoteWavelets.add(
            new KnownWavelet(wavelet, wavelet.getHashedVersion(), Accessibility.READ_WRITE));
      } else {
        localWavelets.add(wavelet);
      }
    }

    // Start listening to wave events and channel events.
    wave.addListener(liveBinder);
    // This binder only starts getting events once open() has been called, since
    // that is what sets this binder as a mux listener. Since wavelet-to-channel
    // binding occurs through event callbacks, this listener setting must occur
    // before trying to bind localWavelets.
    mux.open(liveBinder, filter, remoteWavelets);
    for (ObservableWaveletData local : localWavelets) {
      mux.createOperationChannel(local.getWaveletId(), local.getCreator());
    }
  }

  @Override
  public void onFailed(CorruptionDetail detail) {
    throw new RuntimeException(detail);
  }

  @Override
  public void onOpenFinished() {
    if (whenOpened != null) {
      whenOpened.execute();
    }
  }

  //
  // Wavelet and Channel lifecycle events:
  //

  @Override
  public void onWaveletAdded(ObservableWavelet wavelet) {
    String id = ModernIdSerialiser.INSTANCE.serialiseWaveletId(wavelet.getId());
    if (channels.containsKey(id)) {
      connect(id);
    } else {
      // This will trigger the onOperationChannelCreated callback below.
      mux.createOperationChannel(wavelet.getId(), wavelet.getCreatorId());
    }
  }

  @Override
  public void onOperationChannelCreated(
      OperationChannel channel, ObservableWaveletData snapshot, Accessibility accessibility) {
    WaveletId wid = snapshot.getWaveletId();
    String id = ModernIdSerialiser.INSTANCE.serialiseWaveletId(wid);

    Preconditions.checkState(!channels.containsKey(id));
    channels.put(id, channel);

    if (wave.getWavelet(wid) != null) {
      connect(id);
    } else {
      // This will trigger the onWaveletAdded callback above.
      wave.addWavelet(operationalizer.operationalize(snapshot));
    }
  }

  @Override
  public void onWaveletRemoved(ObservableWavelet wavelet) {
    // TODO
  }

  @Override
  public void onOperationChannelRemoved(OperationChannel channel, WaveletId waveletId) {
    // TODO
  }

  private void connect(String id) {
    binder.bind(id, removeAndReturn(channels, id));
  }

  // Something that should have been on StringMap from the beginning.
  private static <V> V removeAndReturn(StringMap<V> map, String key) {
    V value = map.get(key);
    map.remove(key);
    return value;
  }
}
TOP

Related Classes of org.waveprotocol.wave.client.concurrencycontrol.LiveChannelBinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.