Package com.google.walkaround.wave.server

Source Code of com.google.walkaround.wave.server.WaveletCreator

/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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 com.google.walkaround.wave.server;

import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.walkaround.proto.ObsoleteWaveletMetadata;
import com.google.walkaround.proto.gson.ConvMetadataGsonImpl;
import com.google.walkaround.proto.gson.ObsoleteUdwMetadataGsonImpl;
import com.google.walkaround.proto.gson.ObsoleteWaveletMetadataGsonImpl;
import com.google.walkaround.slob.server.AccessDeniedException;
import com.google.walkaround.slob.server.GsonProto;
import com.google.walkaround.slob.server.SlobAlreadyExistsException;
import com.google.walkaround.slob.server.SlobStore;
import com.google.walkaround.slob.shared.ChangeData;
import com.google.walkaround.slob.shared.ClientId;
import com.google.walkaround.slob.shared.SlobId;
import com.google.walkaround.util.server.RetryHelper;
import com.google.walkaround.util.server.RetryHelper.PermanentFailure;
import com.google.walkaround.util.server.RetryHelper.RetryableFailure;
import com.google.walkaround.util.server.appengine.CheckedDatastore;
import com.google.walkaround.util.server.appengine.CheckedDatastore.CheckedTransaction;
import com.google.walkaround.util.shared.RandomBase64Generator;
import com.google.walkaround.wave.server.WaveletDirectory.ObjectIdAlreadyKnown;
import com.google.walkaround.wave.server.auth.StableUserId;
import com.google.walkaround.wave.server.conv.ConvMetadataStore;
import com.google.walkaround.wave.server.conv.ConvStore;
import com.google.walkaround.wave.server.model.InitialOps;
import com.google.walkaround.wave.server.model.ServerMessageSerializer;
import com.google.walkaround.wave.server.udw.UdwStore;
import com.google.walkaround.wave.server.udw.UdwDirectory;
import com.google.walkaround.wave.shared.WaveSerializer;

import org.waveprotocol.wave.model.operation.wave.WaveletOperation;
import org.waveprotocol.wave.model.wave.ParticipantId;

import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Nullable;

/**
* Creates wavelets.
*
* @author danilatos@google.com (Daniel Danilatos)
* @author ohler@google.com (Christian Ohler)
*/
public class WaveletCreator {

  @SuppressWarnings("unused")
  private static final Logger log = Logger.getLogger(WaveletCreator.class.getName());

  private static final WaveSerializer SERIALIZER =
      new WaveSerializer(new ServerMessageSerializer());

  private final WaveletDirectory waveletDirectory;
  private final UdwDirectory udwDirectory;
  private final ParticipantId participantId;
  private final StableUserId userId;
  private final SlobStore convStore;
  private final SlobStore udwStore;
  private final RandomBase64Generator random64;
  private final CheckedDatastore datastore;
  private final ConvMetadataStore convMetadataStore;

  @Inject
  public WaveletCreator(
      WaveletDirectory waveletDirectory,
      UdwDirectory udwDirectory,
      ParticipantId participantId,
      StableUserId userId,
      @ConvStore SlobStore convStore,
      @UdwStore SlobStore udwStore,
      RandomBase64Generator random64,
      CheckedDatastore datastore,
      ConvMetadataStore convMetadataStore) {
    this.waveletDirectory = waveletDirectory;
    this.udwDirectory = udwDirectory;
    this.participantId = participantId;
    this.userId = userId;
    this.convStore = convStore;
    this.udwStore = udwStore;
    this.random64 = random64;
    this.datastore = datastore;
    this.convMetadataStore = convMetadataStore;
  }

  private WaveletMapping registerWavelet(SlobId objectId) throws IOException {
    WaveletMapping mapping = new WaveletMapping(objectId);
    log.info("Registering " + mapping);
    try {
      waveletDirectory.register(mapping);
    } catch (ObjectIdAlreadyKnown e) {
      throw new RuntimeException("Freshly-created object already known", e);
    }
    log.info("Registered " + mapping);
    return mapping;
  }

  private String makeObsoleteUdwMetadata(SlobId convId) {
    ObsoleteUdwMetadataGsonImpl udwMeta = new ObsoleteUdwMetadataGsonImpl();
    udwMeta.setAssociatedConvId(convId.getId());
    udwMeta.setOwner(userId.getId());
    ObsoleteWaveletMetadataGsonImpl meta = new ObsoleteWaveletMetadataGsonImpl();
    meta.setType(ObsoleteWaveletMetadata.Type.UDW);
    meta.setUdwMetadata(udwMeta);
    return GsonProto.toJson(meta);
  }

  private String makeObsoleteConvMetadata() {
    ObsoleteWaveletMetadataGsonImpl meta = new ObsoleteWaveletMetadataGsonImpl();
    meta.setType(ObsoleteWaveletMetadata.Type.CONV);
    return GsonProto.toJson(meta);
  }

  private List<ChangeData<String>> serializeChanges(List<WaveletOperation> ops) {
    ImmutableList.Builder<ChangeData<String>> out = ImmutableList.builder();
    for (String delta : SERIALIZER.serializeDeltas(ops)) {
      out.add(new ChangeData<String>(getFakeClientId(), delta));
    }
    return out.build();
  }

  private WaveletMapping createUdw(SlobId convId) throws IOException {
    long creationTime = System.currentTimeMillis();
    List<WaveletOperation> history = InitialOps.userDataWaveletOps(participantId, creationTime);
    SlobId objectId = newUdwWithGeneratedId(makeObsoleteUdwMetadata(convId),
        serializeChanges(history));
    return registerWavelet(objectId);
  }

  public SlobId getOrCreateUdw(SlobId convId) throws IOException {
    SlobId existing = udwDirectory.getUdwId(convId, userId);
    if (existing != null) {
      return existing;
    } else {
      SlobId newUdwId = createUdw(convId)
          .getObjectId();
      SlobId existingUdwId = udwDirectory.getOrAdd(convId, userId, newUdwId);
      if (existingUdwId == null) {
        return newUdwId;
      } else {
        log.log(Level.WARNING, "Multiple concurrent UDW creations for "
            + userId + " on " + convId + ": " + existingUdwId + ", " + newUdwId);
        return existingUdwId;
      }
    }
  }

  private ClientId getFakeClientId() {
    return new ClientId("");
  }

  public SlobId newConvWithGeneratedId(
      @Nullable List<WaveletOperation> historyOps,
      @Nullable final ConvMetadataGsonImpl convMetadata, final boolean inhibitPreAndPostCommit)
      throws IOException {
    final List<ChangeData<String>> history;
    if (historyOps != null) {
      history = serializeChanges(historyOps);
    } else {
      history = serializeChanges(
          InitialOps.conversationWaveletOps(participantId, System.currentTimeMillis(), random64));
    }
    SlobId newId;
    try {
      newId = new RetryHelper().run(
          new RetryHelper.Body<SlobId>() {
            @Override public SlobId run() throws RetryableFailure, PermanentFailure {
              SlobId convId = getRandomObjectId();
              CheckedTransaction tx = datastore.beginTransaction();
              try {
                convStore.newObject(tx, convId, makeObsoleteConvMetadata(), history,
                    inhibitPreAndPostCommit);
                convMetadataStore.put(tx, convId,
                    convMetadata != null ? convMetadata : new ConvMetadataGsonImpl());
                tx.commit();
              } catch (SlobAlreadyExistsException e) {
                throw new RetryableFailure("Slob id collision, retrying: " + convId, e);
              } catch (AccessDeniedException e) {
                throw new RuntimeException(
                    "Unexpected AccessDeniedException creating conv " + convId, e);
              } finally {
                tx.close();
              }
              return convId;
            }
          });
    } catch (PermanentFailure e) {
      throw new IOException("PermanentFailure creating conv", e);
    }
    registerWavelet(newId);
    return newId;
  }

  private SlobId getRandomObjectId() {
    // 96 random bits.  (6 bits per base64 character.)  TODO(ohler): Justify the
    // number 96.
    return new SlobId(random64.next(96 / 6));
  }

  private SlobId newUdwWithGeneratedId(final String metadata,
      final List<ChangeData<String>> initialHistory) throws IOException {
    try {
      return new RetryHelper().run(
          new RetryHelper.Body<SlobId>() {
            @Override public SlobId run() throws RetryableFailure, PermanentFailure {
              SlobId udwId = getRandomObjectId();
              CheckedTransaction tx = datastore.beginTransaction();
              try {
                udwStore.newObject(tx, udwId, metadata, initialHistory, false);
                tx.commit();
              } catch (SlobAlreadyExistsException e) {
                throw new RetryableFailure("Slob id collision, retrying: " + udwId, e);
              } catch (AccessDeniedException e) {
                throw new RuntimeException(
                    "Unexpected AccessDeniedException creating udw " + udwId, e);
              } finally {
                tx.close();
              }
              return udwId;
            }
          });
    } catch (PermanentFailure e) {
      throw new IOException("PermanentFailure creating udw", e);
    }
  }

}
TOP

Related Classes of com.google.walkaround.wave.server.WaveletCreator

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.