Package com.google.code.datahub

Source Code of com.google.code.datahub.Datastore

/* Copyright (c) 2012 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 com.google.code.datahub;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.EmbeddedEntity;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PropertyContainer;
import com.google.appengine.api.datastore.Query;
import static com.google.appengine.api.datastore.FetchOptions.Builder.*;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Logger;

/**
* The Datastore class provides a JSON-based interface to the GAE
* Datastore API.  Use of Entity is replaced by JSONObject and Key is
* replaced by its String representation.
*
* TODO(pmy): document Entity/JSONObject transcoding scheme.
* TODO(pmy): @see KeyFactory string methods.
* TODO(pmy): update(path, json, currentVersion)
*
* @author Pablo Mayrgundter
*/
public class Datastore extends AbstractStore {

  protected static final Logger logger = Logger.getLogger(Resource.class.getName());

  static final int DEFAULT_QUERY_LIMIT = 10;

  /** The key name for the ACL property. */
  static final String PROP_ACL_KEY = "##ACL##";

  /** The key name for the internal-only parent-key pointer. */
  static final String INTERNAL_PARENT_PROP = "##PARENT##";

  final DatastoreService service;

  public Datastore() {
    service = DatastoreServiceFactory.getDatastoreService();
    Entity root = new Entity(Path.ROOT_KEY);
    service.put(root);
  }

  @Override
  public Path create(Path parent, JSONObject json, User user) {
    logger.fine("create, parent path: " + parent);
    final Key parentKey = parent.toKey();
    logger.fine("create, parent key: " + parentKey);
    return create(new Entity(Path.PATH_KIND, parentKey), json, user);
  }

  @Override
  public Path create(Path parent, String name, JSONObject json, User user) {
    logger.fine("create, parent path: " + parent);
    final Key parentKey = parent.toKey();
    logger.fine("create, parent key: " + parentKey);
    return create(new Entity(Path.resolvePart(name, parentKey)), json, user);
  }

  @Override
  public void delete(User user, Path ... paths) {
    Key [] keys = new Key[paths.length];
    for (int i = 0; i < keys.length; i++) {
      keys[i] = paths[i].toKey();
      assertExists(paths[i]);
    }
    service.delete(keys);
  }

  @Override
  public JSONObject list(Path path,
                         int offset, int limit, String [] fields, int [] order,
                         String reqEndpointId, long duration,
                         User user) {
    Key key = path.toKey();
    // TODO(pmy): Shouldn't need to store the parent key in every
    // entity to do this, but couldn't figure out how to limit results
    // otherwise.
    Query q = new Query(Path.PATH_KIND, Path.ROOT_KEY)
      .setFilter(new Query.FilterPredicate(INTERNAL_PARENT_PROP,
                                           Query.FilterOperator.EQUAL,
                                           key));
    // TODO(pmy): just .setKeysOnly(); ?
    return entitiesToJson(service.prepare(q).asList(withLimit(10)));
  }

  @Override
  public JSONObject retrieve(Path path, User user) {
    Key key = path.toKey();
    logger.fine("service.get: " + key);
    // TODO(pmy): remove this try/catch when ACLs check is actually
    // performed, as it will throw for missing..
    try {
      return entityToJson(service.get(key));
    } catch (EntityNotFoundException e) {
      throw new NotFoundException(path);
    }
  }

  /** Named create uses this path. */
  @Override
  public void update(Path path, JSONObject json, User user) {
    service.put(jsonToEntity(path, json));
  }

  @Override
  public JSONObject search(Path path, String query, User user) {
    return search(path, query, 0, DEFAULT_LIMIT, null, null, null, DURATION_UNDEFINED, user);
  }

  @Override
  public JSONObject search(Path path,
                           String query,
                           int offset, int limit,
                           String [] fields, int [] order,
                           String endpointId, long duration,
                           User user) {
    return entitiesToJson(service.prepare(parseQuery(path, query))
                          .asQueryResultList(FetchOptions.Builder
                                             .withOffset(offset)
                                             .limit(limit)));
  }

  // Helpers.

  /**
   * Helper for the two public add methods, once the entity has been
   * constructed.
   */
  Path create(Entity entity, JSONObject json, User user) {
    // TODO(pmy): Util.jsonPut(json, PROP_ACL_KEY, new JSONObject());
    Key key = service.put(setProperties(entity, json));
    logger.fine("create helper, inner key: " + key);
    return Path.fromKey(key);
  }

  void assertExists(Path path) {
    try {
      service.get(path.toKey());
    } catch (EntityNotFoundException e) {
      throw new NotFoundException(path);
    }
  }

  // Query support.

  Query parseQuery(Path path, String queryString) {
    // TODO(pmy): quick and dirty query parser, or maybe use search.query.QueryParser?
    Query query = new Query(path.toKey());
    /*
    for (String [] filter : exprToTriples(filterExpr)) {
      q.addFilter(filter[0], strToOp(filter[1]), filter[2]);
    }
    */
    //q.addSort("created", Query.SortDirection.DESCENDING);
    return query;
  }

  String [][] filterExprToFilterTriples(String s) {
    return new String[0][0];
  }

  // TODO(pmy)
  static String [][] exprToTriples(String filterExpr) {
    return new String[0][0];
  }

  static Query.FilterOperator strToOp(String opStr) {
    Query.FilterOperator op;
    if (opStr.equals("=")) {
      op = Query.FilterOperator.EQUAL;
    } else if (opStr.equals("!=")) {
      op = Query.FilterOperator.NOT_EQUAL;
    } else if (opStr.equals(">")) {
      op = Query.FilterOperator.GREATER_THAN;
    } else if (opStr.equals(">=")) {
      op = Query.FilterOperator.GREATER_THAN_OR_EQUAL;
    } else if (opStr.equals("<")) {
      op = Query.FilterOperator.LESS_THAN;
    } else if (opStr.equals("<=")) {
      op = Query.FilterOperator.LESS_THAN_OR_EQUAL;
    } else {
      throw new UnsupportedOperationException("Unknown filter: " + opStr);
    }
    return op;
  }

  // Entity/JSON helpers.

  // TODO(pmy): consolidate jsonToEntiy mapping with Search and
  // delegate for customziation.  Maybe move this up to
  // ResourceServlet?

  /**
   * Creates a new Entity with the given path and JSONObject's
   * properties.
   */
  public static Entity jsonToEntity(Path path, JSONObject json) {
    Key k = path.toKey();
    Entity entity = new Entity(k);
    setProperties(entity, json);
    return entity;
  }

  /**
   * For each property in the given JSONObject, set the corresponding
   * property in the given Entity.
   *
   * @return the given Entity.
   */
  static Entity setProperties(final Entity entity, JSONObject json) {
    setProperties((PropertyContainer) entity, json);
    Key key = entity.getKey();
    Key parent = key.getParent();
    // TODO(pmy): parent may be null: does this matter?
    entity.setProperty(INTERNAL_PARENT_PROP, parent);
    // TODO(pmy): search
    //entity.setProperty(Search.INTERNAL_QUERY_FIELD_PATH,
    // Search.makePathTokens(Path.fromKey(key)));
    return entity;
  }

  /** Recursive callee of setProperties(Entity, JSONObject). */
  static PropertyContainer setProperties(final PropertyContainer entity, JSONObject json) {
    Util.visitJson(json, new Util.Visitor() {
        void visit(String key, Object val) {
          // Datastore will complain about storing JSONObject.NULL, so
          // explicitly convert to java NULL.  JSONObject will convert
          // back to json's null type on the way back out.
          if (val == JSONObject.NULL) {
            entity.setProperty(key, null);
          } else {
            entity.setProperty(key, val);
          }
        }
        void visit(String key, Boolean val) { entity.setProperty(key, val); }
        void visit(String key, Double val) { entity.setProperty(key, val); }
        void visit(String key, Integer val) { entity.setProperty(key, val); }
        void visit(String key, Long val) { entity.setProperty(key, val); }
        void visit(String key, String val) { entity.setProperty(key, val); }
        void visit(String key, JSONObject val) {
          EmbeddedEntity embedded = new EmbeddedEntity();
          setProperties(embedded, val);
          entity.setProperty(key, embedded);
        }
        void visit(String key, JSONArray val) {
          Collection<Object> objs = new ArrayList<Object>(val.length());
          for (int i = 0; i < val.length(); i++) {
            Object arrVal;
            try {
              arrVal = val.get(i);
            } catch(JSONException e) {
              throw new ArrayIndexOutOfBoundsException("" + e);
            }
            if (arrVal instanceof JSONObject) {
              EmbeddedEntity embedded = new EmbeddedEntity();
              setProperties(embedded, (JSONObject) arrVal);
              objs.add(embedded);
            } else {
              objs.add(arrVal);
            }
          }
          entity.setProperty(key, objs);
        }
      });
    return entity;
  }

  public static JSONObject entitiesToJson(Iterable<Entity> entities) {
    JSONObject json = new JSONObject();
    for (Entity e : entities) {
      Util.jsonPut(json, Path.fromKey(e.getKey()).getFilename(), entityToJson(e));
    }
    return json;
  }

  public static JSONObject entityToJson(PropertyContainer entity) {
    JSONObject json = new JSONObject();
    for (java.util.Map.Entry<String, Object> property : entity.getProperties().entrySet()) {
      String keyName = property.getKey();
      if (keyName.equals(INTERNAL_PARENT_PROP)
          || keyName.startsWith(Search.INTERNAL_QUERY_FIELD_PATH)) {
        continue;
      }
      Object val = property.getValue();
      if (keyName == null) {
        throw new NullPointerException("json.org's JSON doesn't allow null keys.");
      }
      if (val == null) {
        val = JSONObject.NULL;
      } else if (val instanceof EmbeddedEntity) {
        val = entityToJson((EmbeddedEntity) val);
      } else if (val instanceof Collection) {
        JSONArray arr = new JSONArray();
        int i = 0;
        for (Object o : (Collection) val) {
          if (o instanceof EmbeddedEntity) {
            o = entityToJson((EmbeddedEntity) o);
          }
          try {
            arr.put(i++, o);
          } catch (JSONException e) {
            // "if the value is a non-finite number." - json.org
            throw new RuntimeException(e);
          }
        }
        val = arr;
      } else if (val instanceof com.google.appengine.api.datastore.Text) {
        val = ((com.google.appengine.api.datastore.Text) val).getValue();
      }
      try {
        json.put(keyName, val);
      } catch (JSONException e) {
        // "if the value is a non-finite number." - json.org
        throw new RuntimeException(e);
      }
    }
    return json;
  }
}
TOP

Related Classes of com.google.code.datahub.Datastore

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.