org.waveprotocol.box.server.waveserver.WaveletNotificationDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.waveprotocol.box.server.waveserver.WaveletNotificationDispatcher.java

Source

/**
 * 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);
            }
        };
    }
}