Package org.waveprotocol.box.server.waveserver

Source Code of org.waveprotocol.box.server.waveserver.WaveletNotificationDispatcher

/**
* 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.waveserver;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.protobuf.ByteString;

import org.waveprotocol.box.common.DeltaSequence;
import org.waveprotocol.box.server.common.CoreWaveletOperationSerializer;
import org.waveprotocol.box.server.util.WaveletDataUtil;
import org.waveprotocol.wave.federation.FederationErrorProto.FederationError;
import org.waveprotocol.wave.federation.FederationHostBridge;
import org.waveprotocol.wave.federation.Proto.ProtocolHashedVersion;
import org.waveprotocol.wave.federation.WaveletFederationListener;
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.data.ReadableWaveletData;
import org.waveprotocol.wave.util.logging.Log;

import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;

/**
* Forwards wave notifications to wave bus subscribers and remote wave servers.
*
* Swallows any runtime exception from a wave bus subscriber but not removes that
* subscriber. The wave server used to do this swallowing but really things are
* in bad shape if a subscriber throws a runtime exception.
* TODO(anorth): Remove this catch and let the server crash.
*
* @author soren@google.com (Soren Lassen)
*/
class WaveletNotificationDispatcher implements WaveBus, WaveletNotificationSubscriber {

  private static final Log LOG = Log.get(WaveletNotificationDispatcher.class);

  /** Picks out the transformed deltas from a list of delta records. */
  private static ImmutableList<TransformedWaveletDelta> transformedDeltasOf(
      Iterable<WaveletDeltaRecord> deltaRecords) {
    ImmutableList.Builder<TransformedWaveletDelta> transformedDeltas = ImmutableList.builder();
    for (WaveletDeltaRecord deltaRecord : deltaRecords) {
      transformedDeltas.add(deltaRecord.getTransformedDelta());
    }
    return transformedDeltas.build();
  }

  /** Picks out the byte strings of the applied deltas from a list of delta records. */
  private static ImmutableList<ByteString> serializedAppliedDeltasOf(
      Iterable<WaveletDeltaRecord> deltaRecords) {
    ImmutableList.Builder<ByteString> serializedAppliedDeltas = ImmutableList.builder();
    for (WaveletDeltaRecord deltaRecord : deltaRecords) {
      serializedAppliedDeltas.add(deltaRecord.getAppliedDelta().getByteString());
    }
    return serializedAppliedDeltas.build();
  }

  private final ImmutableSet<String> localDomains;
  private final WaveletFederationListener.Factory federationHostFactory;
  private final CopyOnWriteArraySet<WaveBus.Subscriber> subscribers =
      new CopyOnWriteArraySet<WaveBus.Subscriber>();

  /** Maps remote domains to wave server stubs for those domains. */
  private final LoadingCache<String, WaveletFederationListener> federationHosts =
      CacheBuilder.newBuilder().build(new CacheLoader<String, WaveletFederationListener>() {
    @Override
    public WaveletFederationListener load(String domain) {
      return federationHostFactory.listenerForDomain(domain);
    }
  });

  /**
   * Constructor.
   *
   * @param certificateManager knows what the local domains are
   * @param federationHostFactory manufactures federation host instances for
   *        remote domains
   */
  @Inject
  public WaveletNotificationDispatcher(
      CertificateManager certificateManager,
      @FederationHostBridge WaveletFederationListener.Factory federationHostFactory) {
    this.localDomains = certificateManager.getLocalDomains();
    this.federationHostFactory = federationHostFactory;
  }

  @Override
  public void subscribe(Subscriber s) {
    subscribers.add(s);
  }

  @Override
  public void unsubscribe(Subscriber s) {
    subscribers.remove(s);
  }

  @Override
  public void waveletUpdate(ReadableWaveletData wavelet, ImmutableList<WaveletDeltaRecord> deltas,
      ImmutableSet<String> domainsToNotify) {
    DeltaSequence sequence = DeltaSequence.of(transformedDeltasOf(deltas));
    for (WaveBus.Subscriber s : subscribers) {
      try {
        s.waveletUpdate(wavelet, sequence);
      } catch (RuntimeException e) {
        LOG.severe("Runtime exception in update to wave bus subscriber " + s, e);
      }
    }

    Set<String> remoteDomainsToNotify = Sets.difference(domainsToNotify, localDomains);
    if (!remoteDomainsToNotify.isEmpty()) {
      ImmutableList<ByteString> serializedAppliedDeltas = serializedAppliedDeltasOf(deltas);
      for (String domain : remoteDomainsToNotify) {
        try {
          federationHosts.get(domain).waveletDeltaUpdate(WaveletDataUtil.waveletNameOf(wavelet),
              serializedAppliedDeltas, federationCallback("delta update"));
        } catch (ExecutionException ex) {
          throw new RuntimeException(ex);
        }
      }
    }
  }

  @Override
  public void waveletCommitted(WaveletName waveletName, HashedVersion version,
      ImmutableSet<String> domainsToNotify) {
    for (WaveBus.Subscriber s : subscribers) {
      try {
        s.waveletCommitted(waveletName, version);
      } catch (RuntimeException e) {
        LOG.severe("Runtime exception in commit to wave bus subscriber " + s, e);
      }
    }

    Set<String> remoteDomainsToNotify = Sets.difference(domainsToNotify, localDomains);
    if (!remoteDomainsToNotify.isEmpty()) {
      ProtocolHashedVersion serializedVersion = CoreWaveletOperationSerializer.serialize(version);
      for (String domain : remoteDomainsToNotify) {
        try {
          federationHosts.get(domain).waveletCommitUpdate(
              waveletName, serializedVersion, federationCallback("commit notice"));
        } catch (ExecutionException ex) {
          throw new RuntimeException(ex);
        }
      }
    }
  }

  private WaveletFederationListener.WaveletUpdateCallback federationCallback(
      final String description) {
    return new WaveletFederationListener.WaveletUpdateCallback() {
      @Override
      public void onSuccess() {
        LOG.info(description + " success");
      }

      @Override
      public void onFailure(FederationError error) {
        LOG.warning(description + " failure: " + error);
      }
    };
  }
}
TOP

Related Classes of org.waveprotocol.box.server.waveserver.WaveletNotificationDispatcher

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.