Package com.google.walkaround.util.server.appengine

Source Code of com.google.walkaround.util.server.appengine.AbstractDirectory

/*
* 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.util.server.appengine;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.common.base.Preconditions;
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.CheckedTransaction;

import javax.annotation.Nullable;

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

/**
* A directory in the datastore that stores a set of T, where each T contains an
* id of type I.  Each entity is its own entity group.
*
* @author ohler@google.com (Christian Ohler)
*
* @param <T> entry type
* @param <I> id type
*/
public abstract class AbstractDirectory<T, I> {

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

  private final CheckedDatastore datastore;
  private final String entityKind;

  public AbstractDirectory(CheckedDatastore datastore, String entityKind) {
    Preconditions.checkNotNull(datastore, "Null datastore");
    Preconditions.checkNotNull(entityKind, "Null entityKind");
    this.datastore = datastore;
    this.entityKind = entityKind;
  }

  protected abstract String serializeId(I id);
  protected abstract I getId(T e);
  protected abstract void populateEntity(T in, Entity out);
  protected abstract T parse(Entity e);

  public Key makeKey(I id) {
    return KeyFactory.createKey(entityKind, serializeId(id));
  }

  @Nullable public T get(CheckedTransaction tx, I id) throws PermanentFailure, RetryableFailure {
    Key key = makeKey(id);
    Entity result = tx.get(key);
    if (result != null) {
      log.info("Looked up " + key + ", found " + result);
      return parse(result);
    } else {
      log.info("Looked up " + key + ", not found");
      return null;
    }
  }

  @Nullable public T getWithoutTx(I id) throws IOException {
    try {
      CheckedTransaction tx = datastore.beginTransaction();
      try {
        return get(tx, id);
      } finally {
        tx.rollback();
      }
    } catch (PermanentFailure e) {
      log.log(Level.SEVERE, "Failed to look up " + id, e);
      throw new IOException(e);
    } catch (RetryableFailure e) {
      log.log(Level.SEVERE, "Failed to look up " + id, e);
      throw new IOException(e);
    }
  }

  public void put(CheckedTransaction tx, T newEntry) throws PermanentFailure, RetryableFailure {
    Preconditions.checkNotNull(tx, "Null tx");
    Preconditions.checkNotNull(newEntry, "Null newEntry");
    Key key = makeKey(getId(newEntry));
    Entity newEntity = new Entity(key);
    populateEntity(newEntry, newEntity);
    // For now, we verify that it parses with no exceptions.  We can turn
    // this off if it's too expensive.
    parse(newEntity);
    log.info("Putting " + newEntity + " in " + tx);
    tx.put(newEntity);
  }

  public void putWithoutTx(final T newEntry) throws IOException {
    Preconditions.checkNotNull(newEntry, "Null newEntry");
    try {
      new RetryHelper().run(new RetryHelper.VoidBody() {
        @Override public void run() throws RetryableFailure, PermanentFailure {
          CheckedTransaction tx = datastore.beginTransaction();
          try {
            put(tx, newEntry);
            tx.commit();
            log.info("Committed " + tx);
          } finally {
            tx.close();
          }
        }
      });
    } catch (PermanentFailure e) {
      throw new IOException(e);
    }
  }

  /**
   * Transactionally checks if an entry with the same key as newEntry exists,
   * and adds newEntry if not.  If an entry already exists, returns the existing
   * entry; otherwise (i.e., if newEntry was added), returns null.
   */
  // NOTE(danilatos): Don't be tempted to implement a version of this method that
  // accepts a Provider or something in order to only create the newEntry if one
  // does not exist in the datastore, because megastore transactions run in parallel
  // and just fail on commit (there is no locking).
  // Instead, use get() and then getOrAdd() if get returns null.

  // Is this method worthwhile, given that a caller can just use get() and put()
  // and manage their own transaction?
  @Nullable public T getOrAdd(final T newEntry) throws IOException {
    Preconditions.checkNotNull(newEntry, "Null newEntry");
    try {
      return new RetryHelper().run(new RetryHelper.Body<T>() {
        @Override public T run() throws RetryableFailure, PermanentFailure {
          CheckedTransaction tx = datastore.beginTransaction();
          try {
            Entity existing = tx.get(makeKey(getId(newEntry)));
            if (existing != null) {
              log.info("Read " + existing + " in " + tx);
              return parse(existing);
            } else {
              put(tx, newEntry);
              tx.commit();
              log.info("Committed " + tx);
              return null;
            }
          } finally {
            tx.close();
          }
        }
      });
    } catch (PermanentFailure e) {
      throw new IOException(e);
    }
  }

}
TOP

Related Classes of com.google.walkaround.util.server.appengine.AbstractDirectory

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.