/** * Copyright 2009 Google Inc. * * Licensed 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; import com.google.common.base.Preconditions; import com.google.protobuf.RpcCallback; import com.google.protobuf.RpcController; import org.waveprotocol.box.common.comms.WaveClientRpc.ProtocolAuthenticate; import org.waveprotocol.box.common.comms.WaveClientRpc.ProtocolAuthenticationResult; import org.waveprotocol.box.common.comms.WaveClientRpc.ProtocolOpenRequest; import org.waveprotocol.box.common.comms.WaveClientRpc.ProtocolSubmitRequest; import org.waveprotocol.box.common.comms.WaveClientRpc.ProtocolSubmitResponse; import org.waveprotocol.box.common.comms.WaveClientRpc.ProtocolWaveClientRpc; import org.waveprotocol.box.common.comms.WaveClientRpc.ProtocolWaveletUpdate; import org.waveprotocol.box.server.common.CoreWaveletOperationSerializer; import org.waveprotocol.box.server.common.SnapshotSerializer; import org.waveprotocol.box.server.rpc.ServerRpcController; import org.waveprotocol.box.server.waveserver.WaveletProvider.SubmitRequestListener; import org.waveprotocol.wave.model.id.IdFilter; import org.waveprotocol.wave.model.id.InvalidIdException; import org.waveprotocol.wave.model.id.ModernIdSerialiser; 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.util.logging.Log; import java.util.Collections; import java.util.List; import javax.annotation.Nullable; /** * RPC interface implementation for the wave server. Adapts incoming and * outgoing RPCs to the client frontend interface. * * */ public class WaveClientRpcImpl implements ProtocolWaveClientRpc.Interface { private static final Log LOG = Log.get(WaveClientRpcImpl.class); private final ClientFrontend frontend; private final boolean handleAuthentication; /** * Creates a new RPC interface to the front-end. * * @param frontend front-end to which to forward requests * @param handleAuthentication whether to handle authentication; it's * otherwise expected to be handled before this class */ public static WaveClientRpcImpl create(ClientFrontend frontend, boolean handleAuthentication) { return new WaveClientRpcImpl(frontend, handleAuthentication); } private WaveClientRpcImpl(ClientFrontend frontend, boolean handleAuthentication) { this.frontend = frontend; this.handleAuthentication = handleAuthentication; } @Override public void open(final RpcController controller, ProtocolOpenRequest request, final RpcCallback<ProtocolWaveletUpdate> done) { WaveId waveId; try { waveId = ModernIdSerialiser.INSTANCE.deserialiseWaveId(request.getWaveId()); } catch (InvalidIdException e) { LOG.warning("Invalid id in open", e); controller.setFailed(e.getMessage()); return; } IdFilter waveletIdFilter = IdFilter.of(Collections.<WaveletId>emptySet(), request.getWaveletIdPrefixList()); ParticipantId loggedInUser = asBoxController(controller).getLoggedInUser(); frontend.openRequest(loggedInUser, waveId, waveletIdFilter, request.getKnownWaveletList(), new ClientFrontend.OpenListener() { @Override public void onFailure(String errorMessage) { LOG.warning("openRequest failure: " + errorMessage); controller.setFailed(errorMessage); } @Override public void onUpdate(WaveletName waveletName, @Nullable CommittedWaveletSnapshot snapshot, List<TransformedWaveletDelta> deltas, @Nullable HashedVersion committedVersion, Boolean hasMarker, String channel_id) { ProtocolWaveletUpdate.Builder builder = ProtocolWaveletUpdate.newBuilder(); if (hasMarker != null) { builder.setMarker(hasMarker.booleanValue()); } if (channel_id != null) { builder.setChannelId(channel_id); } builder.setWaveletName(ModernIdSerialiser.INSTANCE.serialiseWaveletName(waveletName)); for (TransformedWaveletDelta d : deltas) { // TODO(anorth): Add delta application metadata to the result // when the c/s protocol supports it. builder.addAppliedDelta(CoreWaveletOperationSerializer.serialize(d)); } if (!deltas.isEmpty()) { builder.setResultingVersion(CoreWaveletOperationSerializer.serialize( deltas.get((deltas.size() - 1)).getResultingVersion())); } if (snapshot != null) { Preconditions.checkState(committedVersion.equals(snapshot.committedVersion), "Mismatched commit versions, snapshot: " + snapshot.committedVersion + " expected: " + committedVersion); builder.setSnapshot(SnapshotSerializer.serializeWavelet(snapshot.snapshot, snapshot.committedVersion)); builder.setResultingVersion(CoreWaveletOperationSerializer.serialize( snapshot.snapshot.getHashedVersion())); builder.setCommitNotice(CoreWaveletOperationSerializer.serialize( snapshot.committedVersion)); } else { if (committedVersion != null) { builder.setCommitNotice( CoreWaveletOperationSerializer.serialize(committedVersion)); } } done.run(builder.build()); } }); } @Override public void submit(RpcController controller, ProtocolSubmitRequest request, final RpcCallback<ProtocolSubmitResponse> done) { WaveletName waveletName = null; try { waveletName = ModernIdSerialiser.INSTANCE.deserialiseWaveletName(request.getWaveletName()); } catch (InvalidIdException e) { LOG.warning("Invalid id in submit", e); controller.setFailed(e.getMessage()); return; } String channelId; if (request.hasChannelId()) { channelId = request.getChannelId(); } else { channelId = null; } ParticipantId loggedInUser = asBoxController(controller).getLoggedInUser(); frontend.submitRequest(loggedInUser, waveletName, request.getDelta(), channelId, new SubmitRequestListener() { @Override public void onFailure(String error) { done.run(ProtocolSubmitResponse.newBuilder() .setOperationsApplied(0).setErrorMessage(error).build()); } @Override public void onSuccess(int operationsApplied, HashedVersion hashedVersionAfterApplication, long applicationTimestamp) { done.run(ProtocolSubmitResponse.newBuilder() .setOperationsApplied(operationsApplied) .setHashedVersionAfterApplication( CoreWaveletOperationSerializer.serialize(hashedVersionAfterApplication)) .build()); // TODO(arb): applicationTimestamp?? } }); } @Override public void authenticate(RpcController controller, ProtocolAuthenticate request, RpcCallback<ProtocolAuthenticationResult> done) { Preconditions.checkState(handleAuthentication, "ProtocolAuthenticate should be handled in ServerRpcProvider"); done.run(ProtocolAuthenticationResult.getDefaultInstance()); } ServerRpcController asBoxController(RpcController controller) { // This cast is safe (because of how the WaveClientRpcImpl is instantiated). We need to do this // because ServerRpcController implements an autogenerated interface. return (ServerRpcController) controller; } }