Package com.michelboudreau.alternator

Source Code of com.michelboudreau.alternator.AlternatorDBHandler

package com.michelboudreau.alternator;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.dynamodb.model.AttributeAction;
import com.amazonaws.services.dynamodb.model.AttributeValue;
import com.amazonaws.services.dynamodb.model.AttributeValueUpdate;
import com.amazonaws.services.dynamodb.model.BatchGetItemRequest;
import com.amazonaws.services.dynamodb.model.BatchGetItemResult;
import com.amazonaws.services.dynamodb.model.BatchResponse;
import com.amazonaws.services.dynamodb.model.BatchWriteItemRequest;
import com.amazonaws.services.dynamodb.model.BatchWriteItemResult;
import com.amazonaws.services.dynamodb.model.BatchWriteResponse;
import com.amazonaws.services.dynamodb.model.Condition;
import com.amazonaws.services.dynamodb.model.ConditionalCheckFailedException;
import com.amazonaws.services.dynamodb.model.CreateTableRequest;
import com.amazonaws.services.dynamodb.model.CreateTableResult;
import com.amazonaws.services.dynamodb.model.DeleteItemRequest;
import com.amazonaws.services.dynamodb.model.DeleteItemResult;
import com.amazonaws.services.dynamodb.model.DeleteRequest;
import com.amazonaws.services.dynamodb.model.DeleteTableRequest;
import com.amazonaws.services.dynamodb.model.DeleteTableResult;
import com.amazonaws.services.dynamodb.model.DescribeTableRequest;
import com.amazonaws.services.dynamodb.model.DescribeTableResult;
import com.amazonaws.services.dynamodb.model.ExpectedAttributeValue;
import com.amazonaws.services.dynamodb.model.GetItemRequest;
import com.amazonaws.services.dynamodb.model.GetItemResult;
import com.amazonaws.services.dynamodb.model.InternalServerErrorException;
import com.amazonaws.services.dynamodb.model.Key;
import com.amazonaws.services.dynamodb.model.KeySchema;
import com.amazonaws.services.dynamodb.model.KeySchemaElement;
import com.amazonaws.services.dynamodb.model.KeysAndAttributes;
import com.amazonaws.services.dynamodb.model.LimitExceededException;
import com.amazonaws.services.dynamodb.model.ListTablesRequest;
import com.amazonaws.services.dynamodb.model.ListTablesResult;
import com.amazonaws.services.dynamodb.model.PutItemRequest;
import com.amazonaws.services.dynamodb.model.PutItemResult;
import com.amazonaws.services.dynamodb.model.PutRequest;
import com.amazonaws.services.dynamodb.model.QueryRequest;
import com.amazonaws.services.dynamodb.model.QueryResult;
import com.amazonaws.services.dynamodb.model.ResourceInUseException;
import com.amazonaws.services.dynamodb.model.ResourceNotFoundException;
import com.amazonaws.services.dynamodb.model.ReturnValue;
import com.amazonaws.services.dynamodb.model.ScanRequest;
import com.amazonaws.services.dynamodb.model.ScanResult;
import com.amazonaws.services.dynamodb.model.TableStatus;
import com.amazonaws.services.dynamodb.model.UpdateItemRequest;
import com.amazonaws.services.dynamodb.model.UpdateItemResult;
import com.amazonaws.services.dynamodb.model.UpdateTableRequest;
import com.amazonaws.services.dynamodb.model.UpdateTableResult;
import com.amazonaws.services.dynamodb.model.WriteRequest;
import com.amazonaws.services.dynamodb.model.transform.BatchGetItemRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.BatchGetItemResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.BatchWriteItemRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.BatchWriteItemResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.CreateTableRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.CreateTableResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.DeleteItemRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.DeleteItemResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.DeleteTableRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.DeleteTableResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.DescribeTableRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.DescribeTableResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.GetItemRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.GetItemResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.ListTablesRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.ListTablesResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.PutItemRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.PutItemResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.QueryRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.QueryResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.ScanRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.ScanResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.UpdateItemRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.UpdateItemResultMarshaller;
import com.amazonaws.services.dynamodb.model.transform.UpdateTableRequestJsonUnmarshaller;
import com.amazonaws.services.dynamodb.model.transform.UpdateTableResultMarshaller;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.michelboudreau.alternator.enums.AttributeValueType;
import com.michelboudreau.alternator.enums.RequestType;
import com.michelboudreau.alternator.models.ItemRangeGroup;
import com.michelboudreau.alternator.models.Limits;
import com.michelboudreau.alternator.models.Table;
import com.michelboudreau.alternator.parsers.AmazonWebServiceRequestParser;
import com.michelboudreau.alternator.validators.CreateTableRequestValidator;
import com.michelboudreau.alternator.validators.DeleteItemRequestValidator;
import com.michelboudreau.alternator.validators.DeleteTableRequestValidator;
import com.michelboudreau.alternator.validators.DescribeTableRequestValidator;
import com.michelboudreau.alternator.validators.GetItemRequestValidator;
import com.michelboudreau.alternator.validators.ListTablesRequestValidator;
import com.michelboudreau.alternator.validators.PutItemRequestValidator;
import com.michelboudreau.alternator.validators.QueryRequestValidator;
import com.michelboudreau.alternator.validators.ScanRequestValidator;
import com.michelboudreau.alternator.validators.UpdateItemRequestValidator;
import com.michelboudreau.alternator.validators.UpdateTableRequestValidator;
import com.michelboudreau.alternatorv2.AlternatorDBApiVersion2Mapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

public class AlternatorDBHandler {
    public final String API_VERSION_20111205 = "DynamoDB_20111205";
    public final String API_VERSION_20120810 = "DynamoDB_20120810";

  private final Logger logger = LoggerFactory.getLogger(AlternatorDBHandler.class);

  private Map<String, Table> tables = new HashMap<String, Table>();
  private List<Table> tableList = new ArrayList<Table>();

  // TODO: create constructor that can handle a file
  public AlternatorDBHandler() {
  }

  // Maybe save automatically on destroy?
  public synchronized void save(String persistence) {
    try {
      createObjectMapper().writeValue(new File(persistence), tableList);
    } catch (IOException e) {
      e.printStackTrace()//To change body of catch statement use File | Settings | File Templates.
    }
  }

  public synchronized void restore(String persistence) {
    try {
      File dbFile = new File(persistence);
      if (dbFile.exists() == false) {
        return;
      }

      ObjectMapper objectMapper = createObjectMapper();
      tableList = objectMapper.readValue(dbFile, objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, Table.class));

      for (Table table : tableList) {
        tables.put(table.getName(), table);
      }
    } catch (IOException e) {
      e.printStackTrace()//To change body of catch statement use File | Settings | File Templates.
    }
  }

  // Not sure about this.  If correct and only need one, only create one instance
  public ObjectMapper createObjectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
        .setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.ANY)
        .setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);

    mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

    return mapper;
  }

  public String handle(HttpServletRequest request) throws LimitExceededException, InternalServerErrorException, ResourceInUseException, ResourceNotFoundException, ConditionalCheckFailedException {
        try {
            AmazonWebServiceRequestParser parser = new AmazonWebServiceRequestParser(request);

            String apiVersion = parser.getApiVersion();
            if (API_VERSION_20111205.equals(apiVersion)) {
                return handle20111205(request, parser);
            } else if (API_VERSION_20120810.equals(apiVersion)) {
                return handle20120810(request, parser);
            } else {
                String logMessage = "The API Version " + apiVersion + " is not supported.";
                logger.warn(logMessage);
                throw new AmazonServiceException(logMessage);
            }
        } catch (NullPointerException ex) {
            StackTraceElement[] stackTrace = ex.getStackTrace();
            StackTraceElement exceptionSource = stackTrace[0];
            String errorMessage = "Caught " + ex.getClass().getName() +
                    " (" + ex.getMessage() +
                    ") at " + exceptionSource.getClassName() +
                    "." + exceptionSource.getMethodName() +
                    " line " + exceptionSource.getLineNumber();
            logger.error(errorMessage);
            throw new AmazonServiceException(errorMessage);
        }
  }

  private String handle20111205(HttpServletRequest request, AmazonWebServiceRequestParser parser) throws LimitExceededException, InternalServerErrorException, ResourceInUseException, ResourceNotFoundException, ConditionalCheckFailedException {
        RequestType requestType = parser.getType();
    switch (requestType) {
      // Tables
      case CREATE_TABLE:
        return new CreateTableResultMarshaller().marshall(createTable(parser.getData(CreateTableRequest.class, CreateTableRequestJsonUnmarshaller.getInstance())));
      case DESCRIBE_TABLE:
        return new DescribeTableResultMarshaller().marshall(describeTable(parser.getData(DescribeTableRequest.class, DescribeTableRequestJsonUnmarshaller.getInstance())));
      case LIST_TABLES:
        return new ListTablesResultMarshaller().marshall(listTables(parser.getData(ListTablesRequest.class, ListTablesRequestJsonUnmarshaller.getInstance())));
      case UPDATE_TABLE:
        return new UpdateTableResultMarshaller().marshall(updateTable(parser.getData(UpdateTableRequest.class, UpdateTableRequestJsonUnmarshaller.getInstance())));
      case DELETE_TABLE:
        return new DeleteTableResultMarshaller().marshall(deleteTable(parser.getData(DeleteTableRequest.class, DeleteTableRequestJsonUnmarshaller.getInstance())));

      // Items
      case PUT:
        return new PutItemResultMarshaller().marshall(putItem(parser.getData(PutItemRequest.class, PutItemRequestJsonUnmarshaller.getInstance())));
      case GET:
        return new GetItemResultMarshaller().marshall(getItem(parser.getData(GetItemRequest.class, GetItemRequestJsonUnmarshaller.getInstance())));

      case UPDATE:
        return new UpdateItemResultMarshaller().marshall(updateItem(parser.getData(UpdateItemRequest.class, UpdateItemRequestJsonUnmarshaller.getInstance())));
      case DELETE:
        return new DeleteItemResultMarshaller().marshall(deleteItem(parser.getData(DeleteItemRequest.class, DeleteItemRequestJsonUnmarshaller.getInstance())));
      case BATCH_GET_ITEM:
        return new BatchGetItemResultMarshaller().marshall((batchGetItem(parser.getData(BatchGetItemRequest.class, BatchGetItemRequestJsonUnmarshaller.getInstance()))));
      case BATCH_WRITE_ITEM:
        return new BatchWriteItemResultMarshaller().marshall((batchWriteItem(parser.getData(BatchWriteItemRequest.class, BatchWriteItemRequestJsonUnmarshaller.getInstance()))));

      // Operations
      case QUERY:
        return new QueryResultMarshaller().marshall(query(parser.getData(QueryRequest.class, QueryRequestJsonUnmarshaller.getInstance())));
      case SCAN:
        return new ScanResultMarshaller().marshall(scan(parser.getData(ScanRequest.class, ScanRequestJsonUnmarshaller.getInstance())));
      default:
                String logMessage = "The Request Type '" + parser.getType() + "' does not exist.";
                logger.warn(logMessage);
                throw new AmazonServiceException(logMessage);
    }
  }

  private String handle20120810(HttpServletRequest request, AmazonWebServiceRequestParser parser) throws LimitExceededException, InternalServerErrorException, ResourceInUseException, ResourceNotFoundException, ConditionalCheckFailedException {
        RequestType requestType = parser.getType();
    switch (requestType) {
      // Tables
      case CREATE_TABLE:
        return new com.amazonaws.services.dynamodbv2.model.transform.CreateTableResultMarshaller().marshall(createTableV2(parser.getData(com.amazonaws.services.dynamodbv2.model.CreateTableRequest.class, com.amazonaws.services.dynamodbv2.model.transform.CreateTableRequestJsonUnmarshaller.getInstance())));
      case DESCRIBE_TABLE:
        return new com.amazonaws.services.dynamodbv2.model.transform.DescribeTableResultMarshaller().marshall(describeTableV2(parser.getData(com.amazonaws.services.dynamodbv2.model.DescribeTableRequest.class, com.amazonaws.services.dynamodbv2.model.transform.DescribeTableRequestJsonUnmarshaller.getInstance())));
      case LIST_TABLES:
        return new com.amazonaws.services.dynamodbv2.model.transform.ListTablesResultMarshaller().marshall(listTablesV2(parser.getData(com.amazonaws.services.dynamodbv2.model.ListTablesRequest.class, com.amazonaws.services.dynamodbv2.model.transform.ListTablesRequestJsonUnmarshaller.getInstance())));
      case UPDATE_TABLE:
        return new com.amazonaws.services.dynamodbv2.model.transform.UpdateTableResultMarshaller().marshall(updateTableV2(parser.getData(com.amazonaws.services.dynamodbv2.model.UpdateTableRequest.class, com.amazonaws.services.dynamodbv2.model.transform.UpdateTableRequestJsonUnmarshaller.getInstance())));
      case DELETE_TABLE:
        return new com.amazonaws.services.dynamodbv2.model.transform.DeleteTableResultMarshaller().marshall(deleteTableV2(parser.getData(com.amazonaws.services.dynamodbv2.model.DeleteTableRequest.class, com.amazonaws.services.dynamodbv2.model.transform.DeleteTableRequestJsonUnmarshaller.getInstance())));

      // Items
      case PUT:
        return new com.amazonaws.services.dynamodbv2.model.transform.PutItemResultMarshaller().marshall(putItemV2(parser.getData(com.amazonaws.services.dynamodbv2.model.PutItemRequest.class, com.amazonaws.services.dynamodbv2.model.transform.PutItemRequestJsonUnmarshaller.getInstance())));
      case GET:
        return new com.amazonaws.services.dynamodbv2.model.transform.GetItemResultMarshaller().marshall(getItemV2(parser.getData(com.amazonaws.services.dynamodbv2.model.GetItemRequest.class, com.amazonaws.services.dynamodbv2.model.transform.GetItemRequestJsonUnmarshaller.getInstance())));

      case UPDATE:
        return new com.amazonaws.services.dynamodbv2.model.transform.UpdateItemResultMarshaller().marshall(updateItemV2(parser.getData(com.amazonaws.services.dynamodbv2.model.UpdateItemRequest.class, com.amazonaws.services.dynamodbv2.model.transform.UpdateItemRequestJsonUnmarshaller.getInstance())));
      case DELETE:
        return new com.amazonaws.services.dynamodbv2.model.transform.DeleteItemResultMarshaller().marshall(deleteItemV2(parser.getData(com.amazonaws.services.dynamodbv2.model.DeleteItemRequest.class, com.amazonaws.services.dynamodbv2.model.transform.DeleteItemRequestJsonUnmarshaller.getInstance())));
      case BATCH_GET_ITEM:
        return new com.amazonaws.services.dynamodbv2.model.transform.BatchGetItemResultMarshaller().marshall(batchGetItemV2(parser.getData(com.amazonaws.services.dynamodbv2.model.BatchGetItemRequest.class, com.amazonaws.services.dynamodbv2.model.transform.BatchGetItemRequestJsonUnmarshaller.getInstance())));
      case BATCH_WRITE_ITEM:
        return new com.amazonaws.services.dynamodbv2.model.transform.BatchWriteItemResultMarshaller().marshall(batchWriteItemV2(parser.getData(com.amazonaws.services.dynamodbv2.model.BatchWriteItemRequest.class, com.amazonaws.services.dynamodbv2.model.transform.BatchWriteItemRequestJsonUnmarshaller.getInstance())));

      // Operations
      case QUERY:
        return new com.amazonaws.services.dynamodbv2.model.transform.QueryResultMarshaller().marshall(queryV2(parser.getData(com.amazonaws.services.dynamodbv2.model.QueryRequest.class, com.amazonaws.services.dynamodbv2.model.transform.QueryRequestJsonUnmarshaller.getInstance())));
      case SCAN:
        return new com.amazonaws.services.dynamodbv2.model.transform.ScanResultMarshaller().marshall(scanV2(parser.getData(com.amazonaws.services.dynamodbv2.model.ScanRequest.class, com.amazonaws.services.dynamodbv2.model.transform.ScanRequestJsonUnmarshaller.getInstance())));
      default:
                String logMessage = "The Request Type '" + parser.getType() + "' does not exist.";
                logger.warn(logMessage);
                throw new AmazonServiceException(logMessage);
    }
  }

  public synchronized CreateTableResult createTable(CreateTableRequest request) throws LimitExceededException, InternalServerErrorException, ResourceInUseException {
    // table limit of 256
    if (this.tables.size() >= Limits.TABLE_MAX) {
      throw new LimitExceededException("Cannot exceed 256 tables per account.");
    }

    // Validate data coming in
    // TODO: Look into how we're doing validation, maybe implement better solution
    CreateTableRequestValidator validator = new CreateTableRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());
    }

    // get information
    String tableName = request.getTableName();

    // Check to make sure table with same name doesn't exist
    if (this.tables.containsKey(tableName)) {
      throw new ResourceInUseException("The table you're currently trying to create (" + tableName + ") is already available.");
    }

    // Add table to map, array
    Table table = new Table(tableName, request.getKeySchema(), request.getProvisionedThroughput());
    this.tables.put(tableName, table);
    this.tableList.add(table);

    return new CreateTableResult().withTableDescription(table.getTableDescription());
  }

  public com.amazonaws.services.dynamodbv2.model.CreateTableResult createTableV2(com.amazonaws.services.dynamodbv2.model.CreateTableRequest v2Request) throws LimitExceededException, InternalServerErrorException, ResourceInUseException {
        CreateTableRequest request = AlternatorDBApiVersion2Mapper.MapV2CreateTableRequestToV1(v2Request);
        try {
            CreateTableResult result = createTable(request);
            return AlternatorDBApiVersion2Mapper.MapV1CreateTableResultToV2(result);
        } catch (ResourceInUseException ex) {
            throw new com.amazonaws.services.dynamodbv2.model.ResourceInUseException(ex.getMessage());
        }
  }

  public synchronized DescribeTableResult describeTable(DescribeTableRequest request) throws InternalServerErrorException, ResourceNotFoundException {
    // Validate data coming in
    DescribeTableRequestValidator validator = new DescribeTableRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());
    }

    // get information
    String tableName = request.getTableName();
    DescribeTableResult result = null;

    // Check to make sure table with same name doesn't exist
    if (this.tables.containsKey(tableName)) {
      Table table = this.tables.get(tableName);
      result = new DescribeTableResult().withTable(table.getTableDescription());
    } else {
      throw new ResourceNotFoundException("The table '" + tableName + "' does not exist.");
    }
    return result;
  }

  public com.amazonaws.services.dynamodbv2.model.DescribeTableResult describeTableV2(com.amazonaws.services.dynamodbv2.model.DescribeTableRequest v2Request) throws InternalServerErrorException, ResourceNotFoundException {
        DescribeTableRequest request = AlternatorDBApiVersion2Mapper.MapV2DescribeTableRequestToV1(v2Request);
        DescribeTableResult result = describeTable(request);
        return AlternatorDBApiVersion2Mapper.MapV1DescribeTableResultToV2(result);
  }

  public synchronized ListTablesResult listTables(ListTablesRequest request) throws InternalServerErrorException, ResourceNotFoundException {
    // Validate data coming in
    ListTablesRequestValidator validator = new ListTablesRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw createInternalServerException(errors);
    }

    // Create defaults
    String startTableName = request.getExclusiveStartTableName();
    Integer limit = request.getLimit();
    if (limit == null) {
      limit = 100;
    }

    // Check if startTableName exists
    int startIndex = 0;
    if (startTableName != null) {
      if (this.tables.containsKey(startTableName)) {
        for (int i = 0; i < this.tableList.size(); i++) {
          if (tableList.get(i).getName().equals(startTableName)) {
            startIndex = i;
            break;
          }
        }
      } else {
        throw new ResourceNotFoundException("The ExclusiveStartTableName '" + startTableName + "' doesn't exist.");
      }
    }

    // Calculate size max. depending on array size and limit
    int size = this.tableList.size();
    Boolean setTableName = false;
    if (size > (startIndex + limit)) {
      size = startIndex + limit;
      setTableName = true;
    }

    // Get list
    List<String> tables = new ArrayList<String>();
    for (int i = startIndex; i < size; i++) {
      tables.add(this.tableList.get(i).getName());
    }

    // Create result object
    ListTablesResult result = new ListTablesResult().withTableNames(tables);
    if (setTableName) {
      result.setLastEvaluatedTableName(this.tableList.get(size).getName());
    }

    return result;
  }

  public com.amazonaws.services.dynamodbv2.model.ListTablesResult listTablesV2(com.amazonaws.services.dynamodbv2.model.ListTablesRequest v2Request) throws InternalServerErrorException, ResourceNotFoundException {
        ListTablesRequest request = AlternatorDBApiVersion2Mapper.MapV2ListTablesRequestToV1(v2Request);
        ListTablesResult result = listTables(request);
        return AlternatorDBApiVersion2Mapper.MapV1ListTablesResultToV2(result);
  }

  public synchronized DeleteTableResult deleteTable(DeleteTableRequest request) throws InternalServerErrorException, ResourceNotFoundException {
    // Validate data coming in
    DeleteTableRequestValidator validator = new DeleteTableRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());
    }

    // Check existence
    if (!this.tables.containsKey(request.getTableName())) {
      throw new ResourceNotFoundException("The table you want to delete '" + request.getTableName() + "' doesn't exist.");
    }

    // Delete Table
    Table table = tables.remove(request.getTableName());
    tableList.remove(table);

    return new DeleteTableResult().withTableDescription(table.getTableDescription().withTableStatus(TableStatus.DELETING));
  }

  public com.amazonaws.services.dynamodbv2.model.DeleteTableResult deleteTableV2(com.amazonaws.services.dynamodbv2.model.DeleteTableRequest v2Request) throws InternalServerErrorException, ResourceNotFoundException {
        DeleteTableRequest request = AlternatorDBApiVersion2Mapper.MapV2DeleteTableRequestToV1(v2Request);
        try {
            DeleteTableResult result = deleteTable(request);
            return AlternatorDBApiVersion2Mapper.MapV1DeleteTableResultToV2(result);
        } catch (ResourceNotFoundException ex) {
            throw new com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException(ex.getMessage());
        }
  }

  public synchronized UpdateTableResult updateTable(UpdateTableRequest request) throws InternalServerErrorException, ResourceNotFoundException {
    // Validate data coming in
    UpdateTableRequestValidator validator = new UpdateTableRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());
    }

    // Check existence
    if (!this.tables.containsKey(request.getTableName())) {
      throw new ResourceNotFoundException("The table '" + request.getTableName() + "' doesn't exist.");
    }

    // Update Table
    Table table = this.tables.get(request.getTableName());
    table.setProvisionedThroughput(request.getProvisionedThroughput());

    return new UpdateTableResult().withTableDescription(table.getTableDescription());
  }

  public com.amazonaws.services.dynamodbv2.model.UpdateTableResult updateTableV2(com.amazonaws.services.dynamodbv2.model.UpdateTableRequest v2Request) throws InternalServerErrorException, ResourceNotFoundException {
        UpdateTableRequest request = AlternatorDBApiVersion2Mapper.MapV2UpdateTableRequestToV1(v2Request);
        UpdateTableResult result = updateTable(request);
        return AlternatorDBApiVersion2Mapper.MapV1UpdateTableResultToV2(result);
  }

  public synchronized PutItemResult putItem(PutItemRequest request) throws InternalServerErrorException, ResourceNotFoundException, ConditionalCheckFailedException {
    // Validate data coming in
    PutItemRequestValidator validator = new PutItemRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());

    }

    // Check existence of table
    Table table = this.tables.get(request.getTableName());
    if (table == null) {
      throw new ResourceNotFoundException("The table '" + request.getTableName() + "' doesn't exist.");
    }

    // Make sure that item specifies hash key and range key (if in schema)
    KeySchemaElement hashKey = table.getKeySchema().getHashKeyElement();
    KeySchemaElement rangeKey = table.getKeySchema().getRangeKeyElement();
    AttributeValue hashItem = request.getItem().get(hashKey.getAttributeName());
    AttributeValueType hashItemType = getAttributeValueType(hashItem);
    if (hashItem == null || hashItemType != AttributeValueType.fromString(hashKey.getAttributeType())) {
      throw new InternalServerErrorException("Missing hash key (" + hashKey.getAttributeName() + ") from item: " + request.getItem());
    }
    if (rangeKey != null) {
      AttributeValue rangeItem = request.getItem().get(rangeKey.getAttributeName());
      AttributeValueType rangeItemType = getAttributeValueType(rangeItem);
      if (rangeItem == null || rangeItemType != AttributeValueType.fromString(rangeKey.getAttributeType())) {
        throw new InternalServerErrorException("Missing range key (" + rangeKey.getAttributeName() + ") from item: " + request.getItem());
      }
    }

    // Get current item if it exists
//    Map<String, AttributeValue> item = table.getItem(getKeyValue(request.getItem().get(table.getHashKeyName())));
        Map<String, AttributeValue> requestItem = request.getItem();
        String hashKeyValue = getKeyValue(requestItem.get(table.getHashKeyName()));
        String rangeKeyValue = getKeyValue(requestItem.get(table.getRangeKeyName()));
    Map<String, AttributeValue> item = table.getItem(hashKeyValue, rangeKeyValue);

    // Check conditional put
    validateExpected(request.getExpected(), item);

    PutItemResult result = new PutItemResult().withConsumedCapacityUnits(1D);
    if (item != null && request.getReturnValues() != null && ReturnValue.fromValue(request.getReturnValues()) == ReturnValue.ALL_OLD) {
      result.setAttributes(item);
    }

    // put the item in the table
    table.putItem(request.getItem());

    return result;
  }

    private boolean compareAttributeValues(AttributeValue a, AttributeValue b) {
        if (a.getB() != null && a.getB().equals(b.getB())) {
            return true;
        } else if (a.getN() != null && a.getN().equals(b.getN())) {
            return true;
        } else if (a.getS() != null && a.getS().equals(b.getS())) {
            return true;
        } else if (a.getSS() != null && a.getSS().equals(b.getSS())) {
            return true;
        }

        return false;
    }

  public synchronized com.amazonaws.services.dynamodbv2.model.PutItemResult putItemV2(com.amazonaws.services.dynamodbv2.model.PutItemRequest v2Request) throws InternalServerErrorException, ResourceNotFoundException, ConditionalCheckFailedException {
        PutItemRequest request = AlternatorDBApiVersion2Mapper.MapV2PutItemRequestToV1(v2Request);
        try {
            PutItemResult result = putItem(request);
            return AlternatorDBApiVersion2Mapper.MapV1PutItemResultToV2(result, v2Request.getTableName());
        } catch (ConditionalCheckFailedException ex) {
            throw new com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException(ex.getMessage());
        }
  }

  public synchronized GetItemResult getItem(GetItemRequest request) throws InternalServerErrorException, ResourceNotFoundException {
    // Validate data coming in
    GetItemRequestValidator validator = new GetItemRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());
    }

    // get information
    String tableName = request.getTableName();
    Key key = request.getKey();
    List<String> attributesToGet = request.getAttributesToGet();
    GetItemResult result = new GetItemResult();

    // Check to make sure table exists
    if (!this.tables.containsKey(tableName)) {
      throw new ResourceNotFoundException("The table you're currently trying to access (" + tableName + ") doesn't exists.");
    }
    // Check to make sure Key is valid
    String hashKeyValue = getKeyValue(key.getHashKeyElement());
        ItemRangeGroup rangeGroup = this.tables.get(tableName).getItemRangeGroup(hashKeyValue);

    if (rangeGroup == null) {
      return new GetItemResult();
      // throw new ResourceNotFoundException("No item with Hash Key (" + hashKeyValue + ") exists.");
    } else {
            String rangeKeyValue = getKeyValue(key.getRangeKeyElement());
            Map<String, AttributeValue> item = this.tables.get(tableName).getItem(hashKeyValue, rangeKeyValue);
            if (item == null) {
        return new GetItemResult();
        // throw new ResourceNotFoundException("No item with Hash Key (" + hashKeyValue + ") and Range Key )" + rangeKeyValue + ") exists.");
            }

      if (attributesToGet == null) {
        result.setItem(item);
      } else {
                Map<String, AttributeValue> response = new HashMap<String, AttributeValue>();
        for (String att : attributesToGet) {
          AttributeValue res = item.get(att);
          if (res != null) {
            response.put(att, res);
          }
        }
        result.setItem(response);
      }
    }
    return result;
  }

  public com.amazonaws.services.dynamodbv2.model.GetItemResult getItemV2(com.amazonaws.services.dynamodbv2.model.GetItemRequest v2Request) throws InternalServerErrorException, ResourceNotFoundException {
        Table table = this.tables.get(v2Request.getTableName());
        GetItemRequest request = AlternatorDBApiVersion2Mapper.MapV2GetItemRequestToV1(v2Request, table);
        GetItemResult result = getItem(request);
        return AlternatorDBApiVersion2Mapper.MapV1GetItemResultToV2(result, v2Request.getTableName());
  }

  public synchronized DeleteItemResult deleteItem(DeleteItemRequest request) {
    // Validate data coming in
    DeleteItemRequestValidator validator = new DeleteItemRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());
    }

    // Check existence of table
    Table table = this.tables.get(request.getTableName());
    if (table == null) {
      throw new ResourceNotFoundException("The table '" + request.getTableName() + "' doesn't exist.");
    }

    // Get hash and range key
    String hashKey = getKeyValue(request.getKey().getHashKeyElement());
    String rangeKey = getKeyValue(request.getKey().getRangeKeyElement());

    // Get current item if exist
    Map<String, AttributeValue> item = table.getItem(hashKey, rangeKey);

    if (item == null) {
            if (rangeKey == null) {
                throw new ResourceNotFoundException("The item with hash key '" + hashKey + "' doesn't exist in table '" + table.getName() + "'");
            } else {
                throw new ResourceNotFoundException("The item with hash key '" + hashKey + "' and range key '" + rangeKey + "' doesn't exist in table '" + table.getName() + "'");
            }
    }

    // Check conditional put
    validateExpected(request.getExpected(), item);

    DeleteItemResult result = new DeleteItemResult().withConsumedCapacityUnits(1D);
    if (item != null && request.getReturnValues() != null && ReturnValue.fromValue(request.getReturnValues()) == ReturnValue.ALL_OLD) {
      result.setAttributes(item);
    }

    // remove the item from the table
    table.removeItem(hashKey, rangeKey);
    return result;
  }

  public com.amazonaws.services.dynamodbv2.model.DeleteItemResult deleteItemV2(com.amazonaws.services.dynamodbv2.model.DeleteItemRequest v2Request) {
        Table table = this.tables.get(v2Request.getTableName());
        DeleteItemRequest request = AlternatorDBApiVersion2Mapper.MapV2DeleteItemRequestToV1(v2Request, table);
        DeleteItemResult result = deleteItem(request);
        return AlternatorDBApiVersion2Mapper.MapV1DeleteItemResultToV2(result, v2Request.getTableName());
  }

  public synchronized BatchGetItemResult batchGetItem(BatchGetItemRequest request) {
    BatchGetItemResult batchGetItemResult = new BatchGetItemResult();
    Map<String, BatchResponse> response = new HashMap<String, BatchResponse>();
    for (String tableName : request.getRequestItems().keySet()) {
      BatchResponse batchResponse = new BatchResponse();
      List<Map<String, AttributeValue>> items = new ArrayList<Map<String, AttributeValue>>();
      KeysAndAttributes keysAndAttributes = request.getRequestItems().get(tableName);
      List<Key> itemKeys = keysAndAttributes.getKeys();
      List<String> attributeToGet = keysAndAttributes.getAttributesToGet();
      try {
        for (Key itemKey : itemKeys) {
          try {
                        String hashKeyValue = getKeyValue(itemKey.getHashKeyElement());
                        String rangeKeyValue = getKeyValue(itemKey.getRangeKeyElement());
            Map<String, AttributeValue> item = this.tables.get(tableName).getItem(hashKeyValue, rangeKeyValue);
            item = getItemWithAttributesToGet(item, attributeToGet);
            if (item != null)
              items.add(item);
          } catch (NullPointerException e) {
            System.err.println("Caught NullPointerException: " + e.getMessage());
          }
        }
      } catch (NullPointerException e) {
        System.err.println("Caught NullPointerException: " + e.getMessage());
      }
      batchResponse.setConsumedCapacityUnits(1.0);
      if (items.size() != 0) {
        batchResponse.setItems(items);
        response.put(tableName, batchResponse);
        batchGetItemResult.setResponses(response);
        batchGetItemResult.getResponses().put(tableName, batchResponse);
      }
    }
        batchGetItemResult.setUnprocessedKeys(new HashMap<String, KeysAndAttributes>());
    return batchGetItemResult;
  }

  public com.amazonaws.services.dynamodbv2.model.BatchGetItemResult batchGetItemV2(com.amazonaws.services.dynamodbv2.model.BatchGetItemRequest v2Request) {
        BatchGetItemRequest request = AlternatorDBApiVersion2Mapper.MapV2BatchGetItemRequestToV1(v2Request, this.tables);
        BatchGetItemResult result = batchGetItem(request);
        final Set<String> requestedTables = v2Request.getRequestItems().keySet();
        return AlternatorDBApiVersion2Mapper.MapV1BatchGetItemResultToV2(result, this.tables, requestedTables);
  }

  public synchronized BatchWriteItemResult batchWriteItem(BatchWriteItemRequest request) {
    BatchWriteItemResult batchWriteItemResult = new BatchWriteItemResult();
    HashMap<String, BatchWriteResponse> responses = new HashMap<String, BatchWriteResponse>();
    for (String tableName : request.getRequestItems().keySet()) {
      BatchWriteResponse batchWriteResponse = new BatchWriteResponse();
      List<WriteRequest> writeRequests = request.getRequestItems().get(tableName);
      for (WriteRequest writeRequest : writeRequests) {
        PutRequest putRequest = writeRequest.getPutRequest();
        if (putRequest != null) {
          this.tables.get(tableName).putItem(putRequest.getItem());
        }
        DeleteRequest deleteRequest = writeRequest.getDeleteRequest();
        if (deleteRequest != null) {
          Key key = deleteRequest.getKey();
          if (key != null) {
            this.tables.get(tableName).removeItem(key.getHashKeyElement().getS());
          }
        }
      }
      batchWriteResponse.setConsumedCapacityUnits(1.0);
      responses.put(tableName, batchWriteResponse);
    }
        batchWriteItemResult.setResponses(responses);
        batchWriteItemResult.setUnprocessedItems(new HashMap<String, List<WriteRequest>>());
    return batchWriteItemResult;
  }

  public com.amazonaws.services.dynamodbv2.model.BatchWriteItemResult batchWriteItemV2(com.amazonaws.services.dynamodbv2.model.BatchWriteItemRequest v2Request) {
     try {
    com.amazonaws.services.dynamodbv2.model.BatchWriteItemResult batchWriteItemResult =
                new com.amazonaws.services.dynamodbv2.model.BatchWriteItemResult();
        List<com.amazonaws.services.dynamodbv2.model.ConsumedCapacity> v2Capacities =
                new ArrayList<com.amazonaws.services.dynamodbv2.model.ConsumedCapacity>();
    for (String tableName : v2Request.getRequestItems().keySet()) {
      List<com.amazonaws.services.dynamodbv2.model.WriteRequest> writeRequests = v2Request.getRequestItems().get(tableName);
      for (com.amazonaws.services.dynamodbv2.model.WriteRequest writeRequest : writeRequests) {
        com.amazonaws.services.dynamodbv2.model.PutRequest putRequest = writeRequest.getPutRequest();
        if (putRequest != null) {
                    com.amazonaws.services.dynamodbv2.model.PutItemRequest putItemRequest =
                        new com.amazonaws.services.dynamodbv2.model.PutItemRequest()
                            .withTableName(tableName)
                            .withItem(putRequest.getItem())
                            ;
          putItemV2(putItemRequest);
        }
        com.amazonaws.services.dynamodbv2.model.DeleteRequest deleteRequest = writeRequest.getDeleteRequest();
        if (deleteRequest != null) {
                    com.amazonaws.services.dynamodbv2.model.DeleteItemRequest deleteItemRequest =
                        new com.amazonaws.services.dynamodbv2.model.DeleteItemRequest()
                            .withTableName(tableName)
                            .withKey(deleteRequest.getKey())
                            ;
                    deleteItemV2(deleteItemRequest);
        }
      }
            v2Capacities.add(
                new com.amazonaws.services.dynamodbv2.model.ConsumedCapacity()
                    .withTableName(tableName)
                    .withCapacityUnits(1.0)
                    );
    }
        batchWriteItemResult.setUnprocessedItems(
            new HashMap<String, List<com.amazonaws.services.dynamodbv2.model.WriteRequest>>()
            );
        batchWriteItemResult.setConsumedCapacity(v2Capacities);
    return batchWriteItemResult;
      } catch (ResourceNotFoundException rnfe) {
      throw new com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException(rnfe.getMessage());
      }
  }

    private boolean isComparableInScan(AttributeValue value, AttributeValue comp) {
        if (comp == null) return false;
        final AttributeValueType compType = getAttributeValueType(comp);
        final AttributeValueType valueType = getAttributeValueType(value);
        return (compType.equals(AttributeValueType.N) || compType.equals(AttributeValueType.S)) && compType.equals(valueType);
    }

    private int compareForScan(AttributeValue value, AttributeValue comp) {
        //Get type
        final AttributeValueType valueType = getAttributeValueType(value);
        if (valueType.equals(AttributeValueType.S)) return value.getS().compareTo(comp.getS());

        if (valueType.equals(AttributeValueType.N)) {
            try {
                return new Long(value.getN()).compareTo(new Long(comp.getN()));
            } catch (NumberFormatException e) {
                return new Double(value.getN()).compareTo(new Double(comp.getN()));
            }
        }

        throw new IllegalArgumentException("Can only compare String and Number types, got " + valueType);
    }

    private String getAttributeValueAsString(AttributeValue value) {
        final AttributeValueType valueType = getAttributeValueType(value);
        if (valueType.equals(AttributeValueType.N)) return value.getN();
        if (valueType.equals(AttributeValueType.S)) return value.getS();
        throw new IllegalArgumentException("Can only return values for Number and String, got " + valueType);
    }

  public synchronized ScanResult scan(ScanRequest request) {
    ScanResult result = new ScanResult();
    List<Error> errors = new ScanRequestValidator().validate(request);
    if (errors.size() > 0) {
      throw createInternalServerException(errors);
    }
    result.setConsumedCapacityUnits(0.5);
    List<Map<String, AttributeValue>> items = new ArrayList<Map<String, AttributeValue>>();
    for (String key : this.tables.get(request.getTableName()).getItemRangeGroups().keySet()) {
          ItemRangeGroup rangeGroup = this.tables.get(request.getTableName()).getItemRangeGroup(key);
          for (String rangeKey : rangeGroup.getKeySet()) {
      Map<String, AttributeValue> item = rangeGroup.getItem(rangeKey);
      if (request.getScanFilter() != null) {
                //Don't add item immediately - evaluate all conditions first
                boolean shouldAddItem = true;
        for (String k : request.getScanFilter().keySet()) {
                    //Set this to true if one of conditions matches
                    boolean conditionMatches = false;
                    final AttributeValue attribute = item.get(k);
          if (attribute != null) {
            final Condition cond = request.getScanFilter().get(k);
                        final AttributeValue comp = cond.getAttributeValueList().isEmpty() ? null : cond.getAttributeValueList().get(0);
                        final int condSize = cond.getAttributeValueList().size();

                        if (cond.getComparisonOperator() == null) {
              throw new ResourceNotFoundException("There must be a comparisonOperator");
            }

                        if (cond.getComparisonOperator().equals("EQ")) {
              if (condSize == 1 && isComparableInScan(attribute, comp)) {
                conditionMatches = compareForScan(attribute, comp) == 0;
              }
            } else
            if (cond.getComparisonOperator().equals("LE")) {
              if (condSize == 1 && isComparableInScan(attribute, comp)) {
                                conditionMatches = compareForScan(attribute, comp) <= 0;
                }
            } else
            if (cond.getComparisonOperator().equals("LT")) {
              if (condSize == 1 && isComparableInScan(attribute, comp)) {
                                conditionMatches = compareForScan(attribute, comp) < 0;
              }
            } else
            if (cond.getComparisonOperator().equals("GE")) {
              if (condSize == 1 && isComparableInScan(attribute, comp)) {
                                conditionMatches = compareForScan(attribute, comp) >= 0;
                            }
            } else
            if (cond.getComparisonOperator().equals("GT")) {
              if (condSize == 1 && isComparableInScan(attribute, comp)) {
                                conditionMatches = compareForScan(attribute, comp) > 0;
              }
            } else
                        if (cond.getComparisonOperator().equals("BETWEEN")) {
                            if (condSize == 2) {
                                final AttributeValue comp2 = cond.getAttributeValueList().get(1);
                                if (isComparableInScan(attribute, comp) && isComparableInScan(attribute, comp2)) {
                                    conditionMatches = compareForScan(attribute, comp) >= 0 && compareForScan(attribute, comp2) <= 0;
                                }
                            }
                        } else
                        if (cond.getComparisonOperator().equals("BEGINS_WITH")) {
                            //TODO: Should we fail on AttributeValueType other than S (as per AWS DynamoDB docs)?
                            //TODO: Investigate how actual DynamoDB works
                            if (condSize == 1 && getAttributeValueType(attribute).equals(AttributeValueType.S) &&
                                    getAttributeValueType(comp).equals(AttributeValueType.S)) {
                                conditionMatches = attribute.getS().startsWith(comp.getS());
                            }
                        } else
                        if (cond.getComparisonOperator().equals("CONTAINS")) {
                            if (condSize == 1) {
                if (getAttributeValueType(item.get(k)).equals(AttributeValueType.S) || getAttributeValueType(item.get(k)).equals(AttributeValueType.N)) {
                  String value = getAttributeValueAsString(attribute);
                  String subs = getAttributeValueAsString(comp);
                                    conditionMatches = value.contains(subs);
                                }
                            }
                            //TODO: Check for sets!!!
                        } else
            if (cond.getComparisonOperator().equals("IN")) {
              for(AttributeValue value : cond.getAttributeValueList()){
                if (item.get(k).equals(value)){
                                    conditionMatches = true;
                                    break;
                                }
              }
            }
          }
                    shouldAddItem = shouldAddItem && conditionMatches;
        }
                if (shouldAddItem) items.add(item);
      } else {
        items.add(item);
      }
          }
    }
    if ((request.getLimit() != null) && (items.size() > request.getLimit())) {
      items = items.subList(0, request.getLimit() - 1);
    }

    if (request.getAttributesToGet() != null) {
      List<Map<String, AttributeValue>> copy = getItemWithAttributesToGet(items, request.getAttributesToGet());
      items = copy;
    }

    result.setItems(items);
    result.setCount(items.size());
    result.setScannedCount(items.size());
    return result;
  }

  public com.amazonaws.services.dynamodbv2.model.ScanResult scanV2(com.amazonaws.services.dynamodbv2.model.ScanRequest v2Request) {
        Table table = this.tables.get(v2Request.getTableName());
        ScanRequest request = AlternatorDBApiVersion2Mapper.MapV2ScanRequestToV1(v2Request, table);
        ScanResult result = scan(request);
        return AlternatorDBApiVersion2Mapper.MapV1ScanResultToV2(result, table);
  }

  public synchronized QueryResult query(QueryRequest request) {
    // Validate data coming in
    QueryRequestValidator validator = new QueryRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw createInternalServerException(errors);
    }

    // Check existence of table
    Table table = this.tables.get(request.getTableName());
    if (table == null) {
      throw new ResourceNotFoundException("The table '" + request.getTableName() + "' doesn't exist.");
    }

        String hashKeyValue = getKeyValue(request.getHashKeyValue());
        List<String> attributesToGet = request.getAttributesToGet();

    QueryResult queryResult = new QueryResult();
    List<Map<String, AttributeValue>> list = new ArrayList<Map<String, AttributeValue>>();

        KeySchema keySchema = table.getKeySchema();
        KeySchemaElement rangeKeyElement = keySchema.getRangeKeyElement();
        ItemRangeGroup rangeGroup = table.getItemRangeGroup(hashKeyValue);
        if (rangeGroup != null) {
      for (Map<String, AttributeValue> item : rangeGroup.getItems(rangeKeyElement, request.getRangeKeyCondition())) {
        if (request.getScanIndexForward() == null || request.getScanIndexForward() == true) {
                    // The default value is true (forward).
                    // If ScanIndexForward is not specified, the results are returned in ascending order.
          list.add(getItemWithAttributesToGet(item, attributesToGet));
        } else {
          list.add(0, getItemWithAttributesToGet(item, attributesToGet));
        }
            }
        }

    if (request.getLimit() != null && request.getLimit() > 0) {
      while (list.size() > request.getLimit()) {
        list.remove((int) request.getLimit());
      }
    }


    queryResult.setItems(list);
    queryResult.setCount(list.size());
    queryResult.setConsumedCapacityUnits(0.5);

        // DynamoDBMapper implements paged query continuations if we return a LastEvaluatedKey value.
        // Leave this value null to indicate we are returning the full result set.
    queryResult.setLastEvaluatedKey(null)// new Key(request.getHashKeyValue()));

    return queryResult;
  }

  public com.amazonaws.services.dynamodbv2.model.QueryResult queryV2(com.amazonaws.services.dynamodbv2.model.QueryRequest v2Request) {
        Table table = this.tables.get(v2Request.getTableName());
        QueryRequest request = AlternatorDBApiVersion2Mapper.MapV2QueryRequestToV1(v2Request, table);
        QueryResult result = query(request);
        return AlternatorDBApiVersion2Mapper.MapV1QueryResultToV2(result, table);
  }

  public String getKeyValue(AttributeValue value) {
    if (value != null) {
      if (value.getN() != null) {
        return value.getN();
      } else if (value.getS() != null) {
        return value.getS();
      }
    }
    return null;
  }

  public AttributeValueType getAttributeValueType(AttributeValue value) {
    if (value != null) {
      if (value.getN() != null) {
        return AttributeValueType.N;
      } else if (value.getS() != null) {
        return AttributeValueType.S;
      } else if (value.getNS() != null) {
        return AttributeValueType.NS;
      } else if (value.getSS() != null) {
        return AttributeValueType.SS;
      }
    }
    return AttributeValueType.UNKNOWN;
  }

  public InternalServerErrorException createInternalServerException(List<Error> errors) {
    String message = "The following Errors occured: ";
    for (Error error : errors) {
      message += error.getMessage() + "\n";
    }
    return new InternalServerErrorException(message);
  }

  public synchronized UpdateItemResult updateItem(UpdateItemRequest request) {
    // Validate data coming in
    // TODO: Look into how we're doing validation, maybe implement better solution
    UpdateItemRequestValidator validator = new UpdateItemRequestValidator();
    List<Error> errors = validator.validate(request);
    if (errors.size() != 0) {
      throw new AmazonServiceException(errors.toString());
    }

    // get information
    String tableName = request.getTableName();
    Key key = request.getKey();
    Map<String, ExpectedAttributeValue> expected = request.getExpected();
    Map<String, AttributeValueUpdate> attributesToUpdate = request.getAttributeUpdates();
    String returnValues = request.getReturnValues();


    UpdateItemResult result = new UpdateItemResult();
    result.setConsumedCapacityUnits(0.5);

    // Check to make sure table exists
    if (!this.tables.containsKey(tableName)) {
      throw new ResourceNotFoundException("The table you're currently trying to access (" + tableName + ") doesn't exists.");
    }
    // Check to make sure Key is valid
        String hashKeyValue = getKeyValue(key.getHashKeyElement());
        String rangeKeyValue = getKeyValue(key.getRangeKeyElement());
        Map<String, AttributeValue> item = this.tables.get(tableName).getItem(hashKeyValue, rangeKeyValue);
      
    // Check conditional put
        validateExpected(request.getExpected(), item);
       
    if (item == null) {
      item = new HashMap<String, AttributeValue>();
      item.put(this.tables.get(tableName).getHashKeyName(), key.getHashKeyElement());
      if (key.getRangeKeyElement() != null) {
        item.put(this.tables.get(tableName).getRangeKeyName(), key.getRangeKeyElement());
      }
      for (String sKey : attributesToUpdate.keySet()) {
        if (attributesToUpdate.get(sKey).getValue() != null) {
          item.put(sKey, attributesToUpdate.get(sKey).getValue());
        }
      }
      this.tables.get(tableName).putItem(item);
      result.setAttributes(item);
    } else {
      Set<String> sKeyz = new HashSet<String>(item.keySet());
      sKeyz.addAll(attributesToUpdate.keySet());
      for (String sKey : sKeyz) {
        if (attributesToUpdate.containsKey(sKey)) {
          if (attributesToUpdate.get(sKey).getAction().equalsIgnoreCase(AttributeAction.PUT.name())) {
            item.remove(sKey);
            item.put(sKey, attributesToUpdate.get(sKey).getValue());
            attributesToUpdate.remove(sKey);
          } else if (attributesToUpdate.get(sKey).getAction().equalsIgnoreCase(AttributeAction.DELETE.name())) {
            if (attributesToUpdate.get(sKey).getValue() != null) {
              deleteAttributeValue(item, sKey, attributesToUpdate.get(sKey));
            } else {
              item.remove(sKey);
            }
            attributesToUpdate.remove(sKey);
          } else if (attributesToUpdate.get(sKey).getAction().equalsIgnoreCase(AttributeAction.ADD.name())) {
            if (attributesToUpdate.get(sKey).getValue() != null) {
              addAttributeValue(item, sKey, attributesToUpdate.get(sKey));
            } else {
              throw new ResourceNotFoundException("the provided update item with attribute (" + sKey + ") doesn't have an AttributeValue to perform the ADD");
            }
          }
        }
      }
      result.setAttributes(item);
    }
    return result;
  }

  public com.amazonaws.services.dynamodbv2.model.UpdateItemResult updateItemV2(com.amazonaws.services.dynamodbv2.model.UpdateItemRequest v2Request) {
        Table table = this.tables.get(v2Request.getTableName());
        UpdateItemRequest request = AlternatorDBApiVersion2Mapper.MapV2UpdateItemRequestToV1(v2Request, table);
    try {
      UpdateItemResult result = updateItem(request);
      return AlternatorDBApiVersion2Mapper.MapV1UpdateItemResultToV2(result, v2Request.getTableName());
    } catch (ConditionalCheckFailedException ccfev1) {
      throw new com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException(ccfev1.getMessage());
    }
  }

  public Map<String, AttributeValue> getItemWithAttributesToGet(Map<String, AttributeValue> item, List<String> attributesToGet) {
    if (item == null) {
      return item;
    }
    if (attributesToGet == null) {
      return item;
    }
    Set<String> attributes = new HashSet<String>(item.keySet());
    for (String attribute : attributes) {
      if (!attributesToGet.contains(attribute)) {
        item.remove(attribute);
      }
    }
    return item;
  }

  public List<Map<String, AttributeValue>> getItemWithAttributesToGet(List<Map<String, AttributeValue>> items, List<String> attributesToGet) {
    List<Map<String, AttributeValue>> copy = new ArrayList<Map<String, AttributeValue>>();
    for (Map<String, AttributeValue> item : items) {
      copy.add(getItemWithAttributesToGet(item, attributesToGet));
    }
    return copy;
  }

  private void addAttributeValue(Map<String, AttributeValue> item, String attributename, AttributeValueUpdate valueUpdate){

    AttributeValue value = item.get(attributename);
    if (value==null){
      //new field
      value = valueUpdate.getValue();
      item.put(attributename, value);
    }else{
      if (value.getSS() != null) {
        if (valueUpdate.getValue().getSS() == null) {
          throw new ConditionalCheckFailedException("It's not possible to ADD something else than a List<String> for the attribute (" + attributename + ")");
        } else {
          for (String toUp : valueUpdate.getValue().getSS()) {
            value.getSS().add(toUp);
          }
        }
      } else if (value.getNS() != null) {
        if (valueUpdate.getValue().getNS() == null) {
          throw new ConditionalCheckFailedException("It's not possible to ADD something else than a List<Number> for the attribute (" + attributename + ")");
        } else {
          for (String toUp : valueUpdate.getValue().getNS()) {
            value.getNS().add(toUp);
          }
        }
      } else if (value.getS() != null) {
        throw new ConditionalCheckFailedException("It's not possible to ADD on an attribute with a String type for the attribute (" + attributename + ")");
      } else if (value.getN() != null) {
                try {
                    Long l = Long.valueOf(value.getN());
                    l = l + Long.valueOf(valueUpdate.getValue().getN());
                    value.setN(l + "");
                } catch (NumberFormatException e) {
                    Double i = new Double(value.getN());
                    i = i + new Double(valueUpdate.getValue().getN());
                    value.setN(i + "");
                }
      }
    }
  }

  private void deleteAttributeValue(Map<String, AttributeValue> item, String attributename, AttributeValueUpdate valueToDelete){
    AttributeValue existingValue = item.get(attributename);
    if (existingValue == null){
      return; //do nothing. need to confirm with live dynamodb behaviour.
    }
    if (existingValue.getSS() != null) {
      if (valueToDelete.getValue().getSS() == null) {
        throw new ConditionalCheckFailedException("It's not possible to delete something else than a List<String> for the attribute (" + attributename + ") of the item with hash key (" + existingValue + ")");
      } else {
        for (String toDel : valueToDelete.getValue().getSS()) {
          if (existingValue.getSS().contains(toDel)) {
            existingValue.getSS().remove(toDel);
          }
        }
      }
    } else if (existingValue.getNS() != null) {
      if (valueToDelete.getValue().getNS() == null) {
        throw new ConditionalCheckFailedException("It's not possible to delete something else than a List<Number> for the attribute (" + attributename + ") of the item with hash key (" + existingValue + ")");
      } else {
        for (String toDel : valueToDelete.getValue().getNS()) {
          if (existingValue.getNS().contains(toDel)) {
            existingValue.getNS().remove(toDel);
          }
        }
      }
    } else if (existingValue.getS()!=null && existingValue.getS().equals(valueToDelete.getValue().getS())) {
      item.remove(attributename);
    } else if (existingValue.getN()!=null && existingValue.getN().equals(valueToDelete.getValue().getN())) {
      item.remove(attributename);
    }
  }
 
  private void validateExpected(Map<String, ExpectedAttributeValue> expected, Map<String, AttributeValue> item) {
    if (expected != null) {
      for (Map.Entry<String, ExpectedAttributeValue> entry : expected.entrySet()) {
        String key = entry.getKey();
        ExpectedAttributeValue value = entry.getValue();
        value.setExists(value.getValue() != null);
        if ((value.getExists() && item == null) || (!value.getExists() && item != null)) {
          throw new ConditionalCheckFailedException("The exist conditional could not be met.");
        }
        if (value.getValue() != null) {
          // check to see if value conditional is equal
          if (!compareAttributeValues(value.getValue(), item.get(key))) {
            throw new ConditionalCheckFailedException("The value conditional could is not equal");
          }
        }
      }
    }
  }
}
TOP

Related Classes of com.michelboudreau.alternator.AlternatorDBHandler

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.