Package siena.gae

Source Code of siena.gae.GaePersistenceManagerAsync

/*
* @author mandubian <pascal.voitot@mandubian.org>
*/
package siena.gae;

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 java.util.concurrent.Future;

import org.apache.commons.lang.NotImplementedException;

import siena.ClassInfo;
import siena.PersistenceManager;
import siena.SienaException;
import siena.Util;
import siena.core.async.AbstractPersistenceManagerAsync;
import siena.core.async.QueryAsync;
import siena.core.async.SienaFuture;
import siena.core.async.SienaFutureContainer;
import siena.core.async.SienaFutureMock;
import siena.core.async.SienaFutureWrapper;
import siena.core.options.QueryOptionFetchType;
import siena.core.options.QueryOptionOffset;
import siena.core.options.QueryOptionPage;
import siena.core.options.QueryOptionState;

import com.google.appengine.api.datastore.AsyncDatastoreService;
import com.google.appengine.api.datastore.Cursor;
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.PreparedQuery;
import com.google.appengine.api.datastore.QueryResultIterable;
import com.google.appengine.api.datastore.QueryResultList;
import com.google.appengine.api.datastore.Transaction;

public class GaePersistenceManagerAsync extends AbstractPersistenceManagerAsync {

  protected AsyncDatastoreService ds;
  protected PersistenceManager syncPm;
  /*
   * properties are not used but keeps it in case of...
   */
  protected Properties props;
 
  public static final String DB = "GAE_ASYNC";

  public void init(Properties p) {
    ds = DatastoreServiceFactory.getAsyncDatastoreService();
    props = p;
  }
 
  public void init(Properties p, PersistenceManager syncPm) {
    this.syncPm = syncPm;
    ds = DatastoreServiceFactory.getAsyncDatastoreService();
    props = p;
  }
 
  public PersistenceManager sync() {
    if(syncPm==null){
      syncPm = new GaePersistenceManager();
      syncPm.init(props);
    }
    return syncPm;
  }

  public SienaFuture<Void> insert(final Object obj) {
    final Class<?> clazz = obj.getClass();
    final ClassInfo info = ClassInfo.getClassInfo(clazz);
    final Field idField = info.getIdField();
   
    Entity entity = GaeMappingUtils.createEntityInstance(idField, info, obj);
    GaeMappingUtils.fillEntity(obj, entity);
    Future<Key> future = ds.put(entity);
   
    Future<Void> wrapped = new SienaFutureWrapper<Key, Void>(future) {
             @Override
             protected Void wrap(Key generatedKey) throws Exception
             {
               GaeMappingUtils.setIdFromKey(idField, obj, generatedKey)
               return null;
             }
    };
   
    return new SienaFutureContainer<Void>(wrapped);
  }

  public SienaFuture<Void> delete(Object obj) {
    return new SienaFutureContainer<Void>(ds.delete(GaeMappingUtils.getKey(obj)));
  }
 
  public SienaFuture<Void> get(final Object obj) {
    final Key key = GaeMappingUtils.getKey(obj);
    Future<Entity> future = ds.get(key);
   
    Future<Void> wrapped = new SienaFutureWrapper<Entity, Void>(future) {
            @Override
            protected Void wrap(Entity entity) throws Exception
            {
              GaeMappingUtils.fillModel(obj, entity);
              return null;
            }
    };
   
    return new SienaFutureContainer<Void>(wrapped);
  }
 
  public SienaFuture<Void> beginTransaction(int isolationLevel) {
    Future<Transaction> future = ds.beginTransaction();
   
    Future<Void> wrapped = new SienaFutureWrapper<Transaction, Void>(future) {
            @Override
            protected Void wrap(Transaction transaction) throws Exception
            {
              return null;
            }
    };
   
    return new SienaFutureContainer<Void>(wrapped);
  }
 
  public SienaFuture<Void> beginTransaction() {
    Future<Transaction> future = ds.beginTransaction();
   
    Future<Void> wrapped = new SienaFutureWrapper<Transaction, Void>(future) {
            @Override
            protected Void wrap(Transaction transaction) throws Exception
            {
              return null;
            }
    };
   
    return new SienaFutureContainer<Void>(wrapped);
  }

  public SienaFuture<Void> closeConnection() {
    // does nothing
    return null;
  }

  public SienaFuture<Void> commitTransaction() {
    Transaction txn = ds.getCurrentTransaction();
    return new SienaFutureContainer<Void>(txn.commitAsync());
  }

  public SienaFuture<Void> rollbackTransaction() {
    Transaction txn = ds.getCurrentTransaction();
    return new SienaFutureContainer<Void>(txn.rollbackAsync());
  }

  public SienaFuture<Void> update(Object obj) {
    Entity entity = new Entity(GaeMappingUtils.getKey(obj));
    GaeMappingUtils.fillEntity(obj, entity);
    Future<Key> future = ds.put(entity);
   
    Future<Void> wrapped = new SienaFutureWrapper<Key, Void>(future) {
            @Override
            protected Void wrap(Key key) throws Exception
            {
              return null;
            }
    };
   
    return new SienaFutureContainer<Void>(wrapped);
  }


  protected AsyncDatastoreService getDatastoreService() {
    return ds;
  }

 

  private <T> PreparedQuery prepare(QueryAsync<T> query) {
    Class<?> clazz = query.getQueriedClass();
    com.google.appengine.api.datastore.Query q = new com.google.appengine.api.datastore.Query(
        ClassInfo.getClassInfo(clazz).tableName);

    return ds.prepare(GaeQueryUtils.addFiltersOrders(query, q));
  }

  private <T> PreparedQuery prepareKeysOnly(QueryAsync<T> query) {
    Class<?> clazz = query.getQueriedClass();
    com.google.appengine.api.datastore.Query q = new com.google.appengine.api.datastore.Query(
        ClassInfo.getClassInfo(clazz).tableName);

    return ds.prepare(GaeQueryUtils.addFiltersOrders(query, q).setKeysOnly());
  }

  protected <T> T mapJoins(QueryAsync<T> query, T model) {
    try {
      // join queries
      Map<Field, ArrayList<Key>> fieldMap = GaeQueryUtils.buildJoinFieldKeysMap(query);
     
      // creates the list of joined entity keys to extract
      for(Field field: fieldMap.keySet()){
        Key key = GaeMappingUtils.getKey(field.get(model));
        List<Key> keys = fieldMap.get(field);
        if(!keys.contains(key))
          keys.add(key);
      }
     
      Map<Field, Map<Key, Entity>> entityMap =
        new HashMap<Field, Map<Key, Entity>>();

      try {
        // retrieves all joined entities per field
        for(Field field: fieldMap.keySet()){
          Future<Map<Key, Entity>> entities = ds.get(fieldMap.get(field));
          // gets the future here because we need it!
          entityMap.put(field, entities.get());
        }
      }catch(Exception ex){
        throw new SienaException(ex);
      }
      // associates linked models to their models
      // linkedModels is just a map to contain entities already mapped
      Map<Key, Object> linkedModels = new HashMap<Key, Object>();
      Object linkedObj;
      Entity entity;
     
      for(Field field: fieldMap.keySet()){
        Object objVal = field.get(model);
        Key key = GaeMappingUtils.getKey(objVal);
        linkedObj = linkedModels.get(key);
        if(linkedObj==null){
          entity = entityMap.get(field).get(key);
          linkedObj = objVal;
          GaeMappingUtils.fillModel(linkedObj, entity);
          linkedModels.put(key, linkedObj);
        }
     
        field.set(model, linkedObj);       
      }

      return model;
    } catch(IllegalAccessException ex){
      throw new SienaException(ex);
    }   
  }
 
  protected <T> List<T> mapJoins(QueryAsync<T> query, List<T> models) {
    try {
      // join queries
      Map<Field, ArrayList<Key>> fieldMap = GaeQueryUtils.buildJoinFieldKeysMap(query);
     
      // creates the list of joined entity keys to extract
      for (final T model : models) {
        for(Field field: fieldMap.keySet()){
                    Object objVal = Util.readField(model, field);
                    // our object is not linked to another object...so it doesn't have any key
                    if(objVal == null) {
                        continue;
                    }

                    Key key = GaeMappingUtils.getKey(objVal);
          List<Key> keys = fieldMap.get(field);
          if(!keys.contains(key))
            keys.add(key);
        }
      }
     
      Map<Field, Map<Key, Entity>> entityMap =
        new HashMap<Field, Map<Key, Entity>>();

      try {
        // retrieves all joined entities per field
        for(Field field: fieldMap.keySet()){
          Future<Map<Key, Entity>> entities = ds.get(fieldMap.get(field));
          // gets the future here because we need it so we wait for it
          entityMap.put(field, entities.get());
        }
      }catch(Exception ex){
        throw new SienaException(ex);
      }
      // associates linked models to their models
      // linkedModels is just a map to contain entities already mapped
      Map<Key, Object> linkedModels = new HashMap<Key, Object>();
      Object linkedObj;
      Entity entity;
     
      for (final T model : models) {
        for(Field field: fieldMap.keySet()){
          Object objVal = Util.readField(model, field);
                    // our object is not linked to another object...so it doesn't have any key
                    if(objVal == null) {
                        continue;
                    }
          Key key = GaeMappingUtils.getKey(objVal);
          linkedObj = linkedModels.get(key);
          if(linkedObj==null){
            entity = entityMap.get(field).get(key);
            linkedObj = objVal;
            GaeMappingUtils.fillModel(linkedObj, entity);
            linkedModels.put(key, linkedObj);
          }
       
          field.set(model, linkedObj);       
        }
      }
      return models;
    } catch(IllegalAccessException ex){
      throw new SienaException(ex);
    }   
  }
 
  protected <T> T map(QueryAsync<T> query, Entity entity) {
    Class<?> clazz = query.getQueriedClass();
    @SuppressWarnings("unchecked")
    T result = (T)GaeMappingUtils.mapEntity(entity, clazz);
   
    // join management
    if(!query.getJoins().isEmpty() || ClassInfo.getClassInfo(clazz).joinFields.size() != 0)
      return mapJoins(query, result);
   
    return result;
  }
 
  protected <T> List<T> map(QueryAsync<T> query, Iterable<Entity> entities) {
    Class<?> clazz = query.getQueriedClass();
    @SuppressWarnings("unchecked")
    List<T> result = (List<T>) GaeMappingUtils.mapEntities(entities, clazz);
   
    // join management
    if(!query.getJoins().isEmpty() || ClassInfo.getClassInfo(clazz).joinFields.size() != 0)
      return mapJoins(query, result);
   
    return result;
  }
 
  protected <T> List<T> map(QueryAsync<T> query, QueryResultList<Entity> entities) {
    Class<?> clazz = query.getQueriedClass();
    @SuppressWarnings("unchecked")
    List<T> result = (List<T>) GaeMappingUtils.mapEntities(entities, clazz);
   
    // join management
    if(!query.getJoins().isEmpty() || ClassInfo.getClassInfo(clazz).joinFields.size() != 0)
      return mapJoins(query, result);
   
    return result;
  }

 
  private <T> SienaFuture<List<T>> doFetchList(QueryAsync<T> query, int limit, int offset) {
    QueryOptionGaeContext gaeCtx = (QueryOptionGaeContext)query.option(QueryOptionGaeContext.ID);
    if(gaeCtx==null){
      gaeCtx = new QueryOptionGaeContext();
      query.customize(gaeCtx);
    }
   
    QueryOptionState state = (QueryOptionState)query.option(QueryOptionState.ID);
    QueryOptionFetchType fetchType = (QueryOptionFetchType)query.option(QueryOptionFetchType.ID);
    FetchOptions fetchOptions = FetchOptions.Builder.withDefaults();

    QueryOptionPage pag = (QueryOptionPage)query.option(QueryOptionPage.ID);
    if(!pag.isPaginating()){
      // no pagination but pageOption active
      if(pag.isActive()){
        // if local limit is set, it overrides the pageOption.pageSize
        if(limit!=Integer.MAX_VALUE){
          gaeCtx.realPageSize = limit;
          fetchOptions.limit(gaeCtx.realPageSize);
          // pageOption is passivated to be sure it is not reused
          pag.passivate();
        }
        // using pageOption.pageSize
        else {
          gaeCtx.realPageSize = pag.pageSize;
          fetchOptions.limit(gaeCtx.realPageSize);
          // passivates the pageOption in stateless mode not to keep anything between 2 requests
          if(state.isStateless()){
            pag.passivate();
          }           
        }
      }
      else {
        if(limit != Integer.MAX_VALUE){
          gaeCtx.realPageSize = limit;
          fetchOptions.limit(gaeCtx.realPageSize);
        }
      }
    }else {
      // paginating so use the pagesize and don't passivate pageOption
      // local limit is not taken into account
      gaeCtx.realPageSize = pag.pageSize;
      fetchOptions.limit(gaeCtx.realPageSize);
    }

    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);
    // if local offset has been set, uses it
    if(offset!=0){
      off.activate();
      off.offset = offset;
    }
           
    // if previousPage has detected there is no more data, simply returns an empty list
    if(gaeCtx.noMoreDataBefore){
      return new SienaFutureMock<List<T>>(new ArrayList<T>());
    }
   
    if(state.isStateless()) {
      if(pag.isPaginating()){
        if(off.isActive()){
          gaeCtx.realOffset+=off.offset;
          fetchOptions.offset(gaeCtx.realOffset);
          off.passivate();
        }else {
          fetchOptions.offset(gaeCtx.realOffset);
        }
      }else {
        // if stateless and not paginating, resets the realoffset to 0
        gaeCtx.realOffset = 0;
        if(off.isActive()){
          gaeCtx.realOffset=off.offset;
          fetchOptions.offset(gaeCtx.realOffset);
          off.passivate();
        }
      }
     
      switch(fetchType.fetchType){
      case KEYS_ONLY:
        {
          // uses iterable as it is the only async request for prepared query for the time being
          Iterable<Entity> entities = prepareKeysOnly(query).asIterable(fetchOptions);
          return new GaeSienaFutureListMapper<T>(this, entities, query, GaeSienaFutureListMapper.MapType.KEYS_ONLY);
        }
      case NORMAL:
      default:
        {
          // uses iterable as it is the only async request for prepared query for the time being
          Iterable<Entity> entities = prepare(query).asIterable(fetchOptions);
          return new GaeSienaFutureListMapper<T>(this, entities, query);
        }
      }
     
    }else {
      if(off.isActive()){
        // by default, we add the offset but it can be added with the realoffset
        // in case of cursor desactivated
        fetchOptions.offset(off.offset);
        gaeCtx.realOffset+=off.offset;
        off.passivate();
      }
     
      // manages cursor limitations for IN and != operators by using offset 
      if(!gaeCtx.isActive()){
        // cursor not yet created
        switch(fetchType.fetchType){
        case KEYS_ONLY:
          {
            PreparedQuery pq = prepareKeysOnly(query);
            if(!gaeCtx.useCursor){
              // then uses offset (in case of IN or != operators)
              //if(offset.isActive()){
              //  fetchOptions.offset(offset.offset);
              //}
              fetchOptions.offset(gaeCtx.realOffset);             
            }
           
            // we can't use real asynchronous function with cursors
            // so the page is extracted at once and wrapped into a SienaFuture
            QueryResultList<Entity> entities = pq.asQueryResultList(fetchOptions);

            // activates the GaeCtx now that it is initialised
            gaeCtx.activate();
            // sets the current cursor (in stateful mode, cursor is always kept for further use)
            if(pag.isPaginating()){
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addCursor(cursor.toWebSafeString());
              }
            }else{
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addAndMoveCursor(entities.getCursor().toWebSafeString());
              }
              // keeps track of the offset anyway if not paginating
              gaeCtx.realOffset+=entities.size();
            }                     
           
            return new GaeSienaFutureListMapper<T>(
                this, entities, query, GaeSienaFutureListMapper.MapType.KEYS_ONLY);
          }
        case NORMAL:
        default:
          {
            PreparedQuery pq = prepare(query);
            if(!gaeCtx.useCursor){
              // then uses offset (in case of IN or != operators)
              //if(offset.isActive()){
              //  fetchOptions.offset(offset.offset);
              //}
              fetchOptions.offset(gaeCtx.realOffset);             
            }
            // we can't use real asynchronous function with cursors
            // so the page is extracted at once and wrapped into a SienaFuture
            QueryResultList<Entity> entities = pq.asQueryResultList(fetchOptions);
           
            // activates the GaeCtx now that it is initialised
            gaeCtx.activate();
            // sets the current cursor (in stateful mode, cursor is always kept for further use)
            //if(gaeCtx.useCursor){
            if(pag.isPaginating()){
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addCursor(cursor.toWebSafeString());
              }
            }else{
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addAndMoveCursor(entities.getCursor().toWebSafeString());
              }
              // keeps track of the offset anyway if not paginating
              gaeCtx.realOffset+=entities.size();
            }
            //}
           
            return new GaeSienaFutureListMapper<T>(this, entities, query);
          }
        }
       
      }else {
        switch(fetchType.fetchType){
        case KEYS_ONLY:
          {
            // we prepare the query each time
            PreparedQuery pq = prepareKeysOnly(query);
            QueryResultList<Entity> entities;
            if(!gaeCtx.useCursor){
              // then uses offset (in case of IN or != operators)
              //if(offset.isActive()){
              //  fetchOptions.offset(offset.offset);
              //}
              fetchOptions.offset(gaeCtx.realOffset);
              // we can't use real asynchronous function with cursors
              // so the page is extracted at once and wrapped into a SienaFuture
              entities = pq.asQueryResultList(fetchOptions);
            }else {
              // we can't use real asynchronous function with cursors
              // so the page is extracted at once and wrapped into a SienaFuture
              String cursor = gaeCtx.currentCursor();
              if(cursor!=null){
                entities = pq.asQueryResultList(
                  fetchOptions.startCursor(Cursor.fromWebSafeString(cursor)));
              }
              else {
                entities = pq.asQueryResultList(fetchOptions);
              }
            }
           
            // sets the current cursor (in stateful mode, cursor is always kept for further use)
            //if(gaeCtx.useCursor){
            if(pag.isPaginating()){
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addCursor(cursor.toWebSafeString());
              }
            }else{
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addAndMoveCursor(entities.getCursor().toWebSafeString());
              }
              // keeps track of the offset anyway if not paginating
              gaeCtx.realOffset+=entities.size();
            }
            //}
           
            return new GaeSienaFutureListMapper<T>(
                this, entities, query, GaeSienaFutureListMapper.MapType.KEYS_ONLY);
          }
        case NORMAL:
        default:
          {
            PreparedQuery pq = prepare(query);
            QueryResultList<Entity> entities;
            if(!gaeCtx.useCursor){
              // then uses offset (in case of IN or != operators)
              //if(offset.isActive()){
              //  fetchOptions.offset(offset.offset);
              //}
              fetchOptions.offset(gaeCtx.realOffset);
              // we can't use real asynchronous function with cursors
              // so the page is extracted at once and wrapped into a SienaFuture
              entities = pq.asQueryResultList(fetchOptions);
            }else {
              // we can't use real asynchronous function with cursors
              // so the page is extracted at once and wrapped into a SienaFuture
              String cursor = gaeCtx.currentCursor();
              if(cursor!=null){
                entities = pq.asQueryResultList(
                  fetchOptions.startCursor(Cursor.fromWebSafeString(gaeCtx.currentCursor())));
              }else {
                entities = pq.asQueryResultList(fetchOptions);
              }
            }
           
            // sets the current cursor (in stateful mode, cursor is always kept for further use)
            //if(gaeCtx.useCursor){
            if(pag.isPaginating()){
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addCursor(cursor.toWebSafeString());
              }
            }else{
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addAndMoveCursor(entities.getCursor().toWebSafeString());
              }
              // keeps track of the offset anyway
              gaeCtx.realOffset+=entities.size();
            }
            //}
           
            return new GaeSienaFutureListMapper<T>(this, entities, query);
          }
        }
      }

    }
  }
 
 
  private <T> SienaFuture<Iterable<T>> doFetchIterable(QueryAsync<T> query, int limit, int offset)
  {
    QueryOptionGaeContext gaeCtx = (QueryOptionGaeContext)query.option(QueryOptionGaeContext.ID);
    QueryOptionState state = (QueryOptionState)query.option(QueryOptionState.ID);
    QueryOptionFetchType fetchType = (QueryOptionFetchType)query.option(QueryOptionFetchType.ID);
       
    if(gaeCtx==null){
      gaeCtx = new QueryOptionGaeContext();
      query.customize(gaeCtx);
    }

    FetchOptions fetchOptions = FetchOptions.Builder.withDefaults();

    QueryOptionPage pag = (QueryOptionPage)query.option(QueryOptionPage.ID);
    if(!pag.isPaginating()){
      // no pagination but pageOption active
      if(pag.isActive()){
        // if local limit is set, it overrides the pageOption.pageSize
        if(limit!=Integer.MAX_VALUE){
          gaeCtx.realPageSize = limit;
          fetchOptions.limit(gaeCtx.realPageSize);
          // pageOption is passivated to be sure it is not reused
          pag.passivate();
        }
        // using pageOption.pageSize
        else {
          gaeCtx.realPageSize = pag.pageSize;
          fetchOptions.limit(gaeCtx.realPageSize);
          // passivates the pageOption in stateless mode not to keep anything between 2 requests
          if(state.isStateless()){
            pag.passivate();
          }           
        }
      }
      else {
        if(limit != Integer.MAX_VALUE){
          gaeCtx.realPageSize = limit;
          fetchOptions.limit(gaeCtx.realPageSize);
        }
      }
    }else {
      // paginating so use the pagesize and don't passivate pageOption
      // local limit is not taken into account
      gaeCtx.realPageSize = pag.pageSize;
      fetchOptions.limit(gaeCtx.realPageSize);
    }

    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);
    // if local offset has been set, uses it
    if(offset!=0){
      off.activate();
      off.offset = offset;
    }
   
    // if previousPage has detected there is no more data, simply returns an empty list
    if(gaeCtx.noMoreDataBefore){
      return new SienaFutureMock<Iterable<T>>(new ArrayList<T>());
    }
   
    if(state.isStateless()) {
      if(pag.isPaginating()){     
        if(off.isActive()){
          gaeCtx.realOffset+=off.offset;
          fetchOptions.offset(gaeCtx.realOffset);
          off.passivate();
        }else {
          fetchOptions.offset(gaeCtx.realOffset);
        }
      }else {
               
        // if stateless and not paginating, resets the realoffset to 0
        gaeCtx.realOffset = off.offset;
        if(off.isActive()){
          fetchOptions.offset(gaeCtx.realOffset);
          off.passivate();
        }
      }
     
      switch(fetchType.fetchType){
      case ITER:
      default:
        {
          // uses iterable as it is the only async request for prepared query for the time being
          Iterable<Entity> entities = prepare(query).asIterable(fetchOptions);
          return new GaeSienaFutureIterableMapper<T>(this, entities, query);
        }
      }
     
    }else {
      if(off.isActive()){
        // by default, we add the offset but it can be added with the realoffset
        // in case of cursor desactivated
        fetchOptions.offset(off.offset);
        gaeCtx.realOffset+=off.offset;
        off.passivate();
      }
      // manages cursor limitations for IN and != operators   
      if(!gaeCtx.isActive()){
        // cursor not yet created
        switch(fetchType.fetchType){
        case ITER:
        default:
          {
            PreparedQuery pq = prepare(query);
           
            if(pag.isPaginating()){
              // in case of pagination, we need to allow asynchronous calls such as:
              // QueryAsync<MyClass> query = pm.createQuery(MyClass).paginate(5).stateful().order("name");
              // SienaFuture<Iterable<MyClass>> future1 = query.iter();
              // SienaFuture<Iterable<MyClass>> future2 = query.nextPage().iter();
              // Iterable<MyClass> it = future1.get().iterator();
              // while(it.hasNext()) { // do it }
              // it = future2.get().iterator();
              // while(it.hasNext()) { // do it }
             
              // so we can't use the asQueryResultIterable as the cursor is not moved to the end of the current page
              // but moved at each call of iterable.iterator().next()
              // thus we use the List in this case to be able to move directly to the next page with cursors
              QueryResultList<Entity> entities = pq.asQueryResultList(fetchOptions);

              // activates the GaeCtx now that it is initialised
              gaeCtx.activate();
              // sets the current cursor (in stateful mode, cursor is always kept for further use)
              //if(gaeCtx.useCursor){
              Cursor cursor = entities.getCursor();
              if(cursor!=null){
                gaeCtx.addCursor(cursor.toWebSafeString());
              }
              //}
             
              return new GaeSienaFutureIterableMapper<T>(this, entities, query);
            }else {
              // if not paginating, we simply use the queryresultiterable and moves the current cursor
              // while iterating
              QueryResultIterable<Entity> entities = pq.asQueryResultIterable(fetchOptions);
              // activates the GaeCtx now that it is initialised
              gaeCtx.activate();
              return new GaeSienaFutureIterableMapperWithCursor<T>(this, entities, query);
            }
           
          }
        }
       
      }else {
        switch(fetchType.fetchType){
        case ITER:
        default:
          {
            PreparedQuery pq = prepare(query);
            if(pag.isPaginating()){
              // in case of pagination, we need to allow asynchronous calls such as:
              // QueryAsync<MyClass> query = pm.createQuery(MyClass).paginate(5).stateful().order("name");
              // SienaFuture<Iterable<MyClass>> future1 = query.iter();
              // SienaFuture<Iterable<MyClass>> future2 = query.nextPage().iter();
              // Iterable<MyClass> it = future1.get().iterator();
              // while(it.hasNext()) { // do it }
              // it = future2.get().iterator();
              // while(it.hasNext()) { // do it }
             
              // so we can't use the asQueryResultIterable as the cursor is not moved to the end of the current page
              // but moved at each call of iterable.iterator().next()
              // thus we use the List in this case to be able to move directly to the next page with cursors
              QueryResultList<Entity> entities;
              if(!gaeCtx.useCursor){
                // then uses offset (in case of IN or != operators)
                //if(offset.isActive()){
                //  fetchOptions.offset(offset.offset);
                //}
                fetchOptions.offset(gaeCtx.realOffset);
                entities = pq.asQueryResultList(fetchOptions);
              }else {
                String cursor = gaeCtx.currentCursor();
                if(cursor!=null){
                  entities = pq.asQueryResultList(
                    fetchOptions.startCursor(Cursor.fromWebSafeString(cursor)));
                }else {
                  entities = pq.asQueryResultList(fetchOptions);
                }
               
                // sets the current cursor (in stateful mode, cursor is always kept for further use)
                //if(gaeCtx.useCursor){
                gaeCtx.addCursor(entities.getCursor().toWebSafeString());
                //}
              }
              return new GaeSienaFutureIterableMapper<T>(this, entities, query);
            }else {
              // if not paginating, we simply use the queryresultiterable and moves the current cursor
              // while iterating
              QueryResultIterable<Entity> entities;
              if(!gaeCtx.useCursor){
                // then uses offset (in case of IN or != operators)
                //if(offset.isActive()){
                //  fetchOptions.offset(offset.offset);
                //}
                fetchOptions.offset(gaeCtx.realOffset);               
                entities = pq.asQueryResultIterable(fetchOptions);
              }else {
                String cursor = gaeCtx.currentCursor();
                if(cursor!=null){
                  entities = pq.asQueryResultIterable(
                    fetchOptions.startCursor(Cursor.fromWebSafeString(gaeCtx.currentCursor())));
                }else {
                  entities = pq.asQueryResultIterable(fetchOptions)
                }
              }
              return new GaeSienaFutureIterableMapperWithCursor<T>(this, entities, query);
            }
          }
        }
      }

    }
  }
 
  public <T> SienaFuture<Integer> count(QueryAsync<T> query) {
     int nb = prepare(query)
        .countEntities(FetchOptions.Builder.withDefaults());
    
     return new SienaFutureMock<Integer>(nb);
  }

  public <T> SienaFuture<Integer> delete(QueryAsync<T> query) {
    final ArrayList<Key> keys = new ArrayList<Key>();

    for (final Entity entity : prepareKeysOnly(query).asIterable(
        FetchOptions.Builder.withDefaults())) {
      keys.add(entity.getKey());
    }

    Future<Void> future = ds.delete(keys);
    Future<Integer> wrapped = new SienaFutureWrapper<Void, Integer>(future) {
            @Override
            protected Integer wrap(Void v) throws Exception
            {
              return keys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

 
  public <T> SienaFuture<List<T>> fetch(QueryAsync<T> query) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.NORMAL;
    return doFetchList(query, Integer.MAX_VALUE, 0);
  }

  public <T> SienaFuture<List<T>> fetch(QueryAsync<T> query, int limit) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.NORMAL;
    return doFetchList(query, limit, 0);
  }

  public <T> SienaFuture<List<T>> fetch(QueryAsync<T> query, int limit, Object offset) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.NORMAL;
    return doFetchList(query, limit, (Integer)offset);
  }
 
  public <T> SienaFuture<List<T>> fetchKeys(QueryAsync<T> query) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.KEYS_ONLY;

    return doFetchList(query, Integer.MAX_VALUE, 0);
  }

  public <T> SienaFuture<List<T>> fetchKeys(QueryAsync<T> query, int limit) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.KEYS_ONLY;

    return doFetchList(query, limit, 0);
  }

  public <T> SienaFuture<List<T>>  fetchKeys(QueryAsync<T> query, int limit, Object offset) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.KEYS_ONLY;

    return doFetchList(query, limit, (Integer)offset);
  }

  public <T> SienaFuture<Iterable<T>> iter(QueryAsync<T> query) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.ITER;

    return doFetchIterable(query, Integer.MAX_VALUE, 0);
  }

  public <T> SienaFuture<Iterable<T>> iter(QueryAsync<T> query, int limit) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.ITER;

    return doFetchIterable(query, limit, 0);
  }

  public <T> SienaFuture<Iterable<T>> iter(QueryAsync<T> query, int limit, Object offset) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.ITER;

    return doFetchIterable(query, limit, (Integer)offset);
  }

  public <T> SienaFuture<Iterable<T>> iterPerPage(QueryAsync<T> query,
      int pageSize) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.ITER_PER_PAGE;
    return new SienaIterableAsyncPerPageWrapper<T>(query, pageSize);
  }

  public <T> void release(QueryAsync<T> query) {
    super.release(query);
    GaeQueryUtils.release(query);
  }
 
  public <T> void paginate(QueryAsync<T> query) {
    GaeQueryUtils.paginate(query);
  }
 
  public <T> void nextPage(QueryAsync<T> query) {
    GaeQueryUtils.nextPage(query);
  }
 
  public <T> void previousPage(QueryAsync<T> query) {
    GaeQueryUtils.previousPage(query);
  }

  public SienaFuture<Integer> insert(final Object... objects) {
    List<Entity> entities = new ArrayList<Entity>(objects.length);
    for(int i=0; i<objects.length;i++){
      Class<?> clazz = objects[i].getClass();
      ClassInfo info = ClassInfo.getClassInfo(clazz);
      Field idField = info.getIdField();
      Entity entity = GaeMappingUtils.createEntityInstance(idField, info, objects[i]);
      GaeMappingUtils.fillEntity(objects[i], entity);
      entities.add(entity);
    }
       
    Future<List<Key>> future = ds.put(entities);
   
    Future<Integer> wrapped = new SienaFutureWrapper<List<Key>, Integer>(future) {
             @Override
             protected Integer wrap(List<Key> generatedKeys) throws Exception
             {
               int i=0;
               for(Object obj:objects){
                 Class<?> clazz = obj.getClass();
                 ClassInfo info = ClassInfo.getClassInfo(clazz);
                 Field idField = info.getIdField();
                 GaeMappingUtils.setIdFromKey(idField, obj, generatedKeys.get(i++));
               }
               return generatedKeys.size();
             }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

  public SienaFuture<Integer> insert(final Iterable<?> objects) {
    List<Entity> entities = new ArrayList<Entity>();
    for(Object obj:objects){
      Class<?> clazz = obj.getClass();
      ClassInfo info = ClassInfo.getClassInfo(clazz);
      Field idField = info.getIdField();
      Entity entity = GaeMappingUtils.createEntityInstance(idField, info, obj);
      GaeMappingUtils.fillEntity(obj, entity);
      entities.add(entity);
    }   
   
    Future<List<Key>> future = ds.put(entities);
   
    Future<Integer> wrapped = new SienaFutureWrapper<List<Key>, Integer>(future) {
             @Override
             protected Integer wrap(List<Key> generatedKeys) throws Exception
             {
               int i=0;
               for(Object obj:objects){
                 Class<?> clazz = obj.getClass();
                 ClassInfo info = ClassInfo.getClassInfo(clazz);
                 Field idField = info.getIdField();
                 GaeMappingUtils.setIdFromKey(idField, obj, generatedKeys.get(i++));
               }
               return generatedKeys.size();
             }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

  public SienaFuture<Integer> delete(final Object... models) {
    final List<Key> keys = new ArrayList<Key>();
    for(Object obj:models){
      keys.add(GaeMappingUtils.getKey(obj));
    }
   
    Future<Void> future = ds.delete(keys);
   
   
    Future<Integer> wrapped = new SienaFutureWrapper<Void, Integer>(future) {
            @Override
            protected Integer wrap(Void v) throws Exception
            {
              return keys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }


  public SienaFuture<Integer> delete(final Iterable<?> models) {
    final List<Key> keys = new ArrayList<Key>();
    for(Object obj:models){
      keys.add(GaeMappingUtils.getKey(obj));
    }
   
    Future<Void> future = ds.delete(keys);
   
    Future<Integer> wrapped = new SienaFutureWrapper<Void, Integer>(future) {
            @Override
            protected Integer wrap(Void v) throws Exception
            {
              return keys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }


  public <T> SienaFuture<Integer> deleteByKeys(Class<T> clazz, Object... keys) {
    final List<Key> gaeKeys = new ArrayList<Key>();
    for(Object key:keys){
      gaeKeys.add(GaeMappingUtils.makeKeyFromId(clazz, key));
    }
   
    Future<Void> future = ds.delete(gaeKeys);
   
    Future<Integer> wrapped = new SienaFutureWrapper<Void, Integer>(future) {
            @Override
            protected Integer wrap(Void v) throws Exception
            {
              return gaeKeys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

  public <T> SienaFuture<Integer> deleteByKeys(Class<T> clazz, Iterable<?> keys) {
    final List<Key> gaeKeys = new ArrayList<Key>();
    for(Object key:keys){
      gaeKeys.add(GaeMappingUtils.makeKeyFromId(clazz, key));
    }
   
    Future<Void> future = ds.delete(gaeKeys);
   
    Future<Integer> wrapped = new SienaFutureWrapper<Void, Integer>(future) {
            @Override
            protected Integer wrap(Void v) throws Exception
            {
              return gaeKeys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }
 
 
  public SienaFuture<Integer> get(final Object... objects) {
    List<Key> keys = new ArrayList<Key>();
    for(Object obj:objects){
      keys.add(GaeMappingUtils.getKey(obj));
    }
   
    Future<Map<Key, Entity>> future = ds.get(keys);
   
    Future<Integer> wrapped = new SienaFutureWrapper<Map<Key, Entity>, Integer>(future) {
            @Override
            protected Integer wrap(Map<Key, Entity> entityMap) throws Exception
            {
              for(Object obj:objects){
              GaeMappingUtils.fillModel(obj, entityMap.get(GaeMappingUtils.getKey(obj)));
            }
           
            return entityMap.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

  public <T> SienaFuture<Integer> get(final Iterable<T> objects) {
    List<Key> keys = new ArrayList<Key>();
    for(Object obj:objects){
      keys.add(GaeMappingUtils.getKey(obj));
    }
   
    Future<Map<Key, Entity>> future = ds.get(keys);
   
    Future<Integer> wrapped = new SienaFutureWrapper<Map<Key, Entity>, Integer>(future) {
            @Override
            protected Integer wrap(Map<Key, Entity> entityMap) throws Exception
            {
              for(Object obj:objects){
              GaeMappingUtils.fillModel(obj, entityMap.get(GaeMappingUtils.getKey(obj)));
            }
           
            return entityMap.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }
 
  public <T> SienaFuture<T> getByKey(final Class<T> clazz, final Object key) {
    Key gkey = GaeMappingUtils.makeKeyFromId(clazz, key);
    try {
      Future<Entity> future = ds.get(gkey);
     
      Future<T> wrapped = new SienaFutureWrapper<Entity, T>(future) {
              @Override
              protected T wrap(Entity entity) throws Exception
              {
            T obj = Util.createObjectInstance(clazz);
                GaeMappingUtils.fillModelAndKey(obj, entity);
                return obj;
              }
      };
     
      return new SienaFutureContainer<T>(wrapped);
    } catch (Exception e) {
      throw new SienaException(e);
    }
  }
 
  public <T> SienaFuture<List<T>> getByKeys(final Class<T> clazz, final Object... keys) {
    List<Key> gaeKeys = new ArrayList<Key>();
    for(Object key:keys){
      gaeKeys.add(GaeMappingUtils.makeKeyFromId(clazz, key));
    }
   
    Future<Map<Key, Entity>> future = ds.get(gaeKeys);
   
    Future<List<T>> wrapped = new SienaFutureWrapper<Map<Key, Entity>, List<T>>(future) {
            @Override
            protected List<T> wrap(Map<Key, Entity> entityMap) throws Exception
            {
              List<T> models = new ArrayList<T>(entityMap.size());
              for(Object key:keys){
              models.add(GaeMappingUtils.mapEntity(entityMap.get(GaeMappingUtils.makeKeyFromId(clazz, key)), clazz));
            }
           
            return models;
            }
    };
   
    return new SienaFutureContainer<List<T>>(wrapped);
  }

  public <T> SienaFuture<List<T>> getByKeys(final Class<T> clazz, final Iterable<?> keys) {
    List<Key> gaeKeys = new ArrayList<Key>();
    for(Object key:keys){
      gaeKeys.add(GaeMappingUtils.makeKeyFromId(clazz, key));
    }
   
    Future<Map<Key, Entity>> future = ds.get(gaeKeys);
   
    Future<List<T>> wrapped = new SienaFutureWrapper<Map<Key, Entity>, List<T>>(future) {
            @Override
            protected List<T> wrap(Map<Key, Entity> entityMap) throws Exception
            {
              List<T> models = new ArrayList<T>(entityMap.size());
              for(Object key:keys){
              models.add(GaeMappingUtils.mapEntity(entityMap.get(GaeMappingUtils.makeKeyFromId(clazz, key)), clazz));
            }
           
            return models;
            }
    };
   
    return new SienaFutureContainer<List<T>>(wrapped);
  }


  @Override
  public SienaFuture<Integer> update(Object... objects) {
    //throw new NotImplementedException("update not implemented for GAE yet");
    List<Entity> entities = new ArrayList<Entity>();
    for(Object obj:objects){
      Class<?> clazz = obj.getClass();
      ClassInfo info = ClassInfo.getClassInfo(clazz);
      Entity entity = GaeMappingUtils.createEntityInstanceForUpdate(info, obj);
      GaeMappingUtils.fillEntity(obj, entity);
      entities.add(entity);
    }
       
    Future<List<Key>> future = ds.put(entities);
   
    Future<Integer> wrapped = new SienaFutureWrapper<List<Key>, Integer>(future) {
            @Override
            protected Integer wrap(List<Key> keys) throws Exception
            {
              return keys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

  @Override
  public <T> SienaFuture<Integer> update(Iterable<T> objects) {
    //throw new NotImplementedException("update not implemented for GAE yet");
    List<Entity> entities = new ArrayList<Entity>();
    for(Object obj:objects){
      Class<?> clazz = obj.getClass();
      ClassInfo info = ClassInfo.getClassInfo(clazz);
      Entity entity = GaeMappingUtils.createEntityInstanceForUpdate(info, obj);
      GaeMappingUtils.fillEntity(obj, entity);
      entities.add(entity);
    }
       
    Future<List<Key>> future = ds.put(entities);
   
    Future<Integer> wrapped = new SienaFutureWrapper<List<Key>, Integer>(future) {
            @Override
            protected Integer wrap(List<Key> keys) throws Exception
            {
              return keys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped)}

  @Override
  public <T> SienaFuture<Integer> update(QueryAsync<T> query,
      Map<String, ?> fieldValues) {
    throw new NotImplementedException("update not implemented for GAE yet");

  }

  public SienaFuture<Void> save(final Object obj) {
    Class<?> clazz = obj.getClass();
    ClassInfo info = ClassInfo.getClassInfo(clazz);
    final Field idField = info.getIdField();
   
    final Entity entity;
    final Object idVal = Util.readField(obj, idField);
    // id with null value means insert
    if(idVal == null){
      entity = GaeMappingUtils.createEntityInstance(idField, info, obj);
    }
    // id with not null value means update
    else{
      entity = GaeMappingUtils.createEntityInstanceForUpdate(info, obj);     
    }
   
    GaeMappingUtils.fillEntity(obj, entity);
    Future<Key> future = ds.put(entity);
   
    Future<Void> wrapped = new SienaFutureWrapper<Key, Void>(future) {
            @Override
            protected Void wrap(Key generatedKey) throws Exception
            {
              if(idVal == null){
              GaeMappingUtils.setIdFromKey(idField, obj, entity.getKey());
            }
              return null;
            }
    };
   
    return new SienaFutureContainer<Void>(wrapped);
  }

  public SienaFuture<Integer> save(final Object... objects) {
    List<Entity> entities = new ArrayList<Entity>();
    for(Object obj:objects){
      Class<?> clazz = obj.getClass();
      ClassInfo info = ClassInfo.getClassInfo(clazz);
      Field idField = info.getIdField();
     
      Entity entity;
      Object idVal = Util.readField(obj, idField);
      // id with null value means insert
      if(idVal == null){
        entity = GaeMappingUtils.createEntityInstance(idField, info, obj);
      }
      // id with not null value means update
      else{
        entity = GaeMappingUtils.createEntityInstanceForUpdate(info, obj);     
      }
     
      GaeMappingUtils.fillEntity(obj, entity);
      entities.add(entity);     
    }
   
    Future<List<Key>> future = ds.put(entities);
   
    Future<Integer> wrapped = new SienaFutureWrapper<List<Key>, Integer>(future) {
            @Override
            protected Integer wrap(List<Key> keys) throws Exception
            {
              int i=0;
            for(Object obj:objects){
              Class<?> clazz = obj.getClass();
              ClassInfo info = ClassInfo.getClassInfo(clazz);
              Field idField = info.getIdField();
              Object idVal = Util.readField(obj, idField);
              if(idVal == null){
                GaeMappingUtils.setIdFromKey(idField, obj, keys.get(i++));
              }
            }
            return keys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

  public SienaFuture<Integer> save(final Iterable<?> objects) {
    List<Entity> entities = new ArrayList<Entity>();
    for(Object obj:objects){
      Class<?> clazz = obj.getClass();
      ClassInfo info = ClassInfo.getClassInfo(clazz);
      Field idField = info.getIdField();
     
      Entity entity;
      Object idVal = Util.readField(obj, idField);
      // id with null value means insert
      if(idVal == null){
        entity = GaeMappingUtils.createEntityInstance(idField, info, obj);
      }
      // id with not null value means update
      else{
        entity = GaeMappingUtils.createEntityInstanceForUpdate(info, obj);     
      }
     
      GaeMappingUtils.fillEntity(obj, entity);
      entities.add(entity);     
    }
   
    Future<List<Key>> future = ds.put(entities);
   
    Future<Integer> wrapped = new SienaFutureWrapper<List<Key>, Integer>(future) {
            @Override
            protected Integer wrap(List<Key> keys) throws Exception
            {
              int i=0;
            for(Object obj:objects){
              Class<?> clazz = obj.getClass();
              ClassInfo info = ClassInfo.getClassInfo(clazz);
              Field idField = info.getIdField();
              Object idVal = Util.readField(obj, idField);
              if(idVal == null){
                GaeMappingUtils.setIdFromKey(idField, obj, keys.get(i++));
              }
            }
            return keys.size();
            }
    };
   
    return new SienaFutureContainer<Integer>(wrapped);
  }

  private static String[] supportedOperators;

  static {
    supportedOperators = GaeQueryUtils.operators.keySet().toArray(new String[0]);
 

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


}
TOP

Related Classes of siena.gae.GaePersistenceManagerAsync

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.