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