Package siena.sdb

Source Code of siena.sdb.SdbPersistenceManager

/*
* 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.sdb;

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

import siena.AbstractPersistenceManager;
import siena.ClassInfo;
import siena.Query;
import siena.QueryJoin;
import siena.SienaException;
import siena.Util;
import siena.core.async.PersistenceManagerAsync;
import siena.core.options.QueryOptionFetchType;
import siena.core.options.QueryOptionOffset;
import siena.core.options.QueryOptionPage;
import siena.core.options.QueryOptionState;
import siena.gae.GaeMappingUtils;
import siena.gae.GaeQueryUtils;
import siena.gae.QueryOptionGaeContext;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.simpledb.AmazonSimpleDB;
import com.amazonaws.services.simpledb.AmazonSimpleDBClient;
import com.amazonaws.services.simpledb.model.BatchDeleteAttributesRequest;
import com.amazonaws.services.simpledb.model.BatchPutAttributesRequest;
import com.amazonaws.services.simpledb.model.CreateDomainRequest;
import com.amazonaws.services.simpledb.model.DeletableItem;
import com.amazonaws.services.simpledb.model.GetAttributesRequest;
import com.amazonaws.services.simpledb.model.GetAttributesResult;
import com.amazonaws.services.simpledb.model.ReplaceableItem;
import com.amazonaws.services.simpledb.model.SelectRequest;
import com.amazonaws.services.simpledb.model.SelectResult;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;

public class SdbPersistenceManager extends AbstractPersistenceManager {
  public static final String DB = "SDB";
  private static final String[] supportedOperators = { "<", ">", ">=", "<=", "!=", "=", "LIKE", "NOT LIKE", "IN" };

    public final static PmOptionSdbReadConsistency CONSISTENT_READ = new PmOptionSdbReadConsistency(true);
    public final static PmOptionSdbReadConsistency NOT_CONSISTENT_READ = new PmOptionSdbReadConsistency(false);
 
    public final static int MAX_ITEMS_PER_CALL = 25;
    public final static int MAX_ATTR_PER_SELECT = 20;

  private AmazonSimpleDB sdb;
  private String prefix;
  private List<String> domains;

  public void init(Properties p) {
    String awsAccessKeyId = p.getProperty("awsAccessKeyId");
    String awsSecretAccessKey = p.getProperty("awsSecretAccessKey");
    if(awsAccessKeyId == null || awsSecretAccessKey == null)
      throw new SienaException("Both awsAccessKeyId and awsSecretAccessKey properties must be set");
    prefix = p.getProperty("prefix");
    if(prefix == null) prefix = "";
    sdb = new AmazonSimpleDBClient(new BasicAWSCredentials(awsAccessKeyId, awsSecretAccessKey));
  }

  public void checkDomain(String domainName) {
    if(domains == null) {
      domains = sdb.listDomains().getDomainNames();
    }
    if(!domains.contains(domainName)) {
      sdb.createDomain(new CreateDomainRequest(domainName));
      domains.add(domainName);
    }
  }
 
  public boolean isReadConsistent() {
    PmOptionSdbReadConsistency opt = (PmOptionSdbReadConsistency)option(CONSISTENT_READ.type);
    if(opt != null) {
      return opt.isConsistentRead;
    }
    return false;
  }
 
  public void insert(Object obj) {
    Class<?> clazz = obj.getClass();
    ClassInfo info = ClassInfo.getClassInfo(clazz);
   
    String domain = SdbMappingUtils.getDomainName(clazz, prefix);
   
    try {
      checkDomain(domain);
      sdb.putAttributes(SdbMappingUtils.createPutRequest(domain, clazz, info, obj));
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }
 
  @Override
  public int insert(Object... objects) {
    return insert(Arrays.asList(objects));
  }

  @Override
  public int insert(Iterable<?> objects) {
    Map<String, List<ReplaceableItem>> doMap = new HashMap<String, List<ReplaceableItem>>();
    int nb = 0;
    for(Object obj: objects){
      Class<?> clazz = obj.getClass();
      String domain = SdbMappingUtils.getDomainName(clazz, prefix);
      List<ReplaceableItem> doList = doMap.get(domain);
      if(doList == null){
        doList = new ArrayList<ReplaceableItem>();
        doMap.put(domain, doList);
      }
      doList.add(SdbMappingUtils.createItem(obj));
     
      nb++;
    }
    try {
      for(String domain: doMap.keySet()){
        checkDomain(domain);     
        List<ReplaceableItem> doList = doMap.get(domain);
       
        int len = doList.size()> MAX_ITEMS_PER_CALL ? MAX_ITEMS_PER_CALL: doList.size();
        for(int i=0; i < doList.size(); i += len){
          int sz = i+len;
          if(sz > doList.size()){
            sz = doList.size();
          }
         
          sdb.batchPutAttributes(
              new BatchPutAttributesRequest(
                  domain,
                  doList.subList(i, sz)));     

        }
       
      }
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
    return nb;
  }
 
  public void get(Object obj) {
    Class<?> clazz = obj.getClass();
    ClassInfo info = ClassInfo.getClassInfo(obj.getClass());
   
    String domain = SdbMappingUtils.getDomainName(clazz, prefix);
    try {
      checkDomain(domain);
      GetAttributesRequest req = SdbMappingUtils.createGetRequest(domain, clazz, obj);
      // sets consistent read to true when reading one single object
      req.setConsistentRead(isReadConsistent());
      GetAttributesResult res = sdb.getAttributes(req);
      if(res.getAttributes().size() == 0){
        throw new SienaException(req.getItemName()+" not found in domain "+req.getDomainName());
      }
       
      SdbMappingUtils.fillModel(req.getItemName(), res, clazz, obj);
     
      // join management
      if(!info.joinFields.isEmpty()){
        mapJoins(obj);
      }
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }
 
  public int get(Object... models) {
    return get(Arrays.asList(models));
  }

  protected <T> int rawGet(Iterable<T> models) {
    StringBuffer domainBuf = new StringBuffer();
    SelectRequest req = SdbMappingUtils.buildBatchGetQuery(models, prefix, domainBuf);
    req.setConsistentRead(isReadConsistent());
    try
      checkDomain(domainBuf.toString());
      SelectResult res = sdb.select(req);
      int nb = SdbMappingUtils.mapSelectResult(res, models);
     
      // join management
      // gets class
      Class<?> clazz = null;
      for(T obj: models){
        if(clazz == null){
          clazz = obj.getClass();
          break;
        }
      }
      if(!ClassInfo.getClassInfo(clazz).joinFields.isEmpty()){
        mapJoins(models);
      }
     
      return nb;
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }

 
  public <T> int get(Iterable<T> models) {
    Iterator<T> it = models.iterator();
   
    int total = 0;
    while(it.hasNext()){
      List<T> subList = new ArrayList<T>();
      for(int i=0; i < MAX_ATTR_PER_SELECT && it.hasNext();i++){
        subList.add(it.next());
      }
      if(!subList.isEmpty()){
        total += rawGet(subList);
      }
    }
   
    return total;
  }

 
  public void update(Object obj) {
    Class<?> clazz = obj.getClass();
    ClassInfo info = ClassInfo.getClassInfo(clazz);
   
    String domain = SdbMappingUtils.getDomainName(clazz, prefix);
   
    try {
      checkDomain(domain);
      sdb.putAttributes(SdbMappingUtils.createPutRequest(domain, clazz, info, obj));
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }
 
  public <T> int update(Object... models) {
    return update(Arrays.asList(models));
  }

  public <T> int update(Iterable<T> models) {
    Map<String, List<ReplaceableItem>> doMap = new HashMap<String, List<ReplaceableItem>>();
    int nb = 0;
    for(Object obj: models){
      Class<?> clazz = obj.getClass();
      String domain = SdbMappingUtils.getDomainName(clazz, prefix);
      List<ReplaceableItem> doList = doMap.get(domain);
      if(doList == null){
        doList = new ArrayList<ReplaceableItem>();
        doMap.put(domain, doList);
      }
      doList.add(SdbMappingUtils.createItem(obj));
     
      nb++;
    }
    try {
      for(String domain: doMap.keySet()){
        checkDomain(domain);     
        List<ReplaceableItem> doList = doMap.get(domain);
       
        int len = doList.size()> MAX_ITEMS_PER_CALL ? MAX_ITEMS_PER_CALL: doList.size();
        for(int i=0; i < doList.size(); i += len){
          int sz = i+len;
          if(sz > doList.size()){
            sz = doList.size();
          }
          sdb.batchPutAttributes(
              new BatchPutAttributesRequest(
                  domain,
                  doList.subList(i, sz)));     
        }
      }
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
    return nb;
  }

 
  public void delete(Object obj) {
    Class<?> clazz = obj.getClass();
   
    String domain = SdbMappingUtils.getDomainName(clazz, prefix);
   
    try {
      checkDomain(domain);
      sdb.deleteAttributes(SdbMappingUtils.createDeleteRequest(domain, clazz, obj));
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }

  public int delete(Object... models) {
    return delete(Arrays.asList(models));
  }

  @Override
  public int delete(Iterable<?> models) {
    Map<String, List<DeletableItem>> doMap = new HashMap<String, List<DeletableItem>>();
    int nb = 0;
    for(Object obj: models){
      Class<?> clazz = obj.getClass();
      String domain = SdbMappingUtils.getDomainName(clazz, prefix);
      List<DeletableItem> doList = doMap.get(domain);
      if(doList == null){
        doList = new ArrayList<DeletableItem>();
        doMap.put(domain, doList);
      }
      doList.add(SdbMappingUtils.createDeletableItem(obj));
     
      nb++;
    }
    try {
      for(String domain: doMap.keySet()){
        checkDomain(domain);     
        List<DeletableItem> doList = doMap.get(domain);
        int len = doList.size() > MAX_ITEMS_PER_CALL ? MAX_ITEMS_PER_CALL: doList.size();
        for(int i=0; i < doList.size(); i += len){
          int sz = i+len;
          if(sz > doList.size()){
            sz = doList.size();
          }
          sdb.batchDeleteAttributes(
              new BatchDeleteAttributesRequest(
                  domain,
                  doList.subList(i, sz)));     

        }
      }   
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
    return nb;
  }
 
  @Override
  public void save(Object obj) {
    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){
      insert(obj);
    }
    // id with not null value means update
    else{
      update(obj);
    }
  }

  @Override
  public int save(Object... objects) {
    return save(Arrays.asList(objects));
  }

  @Override
  public int save(Iterable<?> objects) {
    List<Object> entities2Insert = new ArrayList<Object>();
    List<Object> entities2Update = new ArrayList<Object>();

    for(Object obj:objects){
      Class<?> clazz = obj.getClass();
      ClassInfo info = ClassInfo.getClassInfo(clazz);
      Field idField = info.getIdField();
     
      Object idVal = Util.readField(obj, idField);
      // id with null value means insert
      if(idVal == null){
        entities2Insert.add(obj);
      }
      // id with not null value means update
      else{
        entities2Update.add(obj);
      }
    }
    return insert(entities2Insert) + update(entities2Update);
  }
 
  public <T> T getByKey(Class<T> clazz, Object key) {
    String domain = SdbMappingUtils.getDomainName(clazz, prefix);
    try {
      checkDomain(domain);
      GetAttributesRequest req = SdbMappingUtils.createGetRequestFromKey(domain, clazz, key);
      // sets consistent read to true when reading one single object
      req.setConsistentRead(isReadConsistent());
      GetAttributesResult res = sdb.getAttributes(req);
      if(res.getAttributes().size() == 0){
        throw new SienaException(req.getItemName()+" not found in domain "+req.getDomainName());
      }
       
      T obj = Util.createObjectInstance(clazz);

      SdbMappingUtils.fillModel(req.getItemName(), res, clazz, obj);
     
      // join management
      if(!ClassInfo.getClassInfo(clazz).joinFields.isEmpty()){
        mapJoins(obj);
      }
     
      return obj;
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }

  public <T> List<T> getByKeys(Class<T> clazz, Object... keys) {
    return getByKeys(clazz, Arrays.asList(keys));
  }
 
  protected <T> List<T> rawGetByKeys(Class<T> clazz, Iterable<?> keys) {
    try
      StringBuffer domainBuf = new StringBuffer();

      SelectRequest req = SdbMappingUtils.buildBatchGetQueryByKeys(clazz, keys, prefix, domainBuf);
      checkDomain(domainBuf.toString());
      req.setConsistentRead(isReadConsistent());
      SelectResult res = sdb.select(req);
      List<T> models = new ArrayList<T>();
       
      SdbMappingUtils.mapSelectResultToListOrderedFromKeys(res, models, clazz, keys);
     
      // join management
      if(!ClassInfo.getClassInfo(clazz).joinFields.isEmpty()){
        mapJoins(models);
      }
     
      return models;
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }
 
  public <T> List<T> getByKeys(Class<T> clazz, Iterable<?> keys) {
    Iterator<?> it = keys.iterator();
   
    List<T> list = new ArrayList<T>();
    while(it.hasNext()){
      List<Object> subList = new ArrayList<Object>();
      for(int i=0; i < MAX_ATTR_PER_SELECT && it.hasNext();i++){
        subList.add(it.next());
      }
      if(!subList.isEmpty()){
        list.addAll(rawGetByKeys(clazz, subList));
      }
    }
   
    return list;
  }


  public <T> int count(Query<T> query) {
    StringBuffer domainBuf = new StringBuffer();
    SelectRequest req = SdbMappingUtils.buildCountQuery(query, prefix, domainBuf);

    try
      checkDomain(domainBuf.toString());
      req.setConsistentRead(isReadConsistent());
      SelectResult res = sdb.select(req);
      return SdbMappingUtils.mapSelectResultToCount(res);
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
  }

  public <T> List<T> fetch(Query<T> query) {
    List<T> models = new ArrayList<T>();
    doFetchList(query, Integer.MAX_VALUE, 0, models, 0);
    return models;
  }
 
  public <T> List<T> fetch(Query<T> query, int limit) {
    List<T> models = new ArrayList<T>();
    doFetchList(query, limit, 0, models, 0);
    return models;
  }

  public <T> List<T> fetch(Query<T> query, int limit, Object offset) {
    List<T> models = new ArrayList<T>();
    doFetchList(query, limit, (Integer)offset, models, 0);
    return models;
  }

  public <T> List<T> fetchKeys(Query<T> query) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.KEYS_ONLY;
    List<T> models = new ArrayList<T>();
    doFetchList(query, Integer.MAX_VALUE, 0, models, 0);   
    return models;
  }

  public <T> List<T> fetchKeys(Query<T> query, int limit) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.KEYS_ONLY;
    List<T> models = new ArrayList<T>();
    doFetchList(query, limit, 0, models, 0);
    return models;
  }

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

    List<T> models = new ArrayList<T>();
    doFetchList(query, limit, (Integer)offset, models, 0);
    return models;
  }
 
  public <T> Iterable<T> iter(Query<T> query) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.ITER;
    return doFetchIterable(query, Integer.MAX_VALUE, 0, false);
  }

  public <T> Iterable<T> iter(Query<T> query, int limit) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.ITER;
    return doFetchIterable(query, limit, 0, false);
  }

  public <T> Iterable<T> iter(Query<T> query, int limit, Object offset) {
    ((QueryOptionFetchType)query.option(QueryOptionFetchType.ID)).fetchType=QueryOptionFetchType.Type.ITER;
    return doFetchIterable(query, limit, (Integer)offset, false);
  }

 
  public <T> int delete(Query<T> query) {
    List<T> l = fetchKeys(query);
   
    return delete(l);
  }
 
  protected <T> void postMapping(Query<T> query){
    QueryOptionPage pag = (QueryOptionPage)query.option(QueryOptionPage.ID);
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
    QueryOptionState state = (QueryOptionState)query.option(QueryOptionState.ID);
    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);

    // desactivates paging & offset in stateless mode
    if(state.isStateless() && !pag.isPaginating()){
      pag.passivate();
      pag.pageSize = 0;
      sdbCtx.resetAll();
    }
    // offset if not kept as it is never reused as is even in stateful
    // mode. the stateful mode only keeps the realOffset/pagination alive
    off.passivate();
    off.offset = 0;
  }
 
  protected <T> void continueFetchNextToken(Query<T> query, List<T> results, int depth){
    QueryOptionPage pag = (QueryOptionPage)query.option(QueryOptionPage.ID);
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
    QueryOptionState state = (QueryOptionState)query.option(QueryOptionState.ID);
    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);
   
    // desactivates offset not to use if fetching more items from next token
    if(state.isStateless()){
      off.passivate();
    }
   
    if(!pag.isActive()){
      if(state.isStateless()){
        // retrieves next token
        if(sdbCtx.nextToken()!=null){
          doFetchList(query, Integer.MAX_VALUE, 0, results, depth+1);
        }
      }else {
        if(sdbCtx.currentToken()!=null){
          // desactivates offset because we don't to go on using offset while going to next tokens
          boolean b = off.isActive();
          off.passivate();
          doFetchList(query, Integer.MAX_VALUE, 0, results, depth+1);
          // reactivate it if it was activated
          if(b) off.activate();
        }
      }
    }
  }
 
  protected <T> void postFetch(Query<T> query, SelectResult res) {
    QueryOptionPage pag = (QueryOptionPage)query.option(QueryOptionPage.ID);
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
    QueryOptionState state = (QueryOptionState)query.option(QueryOptionState.ID);
    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);
   
    if(sdbCtx.realPageSize == 0){
      sdbCtx.realPageSize = res.getItems().size();
    }
   
    String token = null;
    // sets the current cursor (in stateful mode, cursor is always kept for further use)
    if(pag.isPaginating()){
      token = res.getNextToken();
      if(token!=null){
        sdbCtx.addToken(token, sdbCtx.realOffset + sdbCtx.realPageSize + off.offset);
      }
      // if paginating and 0 results then no more data else resets noMoreDataAfter
      if(res.getItems().size()==0){
        sdbCtx.noMoreDataAfter = true;
      } else {
        sdbCtx.noMoreDataAfter = false;
      }
    }else{
      if(state.isStateless()){
        // in stateless, doesn't follow real offset & stays where it is
        sdbCtx.realOffset = off.offset;
       
        token = res.getNextToken();
        if(token!=null){
          sdbCtx.addToken(token, /*sdbCtx.realOffset +*/ sdbCtx.realPageSize);
        }
      }else {
        // follows the real offset in stateful mode
        sdbCtx.realOffset += sdbCtx.realPageSize /*+ off.offset*/;
       
        token = res.getNextToken();
        if(token!=null){
          sdbCtx.addAndMoveToken(token, sdbCtx.realOffset);
        }else {
          // forces to go to next token to invalidate current one
          // if there are no token after, currentToken will be null
          sdbCtx.nextToken();
        }
      }
     
     
    }
  }
 
  protected <T> void preFetch(Query<T> query, int limit, int offset, boolean recursing){
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
    QueryOptionState state = (QueryOptionState)query.option(QueryOptionState.ID);
    QueryOptionPage pag = (QueryOptionPage)query.option(QueryOptionPage.ID);
    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);

    if(sdbCtx==null){
      sdbCtx = new QueryOptionSdbContext();
      query.customize(sdbCtx);
    }
   
    if(!pag.isPaginating()){
      if(state.isStateless()){
        // if not empty, it means we are recursing on tokens
        sdbCtx.reset(recursing);       
      }
      // no pagination but pageOption active
      if(pag.isActive()){       
        // if local limit is set, it overrides the pageOption.pageSize
        if(limit!=Integer.MAX_VALUE){
          sdbCtx.realPageSize = limit;       
          // DONT DO THAT BECAUSE IT PREVENTS GOING TO NEXT TOKENS USING PAGE SIZE
          // pageOption is passivated to be sure it is not reused
          //pag.passivate();
        }
        // using pageOption.pageSize
        else {
          sdbCtx.realPageSize = pag.pageSize;
          // DONT DO THAT BECAUSE IT PREVENTS GOING TO NEXT TOKENS USING PAGE SIZE
          // passivates the pageOption in stateless mode not to keep anything between 2 requests
          //if(state.isStateless()){
          //  pag.passivate();
          //}           
        }
      }
      else {
        if(limit != Integer.MAX_VALUE){
          sdbCtx.realPageSize = limit;
          // activates paging (but not pagination)
          pag.activate();
        }else {
          sdbCtx.realPageSize = 0;
        }
      }
    }else {
      // paginating so use the pagesize and don't passivate pageOption
      // local limit is not taken into account
      sdbCtx.realPageSize = pag.pageSize;
    }
   
    // if local offset has been set, uses it
    if(offset!=0){
      off.activate();
      off.offset = offset;
    }
  }
 
 
  protected final int MAX_DEPTH = 25;
 
  protected <T> void doFetchList(Query<T> query, int limit, int offset, List<T> resList, int depth) {
    if(depth >= MAX_DEPTH){
      throw new SienaException("Reached maximum depth of recursion when retrieving more data ("+MAX_DEPTH+")");
    }
     
    preFetch(query, limit, offset, !resList.isEmpty());
   
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
    QueryOptionFetchType fetchType = (QueryOptionFetchType)query.option(QueryOptionFetchType.ID);
    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);
   
    // if previousPage has detected there is no more data, simply returns an empty list
    if(sdbCtx.noMoreDataBefore || sdbCtx.noMoreDataAfter){
      return;
    }
           
    // manages cursor limitations for IN and != operators with offsets
    if(!sdbCtx.isActive()){
      StringBuffer domainBuf = new StringBuffer();
      SelectRequest req = SdbMappingUtils.buildQuery(query, prefix, domainBuf);
      req.setConsistentRead(isReadConsistent());
      checkDomain(domainBuf.toString());
      SelectResult res = sdb.select(req);
     
      // activates the SdbCtx now that it is really initialised
      sdbCtx.activate();
                 
      postFetch(query, res);
     
      // cursor not yet created
      switch(fetchType.fetchType){
      case KEYS_ONLY:
        if(off.isActive()){
          SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass(), off.offset);
        }else {
          SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass());
        }       
        break;
      case NORMAL:
      default:
        if(off.isActive()){
          SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass(), off.offset);
        }else {
          SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass());
        }
        // join management
        if(!query.getJoins().isEmpty()
            || !ClassInfo.getClassInfo(query.getQueriedClass()).joinFields.isEmpty())
          mapJoins(query, resList);
      }
     
      continueFetchNextToken(query, resList, depth);
      postMapping(query);
    }
    else {
      // we prepare the query each time
      StringBuffer domainBuf = new StringBuffer();
      SelectRequest req = SdbMappingUtils.buildQuery(query, prefix, domainBuf);
      req.setConsistentRead(isReadConsistent());
      checkDomain(domainBuf.toString());
      // we can't use real asynchronous function with cursors
      // so the page is extracted at once and wrapped into a SienaFuture     
      String token = sdbCtx.currentToken();
      if(token!=null){
        req.setNextToken(token);
      }
      SelectResult res = sdb.select(req);
       
      postFetch(query, res);
     
      switch(fetchType.fetchType){
      case KEYS_ONLY:
        if(off.isActive()){
          SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass(), off.offset);
        }else {
          SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass());
        }
        break;
      case NORMAL:
      default:
        if(off.isActive()){
          SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass(), off.offset);
        } else {
          SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass());
        }
        // join management
        if(!query.getJoins().isEmpty()
            || !ClassInfo.getClassInfo(query.getQueriedClass()).joinFields.isEmpty())
          mapJoins(query, resList);
      }
     
      continueFetchNextToken(query, resList, depth);
      postMapping(query);
    }
  }
 
  protected <T> Iterable<T> doFetchIterable(Query<T> query, int limit, int offset, boolean recursing) {
    preFetch(query, limit, offset, recursing);
   
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
   
    // if previousPage has detected there is no more data, simply returns an empty list
    if(sdbCtx.noMoreDataBefore || sdbCtx.noMoreDataAfter){
      return new ArrayList<T>();
    }
           
    // manages cursor limitations for IN and != operators with offsets
    if(!sdbCtx.isActive()){
      StringBuffer domainBuf = new StringBuffer();
      SelectRequest req = SdbMappingUtils.buildQuery(query, prefix, domainBuf);
      req.setConsistentRead(isReadConsistent());
      checkDomain(domainBuf.toString());
      SelectResult res = sdb.select(req);
     
      // activates the SdbCtx now that it is initialised
      sdbCtx.activate();
     
      postFetch(query, res);   
     
      return new SdbSienaIterable<T>(this, res.getItems(), query);
    }
    else {
      // we prepare the query each time
      StringBuffer domainBuf = new StringBuffer();
      SelectRequest req = SdbMappingUtils.buildQuery(query, prefix, domainBuf);
      req.setConsistentRead(isReadConsistent());
      checkDomain(domainBuf.toString());
      // we can't use real asynchronous function with cursors
      // so the page is extracted at once and wrapped into a SienaFuture
      String token = sdbCtx.currentToken();
      if(token!=null){
        req.setNextToken(token);
      }
      SelectResult res = sdb.select(req);
                 
      postFetch(query, res);
     
      return new SdbSienaIterable<T>(this, res.getItems(), query);
    }
  }
 
  /* transactions */

  public void beginTransaction(int isolationLevel) {
  }

  public void beginTransaction() {
  }
 
  public void closeConnection() {
  }

  public void commitTransaction() {
  }

  public void rollbackTransaction() {
  }

  @Override
  public <T> void release(Query<T> query) {
    super.release(query);
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);

    if(sdbCtx != null){
      sdbCtx.resetAll();
    }
  }

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

  @Override
  public <T> int deleteByKeys(Class<T> clazz, Object... keys) {
    return deleteByKeys(clazz, Arrays.asList(keys));
  }


  @Override
  public <T> void nextPage(Query<T> query) {
    SdbMappingUtils.nextPage(query);
  }

  @Override
  public <T> void previousPage(Query<T> query) {
    SdbMappingUtils.previousPage(query);
  }

  @Override
  public <T> void paginate(Query<T> query) {
    QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
    QueryOptionState state = (QueryOptionState)query.option(QueryOptionState.ID);

    if(sdbCtx==null){
      sdbCtx = new QueryOptionSdbContext();
      query.customize(sdbCtx);
    }
   
    // in stateless, resetting pagination resets everything in the context
    if(state.isStateless()){
      sdbCtx.resetAll();     
    }
   
    QueryOptionOffset off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);
    off.passivate();
    off.offset = 0;
  }
 
  @Override
  public <T> PersistenceManagerAsync async() {
    // TODO Auto-generated method stub
    return null;
  }





 

  @Override
  public <T> int update(Query<T> query, Map<String, ?> fieldValues) {
    // TODO Auto-generated method stub
    return 0;
  }



  @Override
  public <T> int deleteByKeys(Class<T> clazz, Iterable<?> keys) {
    List<DeletableItem> doList = new ArrayList<DeletableItem>();
    int nb = 0;
    String domain = SdbMappingUtils.getDomainName(clazz, prefix);
    for(Object key: keys){
      doList.add(SdbMappingUtils.createDeletableItemFromKey(clazz, key));     
      nb++;
    }
    try {
      checkDomain(domain);     
      int len = doList.size() > MAX_ITEMS_PER_CALL ? MAX_ITEMS_PER_CALL: doList.size();
      for(int i=0; i < doList.size(); i += len){
        int sz = i+len;
        if(sz > doList.size()){
          sz = doList.size();
        }
        sdb.batchDeleteAttributes(
          new BatchDeleteAttributesRequest(
            domain,
            doList.subList(i, sz)));     
      }
    }catch(AmazonClientException ex){
      throw new SienaException(ex);
    }
    return nb;
  }



  protected <T> T mapJoins(T model) {
    // join queries
    Map<Class<?>, Map<String, Object>> classMap = new HashMap<Class<?>, Map<String, Object>>();
   
    // sorts by class and itemName
    for(Field field: ClassInfo.getClassInfo(model.getClass()).joinFields)
    {
      Map<String, Object> strMap = classMap.get(field.getType());
      if(strMap == null){
        strMap = new HashMap<String, Object>();
        classMap.put(field.getType(), strMap);
      }
     
      String itemName = SdbMappingUtils.toString(Util.readField(model, field));
      strMap.put(itemName, null);
    }
   
    for(Class<?> clazz: classMap.keySet()){
      List<?> objs = this.getByKeys(clazz, classMap.get(clazz).keySet());
      Map<String, Object> strMap = classMap.get(clazz);
      for(Object obj:objs){
        String itemName = SdbMappingUtils.getItemName(clazz, obj);
        strMap.put(itemName, obj);
      }
    }
         
    for(Field field: ClassInfo.getClassInfo(model.getClass()).joinFields){     
      String itemName = SdbMappingUtils.toString(Util.readField(model, field));
      Util.setField(model, field, classMap.get(field.getType()).get(itemName));
    }

    return model; 
  }
 
  protected <T> void mapJoins(Iterable<T> models) {
   
    // join queries
    Map<Class<?>, Map<String, Object>> classMap = new HashMap<Class<?>, Map<String, Object>>();
   
    for(T model: models){
      // sorts by class and itemName
      for(Field field: ClassInfo.getClassInfo(model.getClass()).joinFields)
      {
        Map<String, Object> strMap = classMap.get(field.getType());
        if(strMap == null){
          strMap = new HashMap<String, Object>();
          classMap.put(field.getType(), strMap);
        }
       
        String itemName = SdbMappingUtils.toString(Util.readField(model, field));
        strMap.put(itemName, null);
      }
    }
   
    for(Class<?> clazz: classMap.keySet()){
      List<?> objs = this.getByKeys(clazz, classMap.get(clazz).keySet());
      Map<String, Object> strMap = classMap.get(clazz);
      for(Object obj:objs){
        String itemName = SdbMappingUtils.getItemName(clazz, obj);
        strMap.put(itemName, obj);
      }
    }
         
    for(T model: models){
      for(Field field: ClassInfo.getClassInfo(model.getClass()).joinFields){     
        String itemName = SdbMappingUtils.toString(Util.readField(model, field));
        Util.setField(model, field, classMap.get(field.getType()).get(itemName));
      }
    }
  }
 
  protected <T> T mapJoins(Query<T> query, T model) {
    List<QueryJoin> joins = query.getJoins();
   
    // join queries
    Map<Class<?>, Map<String, Object>> classMap = new HashMap<Class<?>, Map<String, Object>>();
   
    // sorts by class and itemName
    // joins in query
    for (QueryJoin join : joins)
    {
      Field field = join.field;
      Map<String, Object> strMap = classMap.get(field.getType());
      if(strMap == null){
        strMap = new HashMap<String, Object>();
        classMap.put(field.getType(), strMap);
      }
     
      String itemName = SdbMappingUtils.toString(Util.readField(model, field));
      strMap.put(itemName, null);
    }
   
    // join annotations
    for(Field field:
      ClassInfo.getClassInfo(query.getQueriedClass()).joinFields)
    {
      Map<String, Object> strMap = classMap.get(field.getType());
      if(strMap == null){
        strMap = new HashMap<String, Object>();
        classMap.put(field.getType(), strMap);
      }
     
      String itemName = SdbMappingUtils.toString(Util.readField(model, field));
      strMap.put(itemName, null);
    }
   
    for(Class<?> clazz: classMap.keySet()){
      List<?> objs = this.getByKeys(clazz, classMap.get(clazz).keySet());
      Map<String, Object> strMap = classMap.get(clazz);
      for(Object obj:objs){
        String itemName = SdbMappingUtils.getItemName(clazz, obj);
        strMap.put(itemName, obj);
      }
    }
         
    for(Field field: ClassInfo.getClassInfo(model.getClass()).joinFields){     
      String itemName = SdbMappingUtils.toString(Util.readField(model, field));
      Util.setField(model, field, classMap.get(field.getType()).get(itemName));
    }

    return model; 
  }
 
  protected <T> List<T> mapJoins(Query<T> query, List<T> models) {
    List<QueryJoin> joins = query.getJoins();
   
    // join queries
    Map<Class<?>, Map<String, Object>> classMap = new HashMap<Class<?>, Map<String, Object>>();
   
    // sorts by class and itemName
    // joins in query
    for (final T model : models) {
      for (QueryJoin join : joins)
      {
        Field field = join.field;
        Map<String, Object> strMap = classMap.get(field.getType());
        if(strMap == null){
          strMap = new HashMap<String, Object>();
          classMap.put(field.getType(), strMap);
        }
       
        String itemName = SdbMappingUtils.toString(Util.readField(model, field));
        strMap.put(itemName, null);
      }
     
      // join annotations
      for(Field field:
        ClassInfo.getClassInfo(query.getQueriedClass()).joinFields)
      {
        Map<String, Object> strMap = classMap.get(field.getType());
        if(strMap == null){
          strMap = new HashMap<String, Object>();
          classMap.put(field.getType(), strMap);
        }
       
        String itemName = SdbMappingUtils.toString(Util.readField(model, field));
        strMap.put(itemName, null);
      }
    }
   
    for(Class<?> clazz: classMap.keySet()){
      List<?> objs = this.getByKeys(clazz, classMap.get(clazz).keySet());
      Map<String, Object> strMap = classMap.get(clazz);
      for(Object obj:objs){
        String itemName = SdbMappingUtils.getItemName(clazz, obj);
        strMap.put(itemName, obj);
      }
    }
         
    for(T model: models){
      for (QueryJoin join : joins){
        Field field = join.field;
        String itemName = SdbMappingUtils.toString(Util.readField(model, field));
        Util.setField(model, field, classMap.get(field.getType()).get(itemName));
      }
      for(Field field: ClassInfo.getClassInfo(model.getClass()).joinFields){     
        String itemName = SdbMappingUtils.toString(Util.readField(model, field));
        Util.setField(model, field, classMap.get(field.getType()).get(itemName));
      }
    }

    return models;
  }

   
}
TOP

Related Classes of siena.sdb.SdbPersistenceManager

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.