Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
The Amazon Web Services SDK for Java provides Java APIs for building software on AWS' cost-effective, scalable, and reliable infrastructure products. The AWS Java SDK allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon AutoScaling, etc).
/*
* Copyright 2011-2014 Amazon Technologies, Inc.
*
* 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://aws.amazon.com/apache2.0
*
* This file 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 com.amazonaws.services.dynamodbv2.datamodeling;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.retry.RetryUtils;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.ConsistentReads;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.PaginationLoadingStrategy;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.SaveBehavior;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTableSchemaParser.TableIndexesInfo;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate;
import com.amazonaws.services.dynamodbv2.model.BatchGetItemRequest;
import com.amazonaws.services.dynamodbv2.model.BatchGetItemResult;
import com.amazonaws.services.dynamodbv2.model.BatchWriteItemRequest;
import com.amazonaws.services.dynamodbv2.model.BatchWriteItemResult;
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
import com.amazonaws.services.dynamodbv2.model.Condition;
import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest;
import com.amazonaws.services.dynamodbv2.model.DeleteRequest;
import com.amazonaws.services.dynamodbv2.model.ExpectedAttributeValue;
import com.amazonaws.services.dynamodbv2.model.GetItemRequest;
import com.amazonaws.services.dynamodbv2.model.GetItemResult;
import com.amazonaws.services.dynamodbv2.model.KeysAndAttributes;
import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
import com.amazonaws.services.dynamodbv2.model.PutRequest;
import com.amazonaws.services.dynamodbv2.model.QueryRequest;
import com.amazonaws.services.dynamodbv2.model.QueryResult;
import com.amazonaws.services.dynamodbv2.model.ScanRequest;
import com.amazonaws.services.dynamodbv2.model.ScanResult;
import com.amazonaws.services.dynamodbv2.model.Select;
import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest;
import com.amazonaws.services.dynamodbv2.model.WriteRequest;
import com.amazonaws.services.s3.model.Region;
import com.amazonaws.util.VersionInfoUtils;
/**
* Object mapper for domain-object interaction with DynamoDB.
*
* To use, define a domain class that represents an item in a DynamoDB table and
* annotate it with the annotations found in the
* com.amazonaws.services.dynamodbv2.datamodeling package. In order to allow the
* mapper to correctly persist the data, each modeled property in the domain
* class should be accessible via getter and setter methods, and each property
* annotation should be either applied to the getter method or the class field.
* A minimal example using getter annotations:
*
*
* @DynamoDBTable(tableName = "TestTable")
* public class TestClass {
*
* private Long key;
* private double rangeKey;
* private Long version;
*
* private Set<Integer> integerSetAttribute;
*
* @DynamoDBHashKey
* public Long getKey() {
* return key;
* }
*
* public void setKey(Long key) {
* this.key = key;
* }
*
* @DynamoDBRangeKey
* public double getRangeKey() {
* return rangeKey;
* }
*
* public void setRangeKey(double rangeKey) {
* this.rangeKey = rangeKey;
* }
*
* @DynamoDBAttribute(attributeName = "integerSetAttribute")
* public Set<Integer> getIntegerAttribute() {
* return integerSetAttribute;
* }
*
* public void setIntegerAttribute(Set<Integer> integerAttribute) {
* this.integerSetAttribute = integerAttribute;
* }
*
* @DynamoDBVersionAttribute
* public Long getVersion() {
* return version;
* }
*
* public void setVersion(Long version) {
* this.version = version;
* }
* }
*
*
* Save instances of annotated classes to DynamoDB, retrieve them, and delete
* them using the {@link DynamoDBMapper} class, as in the following example.
*
*
* When using the save, load, and delete methods, {@link DynamoDBMapper} will
* throw {@link DynamoDBMappingException}s to indicate that domain classes are
* incorrectly annotated or otherwise incompatible with this class. Service
* exceptions will always be propagated as {@link AmazonClientException}, and
* DynamoDB-specific subclasses such as {@link ConditionalCheckFailedException}
* will be used when possible.
*
* This class is thread-safe and can be shared between threads. It's also very
* lightweight, so it doesn't need to be.
*
* @see DynamoDBTable
* @see DynamoDBHashKey
* @see DynamoDBRangeKey
* @see DynamoDBAutoGeneratedKey
* @see DynamoDBAttribute
* @see DynamoDBVersionAttribute
* @see DynamoDBIgnore
* @see DynamoDBMarshalling
* @see DynamoDBMapperConfig
*/
public class DynamoDBMapper {
private final S3ClientCache s3cc;
private final AmazonDynamoDB db;
private final DynamoDBMapperConfig config;
private final DynamoDBReflector reflector = new DynamoDBReflector();
private final DynamoDBTableSchemaParser schemaParser = new DynamoDBTableSchemaParser();
private final AttributeTransformer transformer;
/** The max back off time for batch write */
static final long MAX_BACKOFF_IN_MILLISECONDS = 1000 * 3;
/**
* This retry count is applicable only when every batch get item request
* results in no data retrieved from server and the un processed keys is
* same as request items
*/
static final int BATCH_GET_MAX_RETRY_COUNT_ALL_KEYS = 5;
/**
* User agent for requests made using the {@link DynamoDBMapper}.
*/
private static final String USER_AGENT = DynamoDBMapper.class.getName() + "/" + VersionInfoUtils.getVersion();
private static final String NO_RANGE_KEY = new String();
private static final Log log = LogFactory.getLog(DynamoDBMapper.class);
/**
* Constructs a new mapper with the service object given, using the default
* configuration.
*
* @param dynamoDB
* The service object to use for all service calls.
* @see DynamoDBMapperConfig#DEFAULT
*/
public DynamoDBMapper(final AmazonDynamoDB dynamoDB) {
this(dynamoDB, DynamoDBMapperConfig.DEFAULT, null, null);
}
/**
* Constructs a new mapper with the service object and configuration given.
*
* @param dynamoDB
* The service object to use for all service calls.
* @param config
* The default configuration to use for all service calls. It can
* be overridden on a per-operation basis.
*/
public DynamoDBMapper(
final AmazonDynamoDB dynamoDB,
final DynamoDBMapperConfig config) {
this(dynamoDB, config, null, null);
}
/**
* Constructs a new mapper with the service object and S3 client cache
* given, using the default configuration.
*
* @param ddb
* The service object to use for all service calls.
* @param s3CredentialProvider
* The credentials provider for accessing S3.
* Relevant only if {@link S3Link} is involved.
* @see DynamoDBMapperConfig#DEFAULT
*/
public DynamoDBMapper(
final AmazonDynamoDB ddb,
final AWSCredentialsProvider s3CredentialProvider) {
this(ddb, DynamoDBMapperConfig.DEFAULT, s3CredentialProvider);
}
/**
* Constructs a new mapper with the given service object, configuration,
* and transform hook.
*
* @param dynamoDB
* the service object to use for all service calls
* @param config
* the default configuration to use for all service calls. It
* can be overridden on a per-operation basis
* @param transformer
* The custom attribute transformer to invoke when serializing or
* deserializing an object.
*/
public DynamoDBMapper(
final AmazonDynamoDB dynamoDB,
final DynamoDBMapperConfig config,
final AttributeTransformer transformer) {
this(dynamoDB, config, transformer, null);
}
/**
* Constructs a new mapper with the service object, configuration, and S3
* client cache given.
*
* @param dynamoDB
* The service object to use for all service calls.
* @param config
* The default configuration to use for all service calls. It can
* be overridden on a per-operation basis.
* @param s3CredentialProvider
* The credentials provider for accessing S3.
* Relevant only if {@link S3Link} is involved.
*/
public DynamoDBMapper(
final AmazonDynamoDB dynamoDB,
final DynamoDBMapperConfig config,
final AWSCredentialsProvider s3CredentialProvider) {
this(dynamoDB, config, null, validate(s3CredentialProvider));
}
/**
* Throws an exception if the given credentials provider is {@code null}.
*/
private static AWSCredentialsProvider validate(
final AWSCredentialsProvider provider) {
if (provider == null) {
throw new IllegalArgumentException(
"s3 credentials provider must not be null");
}
return provider;
}
/**
* Constructor with all parameters.
*
* @param dynamoDB
* The service object to use for all service calls.
* @param config
* The default configuration to use for all service calls. It can
* be overridden on a per-operation basis.
* @param transformer
* The custom attribute transformer to invoke when serializing or
* deserializing an object.
* @param s3CredentialProvider
* The credentials provider for accessing S3.
* Relevant only if {@link S3Link} is involved.
*/
public DynamoDBMapper(
final AmazonDynamoDB dynamoDB,
final DynamoDBMapperConfig config,
final AttributeTransformer transformer,
final AWSCredentialsProvider s3CredentialsProvider) {
this.db = dynamoDB;
this.config = config;
this.transformer = transformer;
if (s3CredentialsProvider == null) {
this.s3cc = null;
} else {
this.s3cc = new S3ClientCache(s3CredentialsProvider.getCredentials());
}
}
/**
* Loads an object with the hash key given and a configuration override.
* This configuration overrides the default provided at object construction.
*
* @see DynamoDBMapper#load(Class, Object, Object, DynamoDBMapperConfig)
*/
public T load(Class clazz, Object hashKey, DynamoDBMapperConfig config) {
return load(clazz, hashKey, null, config);
}
/**
* Loads an object with the hash key given, using the default configuration.
*
* @see DynamoDBMapper#load(Class, Object, Object, DynamoDBMapperConfig)
*/
public T load(Class clazz, Object hashKey) {
return load(clazz, hashKey, null, config);
}
/**
* Loads an object with a hash and range key, using the default
* configuration.
*
* @see DynamoDBMapper#load(Class, Object, Object, DynamoDBMapperConfig)
*/
public T load(Class clazz, Object hashKey, Object rangeKey) {
return load(clazz, hashKey, rangeKey, config);
}
/**
* Returns an object whose keys match those of the prototype key object given,
* or null if no such item exists.
*
* @param keyObject
* An object of the class to load with the keys values to match.
*
* @see DynamoDBMapper#load(Object, DynamoDBMapperConfig)
*/
public T load(T keyObject) {
return load(keyObject, this.config);
}
/**
* Returns an object whose keys match those of the prototype key object given,
* or null if no such item exists.
*
* @param keyObject
* An object of the class to load with the keys values to match.
* @param config
* Configuration for the service call to retrieve the object from
* DynamoDB. This configuration overrides the default given at
* construction.
*/
public T load(T keyObject, DynamoDBMapperConfig config) {
@SuppressWarnings("unchecked")
Class clazz = (Class) keyObject.getClass();
config = mergeConfig(config);
String tableName = getTableName(clazz, config);
GetItemRequest rq = new GetItemRequest()
.withRequestMetricCollector(config.getRequestMetricCollector());
Map key = getKey(keyObject, clazz);
rq.setKey(key);
rq.setTableName(tableName);
rq.setConsistentRead(config.getConsistentReads() == ConsistentReads.CONSISTENT);
GetItemResult item = db.getItem(applyUserAgent(rq));
Map itemAttributes = item.getItem();
if ( itemAttributes == null ) {
return null;
}
T object = marshalIntoObject(toParameters(itemAttributes, clazz, config));
return object;
}
/**
* Returns a key map for the key object given.
*
* @param keyObject
* The key object, corresponding to an item in a dynamo table.
*/
@SuppressWarnings("unchecked")
private Map getKey(T keyObject) {
return getKey(keyObject, (Class)keyObject.getClass());
}
private Map getKey(T keyObject, Class clazz) {
Map key = new HashMap();
for (Method keyGetter : reflector.getPrimaryKeyGetters(clazz)) {
Object getterResult = safeInvoke(keyGetter, keyObject);
AttributeValue keyAttributeValue = getSimpleAttributeValue(keyGetter, getterResult);
if (keyAttributeValue == null) {
throw new DynamoDBMappingException("Null key found for " + keyGetter);
}
key.put(reflector.getAttributeName(keyGetter), keyAttributeValue);
}
if ( key.isEmpty() ) {
throw new DynamoDBMappingException("Class must be annotated with " + DynamoDBHashKey.class + " and "
+ DynamoDBRangeKey.class);
}
return key;
}
/**
* Returns an object with the given hash key, or null if no such object
* exists.
*
* @param clazz
* The class to load, corresponding to a DynamoDB table.
* @param hashKey
* The key of the object.
* @param rangeKey
* The range key of the object, or null for tables without a
* range key.
* @param config
* Configuration for the service call to retrieve the object from
* DynamoDB. This configuration overrides the default given at
* construction.
*/
public T load(Class clazz, Object hashKey, Object rangeKey, DynamoDBMapperConfig config) {
config = mergeConfig(config);
T keyObject = createKeyObject(clazz, hashKey, rangeKey);
return load(keyObject, config);
}
/**
* Creates a key prototype object for the class given with the single hash and range key given.
*/
private T createKeyObject(Class clazz, Object hashKey, Object rangeKey) {
T keyObject = null;
try {
keyObject = clazz.newInstance();
} catch ( Exception e ) {
throw new DynamoDBMappingException("Failed to instantiate class", e);
}
boolean seenHashKey = false;
boolean seenRangeKey = false;
for ( Method getter : reflector.getPrimaryKeyGetters(clazz) ) {
if ( ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBHashKey.class) ) {
if ( seenHashKey ) {
throw new DynamoDBMappingException("Found more than one method annotated with "
+ DynamoDBHashKey.class + " for class " + clazz
+ ". Use load(Object) for tables with more than a single hash and range key.");
}
seenHashKey = true;
safeInvoke(reflector.getSetter(getter), keyObject, hashKey);
} else if ( ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBRangeKey.class) ) {
if ( seenRangeKey ) {
throw new DynamoDBMappingException("Found more than one method annotated with "
+ DynamoDBRangeKey.class + " for class " + clazz
+ ". Use load(Object) for tables with more than a single hash and range key.");
}
seenRangeKey = true;
safeInvoke(reflector.getSetter(getter), keyObject, rangeKey);
}
}
if ( !seenHashKey ) {
throw new DynamoDBMappingException("No method annotated with " + DynamoDBHashKey.class + " for class "
+ clazz + ".");
} else if ( rangeKey != null && !seenRangeKey ) {
throw new DynamoDBMappingException("No method annotated with " + DynamoDBRangeKey.class + " for class "
+ clazz + ".");
}
return keyObject;
}
/**
* Returns a map of attribute name to EQ condition for the key prototype
* object given. This method considers attributes annotated with either
* {@link DynamoDBHashKey} or {@link DynamoDBIndexHashKey}.
*
* @param obj
* The prototype object that includes the hash key value.
* @return A map of hash key attribute name to EQ condition for the key
* prototype object, or an empty map if obj is null.
*/
private Map getHashKeyEqualsConditions(Object obj) {
Map conditions = new HashMap();
if (obj != null) {
for ( Method getter : reflector.getRelevantGetters(obj.getClass()) ) {
if ( ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBHashKey.class)
|| ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBIndexHashKey.class) ) {
Object getterReturnResult = safeInvoke(getter, obj, (Object[])null);
if (getterReturnResult != null) {
conditions.put(
reflector.getAttributeName(getter),
new Condition().withComparisonOperator(ComparisonOperator.EQ).withAttributeValueList(
getSimpleAttributeValue(getter, getterReturnResult)));
}
}
}
}
return conditions;
}
/**
* Returns the table name for the class given.
*/
protected final String getTableName(final Class> clazz,
final DynamoDBMapperConfig config) {
return getTableName(clazz, config, reflector);
}
static String getTableName(final Class> clazz,
final DynamoDBMapperConfig config,
final DynamoDBReflector reflector) {
DynamoDBTable table = reflector.getTable(clazz);
String tableName = table.tableName();
if ( config.getTableNameOverride() != null ) {
if ( config.getTableNameOverride().getTableName() != null ) {
tableName = config.getTableNameOverride().getTableName();
} else {
tableName = config.getTableNameOverride().getTableNamePrefix()
+ tableName;
}
}
return tableName;
}
/**
* A replacement for {@link #marshallIntoObject(Class, Map)} that takes
* extra parameters to tunnel through to {@code privateMarshalIntoObject}.
*
* Once {@code marshallIntoObject} is removed, this method will directly
* call {@code privateMarshalIntoObject}.
*/
private T marshalIntoObject(
final AttributeTransformer.Parameters parameters
) {
return marshallIntoObject(
parameters.getModelClass(),
MapAnd.wrap(parameters.getAttributeValues(), parameters));
}
/**
* Creates and fills in the attributes on an instance of the class given
* with the attributes given.
*
* This is accomplished by looking for getter methods annotated with an
* appropriate annotation, then looking for matching attribute names in the
* item attribute map.
*
* This method has been marked deprecated because it does not allow
* load/query/scan to pass through their DynamoDBMapperConfig parameter,
* which is needed by some implementations of {@code AttributeTransformer}.
* In a future version of the SDK, load/query/scan will be changed to
* directly call privateMarshalIntoObject, and will no longer call this
* method.
*
* If you are extending DynamoDBMapper and overriding this method to
* customize how the mapper unmarshals POJOs from a raw DynamoDB item,
* please switch to using an AttributeTransformer (or open a GitHub
* issue if you need to fully control the unmarshalling process, and we'll
* figure out a better way to expose such a hook).
*
* If you're simply calling this method, it will continue to be available
* for the forseeable future - feel free to ignore the @Deprecated tag.
*
* @param clazz
* The class to instantiate and hydrate
* @param itemAttributes
* The set of item attributes, keyed by attribute name.
* @deprecated as an extension point for adding custom unmarshalling
*/
@Deprecated
public T marshallIntoObject(Class clazz, Map itemAttributes) {
if (itemAttributes instanceof MapAnd) {
@SuppressWarnings("unchecked")
AttributeTransformer.Parameters parameters =
((MapAnd, ?, AttributeTransformer.Parameters>) itemAttributes)
.getExtra();
return privateMarshalIntoObject(parameters);
} else {
// Called via some unexpected external codepath; use the class-level
// config.
return privateMarshalIntoObject(
toParameters(itemAttributes, clazz, this.config));
}
}
/**
* The one true implementation of marshalIntoObject.
*/
private T privateMarshalIntoObject(
final AttributeTransformer.Parameters parameters) {
T toReturn = null;
try {
toReturn = parameters.getModelClass().newInstance();
} catch ( InstantiationException e ) {
throw new DynamoDBMappingException("Failed to instantiate new instance of class", e);
} catch ( IllegalAccessException e ) {
throw new DynamoDBMappingException("Failed to instantiate new instance of class", e);
}
if ( parameters.getAttributeValues() == null
|| parameters.getAttributeValues().isEmpty() ) {
return toReturn;
}
Map result = untransformAttributes(parameters);
for ( Method m : reflector.getRelevantGetters(parameters.getModelClass()) ) {
String attributeName = reflector.getAttributeName(m);
if ( result.containsKey(attributeName) ) {
setValue(toReturn, m, result.get(attributeName));
}
}
return toReturn;
}
/**
* Unmarshalls the list of item attributes into objects of type clazz.
*
* This method has been marked deprecated because it does not allow
* query/scan to pass through their DynamoDBMapperConfig parameter,
* which is needed by some implementations of {@code AttributeTransformer}.
* In a future version of the SDK, query/scan will be changed to directly
* call privateMarshalIntoObjects, and will no longer call this method.
*
* If you are extending DynamoDBMapper and overriding this method to
* customize how the mapper unmarshals POJOs from raw DynamoDB items,
* please switch to using an AttributeTransformer (or open a GitHub
* issue if you need to fully control the unmarshalling process, and we'll
* figure out a better way to expose such a hook).
*
* If you're simply calling this method, it will continue to be available
* for the forseeable future - feel free to ignore the @Deprecated tag.
*
* @see DynamoDBMapper#marshallIntoObject(Class, Map)
* @deprecated as an extension point for adding custom unmarshalling
*/
@Deprecated
public List marshallIntoObjects(Class clazz, List