Package org.springframework.data.simpledb.core

Source Code of org.springframework.data.simpledb.core.SimpleDbTemplate

package org.springframework.data.simpledb.core;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.simpledb.attributeutil.SimpleDBAttributeConverter;
import org.springframework.data.simpledb.attributeutil.SimpleDbAttributeValueSplitter;
import org.springframework.data.simpledb.core.entity.EntityWrapper;
import org.springframework.data.simpledb.core.entity.json.JsonMarshaller;
import org.springframework.data.simpledb.exception.InvalidSimpleDBQueryException;
import org.springframework.data.simpledb.parser.SimpleDBParser;
import org.springframework.data.simpledb.query.QueryUtils;
import org.springframework.data.simpledb.reflection.FieldType;
import org.springframework.data.simpledb.reflection.FieldTypeIdentifier;
import org.springframework.data.simpledb.reflection.MetadataParser;
import org.springframework.data.simpledb.reflection.ReflectionUtils;
import org.springframework.data.simpledb.repository.support.entityinformation.SimpleDbEntityInformation;
import org.springframework.data.simpledb.repository.support.entityinformation.SimpleDbEntityInformationSupport;
import org.springframework.util.Assert;

import com.amazonaws.services.simpledb.model.Attribute;
import com.amazonaws.services.simpledb.model.BatchDeleteAttributesRequest;
import com.amazonaws.services.simpledb.model.DeletableItem;
import com.amazonaws.services.simpledb.model.DeleteAttributesRequest;
import com.amazonaws.services.simpledb.model.Item;
import com.amazonaws.services.simpledb.model.PutAttributesRequest;
import com.amazonaws.services.simpledb.model.SelectRequest;
import com.amazonaws.services.simpledb.model.SelectResult;

/**
* Primary implementation of {@link SimpleDbOperations}
*/
public class SimpleDbTemplate extends AbstractSimpleDbTemplate {

    private static final int MAX_BATCH_SIZE = 25;
  private static final Logger LOGGER = LoggerFactory.getLogger(SimpleDbTemplate.class);

    public SimpleDbTemplate(SimpleDb simpleDb) {
        super(simpleDb);
    }

  @Override
    public <T> T createOrUpdateImpl(T domainItem, EntityWrapper<T, ?> entity) {
        Assert.notNull(entity.getDomain(), "Domain name should not be null");

        logOperation("Create or update", entity);

        for (final Field field : ReflectionUtils.getFirstLevelOfReferenceAttributes(domainItem.getClass())) {
            final Object referenceEntity = ReflectionUtils.callGetter(domainItem, field.getName());

      /* recursive call */
            if (referenceEntity != null) {
                createOrUpdate(referenceEntity);
            }
        }

        if (entity.getItemName() != null) {
          delete(entity.getDomain(), entity.getItemName());
        }
        entity.generateIdIfNotSet();

        Map<String, List<String>> rawAttributes = entity.toMultiValueAttributes();
        List<PutAttributesRequest> putAttributesRequests = SimpleDbRequestBuilder.createPutAttributesRequests(
                entity.getDomain(), entity.getItemName(), rawAttributes);

        for (PutAttributesRequest request : putAttributesRequests) {
            getDB().putAttributes(request);
        }
       
        return entity.getItem();
    }

    @Override
    public void deleteAttributesImpl(String domainName, String itemName) {
        LOGGER.debug("Delete Domain\"{}\" ItemName \"{}\"", domainName, itemName);

        Assert.notNull(domainName, "Domain name should not be null");
        Assert.notNull(itemName, "Item name should not be null");

        getDB().deleteAttributes(new DeleteAttributesRequest(domainName, itemName));
    }

    @Override
    public <T> void deleteImpl(T domainItem, SimpleDbEntityInformation<T, ?> entityInformation,
                               EntityWrapper<T, ?> entity) {

        for (final Field field : ReflectionUtils.getFirstLevelOfReferenceAttributes(domainItem.getClass())) {
            final Object referenceEntity = ReflectionUtils.callGetter(domainItem, field.getName());

      /* recursive call */
            if (referenceEntity != null) {
                delete(referenceEntity);
            }
        }

        delete(entity.getDomain(), entity.getItemName());
    }

    @Override
  public <T, ID> void delete(Class<T> entityClass, Iterable<? extends ID> ids) {

      if (ids.iterator().hasNext()) {
        String domainName = getDomainName(entityClass);
       
      List<DeletableItem> deleteList = new ArrayList<DeletableItem>();
      for (ID id : ids) {
        deleteList.add(new DeletableItem().withName((String) id));
      }
      // max allowed batch size is 25
      List<DeletableItem> batch = new ArrayList<DeletableItem>(MAX_BATCH_SIZE);
      for (int i = 0; i < deleteList.size(); i += MAX_BATCH_SIZE) {
        int batchIndex = ((i + MAX_BATCH_SIZE) < deleteList.size()) ? (i + MAX_BATCH_SIZE)
            : deleteList.size();
        for (int j = i; j < batchIndex; j++) {
          batch.add(deleteList.get(j));
        }
        LOGGER.debug(String.format("Batch size: %d", batch.size()));
        getDB().batchDeleteAttributes(new BatchDeleteAttributesRequest(
            domainName, batch));
        batch.clear();
      }
    }
  }

  @Override
    public SelectResult invokeFindImpl(boolean consistentRead, String escapedQuery) {
      LOGGER.debug("Query: {}", escapedQuery);
        return getDB().select(new SelectRequest(escapedQuery, consistentRead));
    }

    @Override
    public <T, ID extends Serializable> T readImpl(ID id, Class<T> entityClass, boolean consistentRead,
                                                   SimpleDbEntityInformation<T, ?> entityInformation) {
        LOGGER.debug("Read ItemName \"{}\"", id);

        List<ID> ids = new ArrayList<ID>();
        {
            ids.add(id);
        }
        List<T> results = find(entityClass, new QueryBuilder(entityInformation).withIds(ids).toString(), consistentRead);
        return results.size() == 1 ? results.get(0) : null;
    }

    @Override
    public <T> long countImpl(String query, boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation) {
      final String countQuery = new QueryBuilder(query, true).toString();
      return invokeCountImpl(consistentRead, entityInformation, countQuery);
    }

    @Override
    public <T> long countImpl(boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation) {
      final String countQuery = new QueryBuilder(entityInformation, true).toString();
      return invokeCountImpl(consistentRead, entityInformation, countQuery);
    }

  private <T> long invokeCountImpl(boolean consistentRead,
      SimpleDbEntityInformation<T, ?> entityInformation,
      final String countQuery) {
   
    LOGGER.debug("Count items for query " + countQuery);
        validateSelectQuery(countQuery);

        final String escapedQuery = getEscapedQuery(countQuery, entityInformation);

        final SelectResult selectResult = invokeFindImpl(consistentRead, escapedQuery);
        for (Item item : selectResult.getItems()) {
            if (item.getName().equals("Domain")) {
                for (Attribute attribute : item.getAttributes()) {
                    if (attribute.getName().equals("Count")) {
                        return Long.parseLong(attribute.getValue());
                    }
                }
            }
        }

        return 0;
  }

    @Override
    public <T> List<T> findAllQueryImpl(Class<T> entityClass, SimpleDbEntityInformation<T, ?> entityInformation) {
        final String findAllQuery = new QueryBuilder(entityInformation).toString();

        return find(entityClass, findAllQuery);
    }

    @Override
    public <T> Page<T> executePagedQueryImpl(Class<T> entityClass, String query, Pageable pageable,
                                             boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation) {
        Assert.notNull(pageable);
        Assert.isTrue(pageable.getPageNumber() >= 0);
        Assert.isTrue(pageable.getPageSize() > 0);

        final String escapedQuery = getEscapedQuery(query, entityInformation);

        List<T> resultsList;
        String queryWithPageSizeLimit = new QueryBuilder(escapedQuery).with(pageable).toString();

        if (pageable.getPageNumber() > 0) {
            String pageOffsetToken = getPageOffsetToken(pageable, entityInformation, escapedQuery, consistentRead);

            if (pageOffsetToken != null && !pageOffsetToken.isEmpty()) {
                resultsList = find(entityInformation, queryWithPageSizeLimit, pageOffsetToken, consistentRead);
            } else {
                resultsList = Collections.emptyList();
            }

        } else {
            resultsList = find(entityClass, queryWithPageSizeLimit, consistentRead);
        }

        final String countQuery = new QueryBuilder(escapedQuery, true).toString();

        Long totalCount = count(countQuery, entityClass, consistentRead);

        return new PageImpl<T>(resultsList, pageable, totalCount);
    }

    @Override
    public <T> List<T> recursiveFindImpl(Class<T> entityClass, String query, boolean consistentRead,
                                         SimpleDbEntityInformation<T, ?> entityInformation) {

        LOGGER.debug("Find All Domain \"{}\" isConsistent=\"{}\"", entityInformation.getDomain(), consistentRead);

        validateSelectQuery(query);

        final String escapedQuery = getEscapedQuery(query, entityInformation);

        List<T> result = new ArrayList<T>();

        List<String> referenceFieldsNames = ReflectionUtils.getReferencedAttributeNames(entityClass);

        final DomainItemBuilder<T> domainItemBuilder = new DomainItemBuilder<T>();

        final SelectResult selectResult = invokeFindImpl(consistentRead, escapedQuery);

        if (referenceFieldsNames.isEmpty()) {
            return domainItemBuilder.populateDomainItems(entityInformation, selectResult);
        }

        for (Item item : selectResult.getItems()) {

            T populatedItem = domainItemBuilder.populateDomainItem(entityInformation, item);

            result.add(populatedItem);
            for (Attribute attribute : item.getAttributes()) {
                if (!referenceFieldsNames.contains(attribute.getName())) {
                    continue;
                }

                Class<?> referenceEntityClazz = ReflectionUtils.getFieldClass(entityClass, attribute.getName());
                Object referenceEntity = read(attribute.getValue(), referenceEntityClazz);

                ReflectionUtils.callSetter(populatedItem, attribute.getName(), referenceEntity);

            }
        }

        return result;
    }

    @Override
    public <T> List<T> findImpl(SimpleDbEntityInformation<T, ?> entityInformation, String query, String nextToken,
                                boolean consistentRead) {

        LOGGER.debug("Find All Domain \"{}\" isConsistent=\"{}\", with token!", entityInformation.getDomain(),
                consistentRead);

        final DomainItemBuilder<T> domainItemBuilder = new DomainItemBuilder<T>();

        validateSelectQuery(query);

        final String escapedQuery = getEscapedQuery(query, entityInformation);
        SelectRequest selectRequest = new SelectRequest(escapedQuery, consistentRead);

        selectRequest.setNextToken(nextToken);

        final SelectResult selectResult = getDB().select(selectRequest);

        return domainItemBuilder.populateDomainItems(entityInformation, selectResult);
    }

  @SuppressWarnings("unchecked")
  @Override
  protected <T, ID> void updateImpl(ID id, Class<T> entityClass,
      Map<String, ? extends Object> propertyMap) {
    // From the propertyMap, retrieve the Field which will be updated,
      // from the Field, serialize the corresponding Object value as per
      // FieldWrapper#serialize semantics, plug into the scheme to convert
      // to item and send a put request.
    String domainName = getDomainName(entityClass);
      Map<String, String> serializedValues = new LinkedHashMap<String, String>();
    for (Map.Entry<String, ?> entry : propertyMap.entrySet()) {
        String propertyPath = entry.getKey();
        Object propertyValue = entry.getValue();
        if (propertyValue == null) {
          continue;
        }
        String serializedPropertyValue = null;
        Field propertyField = ReflectionUtils.getPropertyField(entityClass, propertyPath);
        if (FieldTypeIdentifier.isOfType(propertyField, FieldType.PRIMITIVE, FieldType.CORE_TYPE)) {
          serializedPropertyValue = SimpleDBAttributeConverter.encode(propertyValue);
       
        } else if (FieldTypeIdentifier.isOfType(propertyField, FieldType.NESTED_ENTITY)) {
          SimpleDbEntityInformation<T, Serializable> entityMetadata = (SimpleDbEntityInformation<T, Serializable>) SimpleDbEntityInformationSupport.getMetadata(propertyValue.getClass(), domainName);
        EntityWrapper<T, Serializable> entity = new EntityWrapper<T, Serializable>(entityMetadata, (T) propertyValue, true);
        Map<String, String> nestedAttributes = entity.serialize();
        // add to serializedValues after prefixing propertyPath
        for (Map.Entry<String, String> e : nestedAttributes.entrySet()) {
          String key = String.format("%s.%s", propertyPath, e.getKey());
          serializedValues.put(key, e.getValue());
        }
       
        } else {
          serializedPropertyValue = JsonMarshaller.getInstance().marshall(propertyValue);
        }

        if (serializedPropertyValue != null) {
          serializedValues.put(propertyPath, serializedPropertyValue);
        }
      }
    Map<String, List<String>> rawAttributes = SimpleDbAttributeValueSplitter.splitAttributeValuesWithExceedingLengths(serializedValues);
    List<PutAttributesRequest> putAttributesRequests = SimpleDbRequestBuilder.createPutAttributesRequests(
        domainName, (String) id, rawAttributes);

        for (PutAttributesRequest request : putAttributesRequests) {
            getDB().putAttributes(request);
        }
  }

    private <T> String getEscapedQuery(String query, SimpleDbEntityInformation<T, ?> entityInformation) {
        return QueryUtils.escapeQueryAttributes(query, MetadataParser.getIdField(entityInformation.getJavaType())
                .getName());
    }

    /*
     * Validate a custom query before sending the request to the DB.
     */
    private void validateSelectQuery(final String selectQuery) {
        final SimpleDBParser parser = new SimpleDBParser(selectQuery);
        try {
            parser.selectQuery();
        } catch (Exception e) {
            throw new InvalidSimpleDBQueryException("The following query is an invalid SimpleDB query: " + selectQuery,
                    e);
        }
    }

    private String getNextToken(String query, boolean consistentRead) {
        LOGGER.debug("Get next token for query: " + query);

        Assert.isTrue(query.contains("limit"), "Only queries with limit have a next token!");

        final SelectResult selectResult = getDB().select(new SelectRequest(query, consistentRead));

        return selectResult.getNextToken();
    }

    private <T> String getPageOffsetToken(final Pageable pageable, SimpleDbEntityInformation<T, ?> entityInformation,
                                          String query, boolean consistentRead) {

      int endOfPreviousPageLimit = pageable.getPageNumber() * pageable.getPageSize();

        final String escapedQuery = getEscapedQuery(query, entityInformation);
        final String countQuery = new QueryBuilder(escapedQuery, true).withLimit(endOfPreviousPageLimit).toString();

        return getNextToken(countQuery, consistentRead);
    }

    private void logOperation(String operation, EntityWrapper<?, ?> entity) {
        LOGGER.debug(operation + " \"{}\" ItemName \"{}\"", entity.getDomain(), entity.getItemName());
    }

}
TOP

Related Classes of org.springframework.data.simpledb.core.SimpleDbTemplate

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.