GaePersistenceManager.java :  » Google-tech » siena » siena » gae » Java Open Source

Java Open Source » Google tech » siena 
siena » siena » gae » GaePersistenceManager.java
/*
 * Copyright 2009 Alberto Gimeno <gimenete at gmail.com>
 *
 *   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 siena.gae;

import static com.google.appengine.api.datastore.FetchOptions.Builder.withChunkSize;
import static com.google.appengine.api.datastore.FetchOptions.Builder.withLimit;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.NotImplementedException;

import siena.AbstractPersistenceManager;
import siena.ClassInfo;
import siena.Json;
import siena.Query;
import siena.QueryFilter;
import siena.QueryOrder;
import siena.SienaException;
import siena.Util;
import siena.embed.Embedded;
import siena.embed.JsonSerializer;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.appengine.api.datastore.Text;

public class GaePersistenceManager extends AbstractPersistenceManager {

  private DatastoreService ds;

  public void beginTransaction(int isolationLevel) {
  }

  public void closeConnection() {
  }

  public void commitTransaction() {
  }

  public void delete(Object obj) {
    ds.delete(getKey(obj));
  }

  public void get(Object obj) {
    Key key = getKey(obj);
    try {
      Entity entity = ds.get(key);
      fillModel(obj, entity);
    } catch(Exception e) {
      throw new SienaException(e);
    }
  }

  public void init(Properties p) {
    ds = DatastoreServiceFactory.getDatastoreService();
  }

  public void insert(Object obj) {
    Class<?> clazz = obj.getClass();
    Entity entity = new Entity(ClassInfo.getClassInfo(clazz).tableName);
    fillEntity(obj, entity);
    ds.put(entity);
    setKey(ClassInfo.getIdField(clazz), obj, entity.getKey());
  }
  
  private void setKey(Field f, Object obj, Key key) {
    try {
      Object value = key.getId();
      if(f.getType() == String.class)
        value = value.toString();
      f.setAccessible(true);
      f.set(obj, value);
    } catch(Exception e) {
      throw new SienaException(e);
    }
  }

  public void rollbackTransaction() {
  }

  public void update(Object obj) {
    try {
      //      Entity entity = new Entity(getEntityName(obj.getClass()));
      //      entity.setProperty(Entity.KEY_RESERVED_PROPERTY, getKey(obj));
      Entity entity = ds.get(getKey(obj)); // FIXME don't read again
      fillEntity(obj, entity);
      ds.put(entity);
    } catch(SienaException e) {
      throw e;
    } catch(Exception e) {
      throw new SienaException(e);
    }
  }

  public Key getKey(Object obj) {
    try {
      Field f = ClassInfo.getIdField(obj.getClass());
      Object value = f.get(obj);
      if(value instanceof String)
        value = Long.parseLong((String) value); 
      return KeyFactory.createKey(ClassInfo.getClassInfo(obj.getClass()).tableName, (Long) value);
    } catch (Exception e) {
      throw new SienaException(e);
    }
  }

  private Object readField(Object object, Field field) {
    field.setAccessible(true);
    try {
      return field.get(object);
    } catch (Exception e) {
      throw new SienaException(e);
    }
  }

  private void fillEntity(Object obj, Entity entity) {
    Class<?> clazz = obj.getClass();

    for (Field field : ClassInfo.getClassInfo(clazz).updateFields) {
      String property = ClassInfo.getColumnNames(field)[0];
      Object value = readField(obj, field);
      Class<?> fieldClass = field.getType();
      if(ClassInfo.isModel(fieldClass)) {
        if(value == null) {
          entity.setProperty(property, null);
        } else {
          Key key = getKey(value);
          entity.setProperty(property, key);
        }
      } else {
        if(value != null) {
          if(field.getType() == Json.class) {
            value = value.toString();
          } else if(value instanceof String) {
            String s = (String) value;
            if(s.length() > 500)
              value = new Text(s);
          } else if(field.getAnnotation(Embedded.class) != null) {
            value = JsonSerializer.serialize(value).toString();
            String s = (String) value;
            if(s.length() > 500)
              value = new Text(s);
          }
        }
        Unindexed ui = field.getAnnotation(Unindexed.class);
        if(ui == null) {
          entity.setProperty(property, value);
        } else {
          entity.setUnindexedProperty(property, value);
        }
      }
    }
  }

  private void fillModel(Object obj, Entity entity) {
    Class<?> clazz = obj.getClass();

    for (Field field : ClassInfo.getClassInfo(clazz).updateFields) {
      field.setAccessible(true);
      String property = ClassInfo.getColumnNames(field)[0];
      try {
        Class<?> fieldClass = field.getType();
        if(ClassInfo.isModel(fieldClass)) {
          Key key = (Key) entity.getProperty(property);
          if(key != null) {
            Object value = fieldClass.newInstance();
            Field id = ClassInfo.getIdField(fieldClass);
            setKey(id, value, key);
            field.set(obj, value);
          }
        } else {
          setFromObject(obj, field, entity.getProperty(property));
        }
      } catch (Exception e) {
        throw new SienaException(e);
      }
    }
  }
  
  private void setFromObject(Object object, Field f, Object value)
    throws IllegalArgumentException, IllegalAccessException {
    if(value instanceof Text)
      value = ((Text) value).getValue();
    Util.setFromObject(object, f, value);
  }

  protected DatastoreService getDatastoreService() {
    return ds;
  }

  protected <T> List<T> mapEntities(List<Entity> entities, Class<T> clazz) {
    Field id = ClassInfo.getIdField(clazz);
    List<T> list = new ArrayList<T>(entities.size());
    for (Entity entity : entities) {
      T obj;
      try {
        obj = clazz.newInstance();
        fillModel(obj, entity);
        list.add(obj);
        setKey(id, obj, entity.getKey());
      } catch (SienaException e) {
        throw e;
      } catch (Exception e) {
        throw new SienaException(e);
      }
    }
    return list;
  }
  
  private <T> PreparedQuery prepare(Query<T> query) {
    Class<?> clazz = query.getQueriedClass();
    com.google.appengine.api.datastore.Query q = new com.google.appengine.api.datastore.Query(
        ClassInfo.getClassInfo(clazz).tableName);
    
    List<QueryFilter> filters = query.getFilters();
    for (QueryFilter filter : filters) {
      Field f = filter.field;
      String propertyName = ClassInfo.getColumnNames(f)[0];
      Object value = filter.value;
      FilterOperator op = operators.get(filter.operator);

      if(value != null && ClassInfo.isModel(value.getClass())) {
        Key key = getKey(value);
        q.addFilter(propertyName, op, key);
      } else {
        if(ClassInfo.isId(f)) {
          if(value instanceof String) {
            value = Long.parseLong(value.toString());
          }
          Key key = KeyFactory.createKey(ClassInfo.getClassInfo(clazz).tableName, (Long) value);
          q.addFilter(Entity.KEY_RESERVED_PROPERTY, op, key);
        } else {
          q.addFilter(propertyName, op, value);
        }
      }
    }
    
    List<QueryOrder> orders = query.getOrders();
    for (QueryOrder order : orders) {
      Field f = order.field;
      if(ClassInfo.isId(f)) {
        q.addSort(Entity.KEY_RESERVED_PROPERTY);
      } else {
        q.addSort(ClassInfo.getColumnNames(f)[0], order.ascending ? SortDirection.ASCENDING : SortDirection.DESCENDING);
      }
    }
    
    return ds.prepare(q);
  }
  
  @SuppressWarnings("unchecked")
  private <T> List<T> map(Query<T> query, int offset, List<Entity> entities) {
    Class<?> clazz = query.getQueriedClass();
    List<T> result = (List<T>) mapEntities(entities, clazz);
    query.setNextOffset(offset + result.size());
    return result;
  }

  @Override
  public <T> List<T> fetch(Query<T> query) {
    return map(query, 0, prepare(query).asList(withChunkSize(FetchOptions.DEFAULT_CHUNK_SIZE)));
  }

  @Override
  public <T> List<T> fetch(Query<T> query, int limit) {
    return map(query, 0, prepare(query).asList(withLimit(limit)));
  }

  @Override
  public <T> List<T> fetch(Query<T> query, int limit, Object offset) {
    return map(query, (Integer) offset, prepare(query).asList(withLimit(limit).offset((Integer) offset)));
  }

  @Override
  public <T> int count(Query<T> query) {
    return prepare(query).countEntities();
  }

  @Override
  public <T> int delete(Query<T> query) {
    throw new NotImplementedException();
  }

  @Override
  public <T> List<T> fetchKeys(Query<T> query) {
    throw new NotImplementedException();
  }

  @Override
  public <T> List<T> fetchKeys(Query<T> query, int limit) {
    throw new NotImplementedException();
  }

  @Override
  public <T> List<T> fetchKeys(Query<T> query, int limit, Object offset) {
    throw new NotImplementedException();
  }

  private static final Map<String, FilterOperator> operators = new HashMap<String, FilterOperator>() {
    private static final long serialVersionUID = 1L;
    {
      put("=",  FilterOperator.EQUAL);
      put("<",  FilterOperator.LESS_THAN);
      put(">",  FilterOperator.GREATER_THAN);
      put("<=", FilterOperator.LESS_THAN_OR_EQUAL);
      put(">=", FilterOperator.GREATER_THAN_OR_EQUAL);
      put(" IN", FilterOperator.IN);
    }
  };
  
  private static String[] supportedOperators;
  
  static {
    supportedOperators = operators.keySet().toArray(new String[0]);
  }

  @Override
  public String[] supportedOperators() {
    return supportedOperators;
  }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.