All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBReflector Maven / Gradle / Ivy

Go to download

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).

The newest version!
/*
 * 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.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.apache.http.annotation.GuardedBy;

import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.BinaryAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.BinarySetAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.NumberAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.NumberSetAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.StringAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.util.DateUtils;

/**
 * Reflection assistant for {@link DynamoDBMapper}
 */
public class DynamoDBReflector {

    /*
     * Several caches for performance. Collectively, they can make this class
     * over twice as fast.
     */
    private final Map, Collection> getterCache = new HashMap, Collection>();
    private final Map, Method> primaryHashKeyGetterCache = new HashMap, Method>();
    private final Map, Method> primaryRangeKeyGetterCache = new HashMap, Method>();

    /*
     * All caches keyed by a Method use the getter for a particular mapped
     * property
     */
    private final Map setterCache = new HashMap();

    @GuardedBy("readWriteLockAttrName")
    private final Map attributeNameCache = new HashMap();

    private final Map argumentUnmarshallerCache = new HashMap();
    private final Map argumentMarshallerCache = new HashMap();
    private final Map versionArgumentMarshallerCache = new HashMap();
    private final Map keyArgumentMarshallerCache = new HashMap();
    private final Map versionAttributeGetterCache = new HashMap();
    private final Map autoGeneratedKeyGetterCache = new HashMap();

    private final ReentrantReadWriteLock readWriteLockAttrName = new ReentrantReadWriteLock();
    private final ReadLock readLockAttrName = readWriteLockAttrName.readLock();
    private final WriteLock writeLockAttrName = readWriteLockAttrName.writeLock();

    /**
     * Returns the set of getter methods which are relevant when marshalling or
     * unmarshalling an object.
     */
    Collection getRelevantGetters(Class clazz) {
        synchronized (getterCache) {
            if ( !getterCache.containsKey(clazz) ) {
                List relevantGetters = new LinkedList();
                for ( Method m : clazz.getMethods() ) {
                    if ( isRelevantGetter(m) ) {
                        relevantGetters.add(m);
                    }
                }
                getterCache.put(clazz, relevantGetters);
            }
            return getterCache.get(clazz);
        }
    }

    /**
     * Returns whether the method given is a getter method we should serialize /
     * deserialize to the service. The method must begin with "get" or "is",
     * have no arguments, belong to a class that declares its table, and not be
     * marked ignored.
     */
    private boolean isRelevantGetter(Method m) {
        return (m.getName().startsWith("get") || m.getName().startsWith("is"))
                && m.getParameterTypes().length == 0
                && m.getDeclaringClass().getAnnotation(DynamoDBTable.class) != null
                && !ReflectionUtils.getterOrFieldHasAnnotation(m, DynamoDBIgnore.class);
    }

    /**
     * Returns the annotated {@link DynamoDBRangeKey} getter for the class
     * given, or null if the class doesn't have one.
     */
     Method getPrimaryRangeKeyGetter(Class clazz) {
        synchronized (primaryRangeKeyGetterCache) {
            if ( !primaryRangeKeyGetterCache.containsKey(clazz) ) {
                Method rangeKeyMethod = null;
                for ( Method method : getRelevantGetters(clazz) ) {
                    if ( method.getParameterTypes().length == 0
                            && ReflectionUtils.getterOrFieldHasAnnotation(method, DynamoDBRangeKey.class)) {
                        rangeKeyMethod = method;
                        break;
                    }
                }
                primaryRangeKeyGetterCache.put(clazz, rangeKeyMethod);
            }
            return primaryRangeKeyGetterCache.get(clazz);
        }
    }

    /**
     * Returns all annotated {@link DynamoDBHashKey} and
     * {@link DynamoDBRangeKey} getters for the class given, throwing an
     * exception if there isn't one.
     *
     * TODO: caching
     */
     Collection getPrimaryKeyGetters(Class clazz) {
        List keyGetters = new LinkedList();
        for (Method getter : getRelevantGetters(clazz)) {
            if (ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBHashKey.class)
                    || ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBRangeKey.class)) {
                keyGetters.add(getter);
            }
        }

        return keyGetters;
    }


    /**
     * Returns the annotated {@link DynamoDBHashKey} getter for the class given,
     * throwing an exception if there isn't one.
     */
     Method getPrimaryHashKeyGetter(Class clazz) {
        Method hashKeyMethod;
        synchronized (primaryHashKeyGetterCache) {
            if ( !primaryHashKeyGetterCache.containsKey(clazz) ) {
                for ( Method method : getRelevantGetters(clazz) ) {
                    if ( method.getParameterTypes().length == 0
                            && ReflectionUtils.getterOrFieldHasAnnotation(method, DynamoDBHashKey.class)) {
                        primaryHashKeyGetterCache.put(clazz, method);
                        break;
                    }
                }
            }
            hashKeyMethod = primaryHashKeyGetterCache.get(clazz);
        }

        if ( hashKeyMethod == null ) {
            throw new DynamoDBMappingException("Public, zero-parameter hash key property must be annotated with "
                    + DynamoDBHashKey.class);
        }
        return hashKeyMethod;
    }

    /**
     * Returns the {@link DynamoDBTable} annotation of the class given, throwing
     * a runtime exception if it isn't annotated.
     */
     DynamoDBTable getTable(Class clazz) {
        DynamoDBTable table = clazz.getAnnotation(DynamoDBTable.class);
        if ( table == null )
            throw new DynamoDBMappingException("Class " + clazz + " must be annotated with " + DynamoDBTable.class);
        return table;
    }

    /**
     * Returns whether or not this getter has a custom marshaller
     */
    private boolean isCustomMarshaller(Method getter) {
        return ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBMarshalling.class);
    }

    /**
     * Returns the argument unmarshaller used to unmarshall the getter / setter
     * pair given.
     * 

* Determining how to unmarshall a response, especially a numeric one, * requires checking it against all supported types. This is expensive, so * we cache a lookup table of getter method to argument unmarhsaller which * can be reused. * * @param toReturn * The typed domain object being unmarshalled for the client * @param getter * The getter method being considered * @param setter * The corresponding setter method being considered */ ArgumentUnmarshaller getArgumentUnmarshaller(final T toReturn, final Method getter, final Method setter, S3ClientCache s3cc) { synchronized (argumentUnmarshallerCache) { ArgumentUnmarshaller unmarshaller = argumentUnmarshallerCache.get(getter); if ( unmarshaller != null ) { return unmarshaller; } Class[] parameterTypes = setter.getParameterTypes(); Class paramType = parameterTypes[0]; if ( parameterTypes.length != 1 ) { throw new DynamoDBMappingException("Expected exactly one agument to " + setter); } if ( isCustomMarshaller(getter) ) { unmarshaller = new SUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return getCustomMarshalledValue(toReturn, getter, value); } }; } else { unmarshaller = computeArgumentUnmarshaller(toReturn, getter, setter, paramType, s3cc); } argumentUnmarshallerCache.put(getter, unmarshaller); return unmarshaller; } } /** * Note this method is synchronized on {@link #argumentUnmarshallerCache} while being executed. */ private ArgumentUnmarshaller computeArgumentUnmarshaller( final T toReturn, final Method getter, final Method setter, Class paramType, S3ClientCache s3cc) { ArgumentUnmarshaller unmarshaller = null; // If we're dealing with a collection, we need to get the // underlying type out of it final boolean isCollection = Set.class.isAssignableFrom(paramType); if ( isCollection ) { Type genericType = setter.getGenericParameterTypes()[0]; if ( genericType instanceof ParameterizedType ) { if (((ParameterizedType) genericType).getActualTypeArguments()[0].toString().equals("byte[]")) { paramType = byte[].class; } else { paramType = (Class) ((ParameterizedType) genericType).getActualTypeArguments()[0]; } } } else if ( Collection.class.isAssignableFrom(paramType) ) { throw new DynamoDBMappingException("Only java.util.Set collection types are permitted for " + DynamoDBAttribute.class); } if ( double.class.isAssignableFrom(paramType) || Double.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(Double.parseDouble(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return Double.parseDouble(value.getN()); } }; } } else if ( BigDecimal.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(new BigDecimal(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return new BigDecimal(value.getN()); } }; } } else if ( BigInteger.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { ((Set) argument).add(new BigInteger(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return new BigInteger(value.getN()); } }; } } else if ( int.class.isAssignableFrom(paramType) || Integer.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(Integer.parseInt(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return Integer.parseInt(value.getN()); } }; } } else if ( float.class.isAssignableFrom(paramType) || Float.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(Float.parseFloat(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return Float.parseFloat(value.getN()); } }; } } else if ( byte.class.isAssignableFrom(paramType) || Byte.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(Byte.parseByte(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return Byte.parseByte(value.getN()); } }; } } else if ( long.class.isAssignableFrom(paramType) || Long.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(Long.parseLong(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return Long.parseLong(value.getN()); } }; } } else if ( short.class.isAssignableFrom(paramType) || Short.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(Short.parseShort(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return Short.parseShort(value.getN()); } }; } } else if ( boolean.class.isAssignableFrom(paramType) || Boolean.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new NSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getNS() ) { argument.add(parseBoolean(s)); } return argument; } }; } else { unmarshaller = new NUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return parseBoolean(value.getN()); } }; } } else if ( Date.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new SSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { Set argument = new HashSet(); for ( String s : value.getSS() ) { argument.add(new DateUtils().parseIso8601Date(s)); } return argument; } }; } else { unmarshaller = new SUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { return new DateUtils().parseIso8601Date(value.getS()); } }; } } else if ( Calendar.class.isAssignableFrom(paramType) ) { if ( isCollection ) { unmarshaller = new SSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { Set argument = new HashSet(); for ( String s : value.getSS() ) { Calendar cal = GregorianCalendar.getInstance(); cal.setTime(new DateUtils().parseIso8601Date(s)); argument.add(cal); } return argument; } }; } else { unmarshaller = new SUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { Calendar cal = GregorianCalendar.getInstance(); cal.setTime(new DateUtils().parseIso8601Date(value.getS())); return cal; } }; } } else if (ByteBuffer.class.isAssignableFrom(paramType)) { if ( isCollection ) { unmarshaller = new BSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { Set argument = new HashSet(); for (ByteBuffer b : value.getBS()) { argument.add(b); } return argument; } }; } else { unmarshaller = new BUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { return value.getB(); } }; } } else if (byte[].class.isAssignableFrom(paramType)) { if ( isCollection ) { unmarshaller = new BSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { Set argument = new HashSet(); for (ByteBuffer b : value.getBS()) { byte[] bytes = null; if (b.hasArray()) { bytes = b.array(); } else { bytes = new byte[b.limit()]; b.get(bytes, 0, bytes.length); } argument.add(bytes); } return argument; } }; } else { unmarshaller = new BUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { ByteBuffer byteBuffer = value.getB(); byte[] bytes = null; if (byteBuffer.hasArray()) { bytes = byteBuffer.array(); } else { bytes = new byte[byteBuffer.limit()]; byteBuffer.get(bytes, 0, bytes.length); } return bytes; } }; } } else { unmarshaller = defaultArgumentUnmarshaller(paramType, isCollection, s3cc); } return unmarshaller; } /** * Note this method is synchronized on {@link #argumentUnmarshallerCache} while being executed. * @param paramType the parameter type or the element type if the parameter is a collection * @param isCollection true if the parameter is a collection; false otherwise. * @return the default unmarshaller */ private ArgumentUnmarshaller defaultArgumentUnmarshaller (Class paramType, boolean isCollection, final S3ClientCache s3cc) { if (S3Link.class.isAssignableFrom(paramType)) { if ( isCollection ) { throw new DynamoDBMappingException("Collection types are not permitted for " + S3Link.class); } else { return new SUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { if ( s3cc == null ) { throw new IllegalStateException("Mapper must be constructed with S3 AWS Credentials to load S3Link"); } // value should never be null String json = value.getS(); return S3Link.fromJson(s3cc, json); } }; } } else { if ( !String.class.isAssignableFrom(paramType) ) { throw new DynamoDBMappingException("Expected a String, but was " + paramType); } else { if ( isCollection ) { return new SSUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { Set argument = new HashSet(); for ( String s : value.getSS() ) { argument.add(s); } return argument; } }; } else { return new SUnmarshaller() { @Override public Object unmarshall(AttributeValue value) { return value.getS(); } }; } } } } /** * Marshalls the custom value given into the proper return type. */ @SuppressWarnings({ "rawtypes", "unchecked" }) private T getCustomMarshalledValue(T toReturn, Method getter, AttributeValue value) { DynamoDBMarshalling annotation = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBMarshalling.class); Class> marshallerClass = annotation.marshallerClass(); DynamoDBMarshaller marshaller; try { marshaller = marshallerClass.newInstance(); } catch ( InstantiationException e ) { throw new DynamoDBMappingException("Couldn't instantiate marshaller of class " + marshallerClass, e); } catch ( IllegalAccessException e ) { throw new DynamoDBMappingException("Couldn't instantiate marshaller of class " + marshallerClass, e); } return (T) marshaller.unmarshall(getter.getReturnType(), value.getS()); } /** * Returns an attribute value for the getter method with a custom marshaller. * Directly returns null when the custom marshaller returns a null String. */ @SuppressWarnings({ "rawtypes", "unchecked" }) private AttributeValue getCustomerMarshallerAttributeValue(Method getter, Object getterReturnResult) { DynamoDBMarshalling annotation = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBMarshalling.class); Class> marshallerClass = annotation.marshallerClass(); DynamoDBMarshaller marshaller; try { marshaller = marshallerClass.newInstance(); } catch ( InstantiationException e ) { throw new DynamoDBMappingException("Failed to instantiate custom marshaller for class " + marshallerClass, e); } catch ( IllegalAccessException e ) { throw new DynamoDBMappingException("Failed to instantiate custom marshaller for class " + marshallerClass, e); } String stringValue = marshaller.marshall(getterReturnResult); if(stringValue == null) { return null; } else { return new AttributeValue().withS(stringValue); } } /** * Returns a marshaller that knows how to provide an AttributeValue for the * result of the getter given. */ ArgumentMarshaller getArgumentMarshaller(final Method getter) { synchronized (argumentMarshallerCache) { ArgumentMarshaller marshaller = argumentMarshallerCache.get(getter); if ( marshaller != null ) { return marshaller; } if ( isCustomMarshaller(getter) ) { // Custom marshaller always returns String attribute value. marshaller = new StringAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { return getCustomerMarshallerAttributeValue(getter, obj); } }; } else { marshaller = computeArgumentMarshaller(getter); } argumentMarshallerCache.put(getter, marshaller); return marshaller; } } /** * Note this method is synchronized on {@link #argumentMarshallerCache} while being executed. */ private ArgumentMarshaller computeArgumentMarshaller(final Method getter) { ArgumentMarshaller marshaller; Class returnType = getter.getReturnType(); if ( Set.class.isAssignableFrom(returnType) ) { Type genericType = getter.getGenericReturnType(); if ( genericType instanceof ParameterizedType ) { if ( ((ParameterizedType) genericType).getActualTypeArguments()[0].toString().equals("byte[]") ) { returnType = byte[].class; } else { returnType = (Class) ((ParameterizedType) genericType).getActualTypeArguments()[0]; } } if ( Date.class.isAssignableFrom(returnType) ) { marshaller = new StringSetAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { List timestamps = new LinkedList(); for ( Object o : (Set) obj ) { timestamps.add(new DateUtils().formatIso8601Date((Date) o)); } return new AttributeValue().withSS(timestamps); } }; } else if ( Calendar.class.isAssignableFrom(returnType) ) { marshaller = new StringSetAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { List timestamps = new LinkedList(); for ( Object o : (Set) obj ) { timestamps.add(new DateUtils().formatIso8601Date(((Calendar) o).getTime())); } return new AttributeValue().withSS(timestamps); } }; } else if ( boolean.class.isAssignableFrom(returnType) || Boolean.class.isAssignableFrom(returnType) ) { marshaller = new NumberSetAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { List booleanAttributes = new ArrayList(); for ( Object b : (Set) obj ) { if ( b == null || !(Boolean) b ) { booleanAttributes.add("0"); } else { booleanAttributes.add("1"); } } return new AttributeValue().withNS(booleanAttributes); } }; } else if ( returnType.isPrimitive() || Number.class.isAssignableFrom(returnType) ) { marshaller = new NumberSetAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { List attributes = new ArrayList(); for ( Object o : (Set) obj ) { attributes.add(String.valueOf(o)); } return new AttributeValue().withNS(attributes); } }; } else if (ByteBuffer.class.isAssignableFrom(returnType)) { marshaller = new BinarySetAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { List attributes = new ArrayList(); for ( Object o : (Set) obj ) { attributes.add((ByteBuffer) o); } return new AttributeValue().withBS(attributes); } }; } else if (byte[].class.isAssignableFrom(returnType)) { marshaller = new BinarySetAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { List attributes = new ArrayList(); for ( Object o : (Set) obj ) { attributes.add(ByteBuffer.wrap((byte[])o)); } return new AttributeValue().withBS(attributes); } }; } else { // subclass may extend the behavior by overriding the // defaultCollectionArgumentMarshaller method marshaller = defaultCollectionArgumentMarshaller(returnType); } } else if ( Collection.class.isAssignableFrom(returnType) ) { throw new DynamoDBMappingException("Non-set collections aren't supported: " + (getter.getDeclaringClass() + "." + getter.getName())); } else { // Non-set return type if ( Date.class.isAssignableFrom(returnType) ) { marshaller = new StringAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { return new AttributeValue().withS(new DateUtils().formatIso8601Date((Date) obj)); } }; } else if ( Calendar.class.isAssignableFrom(returnType) ) { marshaller = new StringAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { return new AttributeValue().withS(new DateUtils() .formatIso8601Date(((Calendar) obj).getTime())); } }; } else if ( boolean.class.isAssignableFrom(returnType) || Boolean.class.isAssignableFrom(returnType) ) { marshaller = new NumberAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { if ( obj == null || !(Boolean) obj ) { return new AttributeValue().withN("0"); } else { return new AttributeValue().withN("1"); } } }; } else if ( returnType.isPrimitive() || Number.class.isAssignableFrom(returnType) ) { marshaller = new NumberAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { return new AttributeValue().withN(String.valueOf(obj)); } }; } else if ( returnType == String.class ) { marshaller = new StringAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { if ( ((String) obj).length() == 0 ) return null; return new AttributeValue().withS(String.valueOf(obj)); } }; } else if ( returnType == ByteBuffer.class ) { marshaller = new BinaryAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { return new AttributeValue().withB((ByteBuffer)obj); } }; } else if ( returnType == byte[].class) { marshaller = new BinaryAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { return new AttributeValue().withB(ByteBuffer.wrap((byte[])obj)); } }; } else { marshaller = defaultArgumentMarshaller(returnType, getter); } } return marshaller; } /** * Note this method is synchronized on {@link #argumentMarshallerCache} while being executed. * @param returnElementType the element of the return type which is known to be a collection * @return the default argument marshaller for a collection */ private ArgumentMarshaller defaultCollectionArgumentMarshaller(final Class returnElementType) { if ( S3Link.class.isAssignableFrom(returnElementType) ) { throw new DynamoDBMappingException("Collection types not permitted for " + S3Link.class); } else { return new StringSetAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { List attributes = new ArrayList(); for ( Object o : (Set) obj ) { attributes.add(String.valueOf(o)); } return new AttributeValue().withSS(attributes); } }; } } /** * Note this method is synchronized on {@link #argumentMarshallerCache} while being executed. * @param returnType the return type * @return the default argument marshaller */ private ArgumentMarshaller defaultArgumentMarshaller(final Class returnType, final Method getter) { if ( returnType == S3Link.class ) { return new StringAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { S3Link s3link = (S3Link) obj; if ( s3link.getBucketName() == null || s3link.getKey() == null ) { // insufficient S3 resource specification return null; } String json = s3link.toJson(); return new AttributeValue().withS(json); } }; } else { throw new DynamoDBMappingException("Unsupported type: " + returnType + " for " + getter); } } /** * Attempts to parse the string given as a boolean and return its value. * Throws an exception if the value is anything other than 0 or 1. */ private boolean parseBoolean(String s) { if ( "1".equals(s) ) { return true; } else if ( "0".equals(s) ) { return false; } else { throw new IllegalArgumentException("Expected 1 or 0 for boolean value, was " + s); } } /** * Returns the attribute name corresponding to the given getter method. */ String getAttributeName(Method getter) { String attributeName; readLockAttrName.lock(); try { attributeName = attributeNameCache.get(getter); } finally { readLockAttrName.unlock(); } if ( attributeName != null ) return attributeName; DynamoDBHashKey hashKeyAnnotation = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBHashKey.class); if ( hashKeyAnnotation != null ) { attributeName = hashKeyAnnotation.attributeName(); if ( attributeName != null && attributeName.length() > 0 ) return cacheAttributeName(getter, attributeName); } DynamoDBIndexHashKey indexHashKey = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBIndexHashKey.class); if ( indexHashKey != null ) { attributeName = indexHashKey.attributeName(); if ( attributeName != null && attributeName.length() > 0 ) return cacheAttributeName(getter, attributeName); } DynamoDBRangeKey rangeKey = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBRangeKey.class); if ( rangeKey != null ) { attributeName = rangeKey.attributeName(); if ( attributeName != null && attributeName.length() > 0 ) return cacheAttributeName(getter, attributeName); } DynamoDBIndexRangeKey indexRangeKey = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBIndexRangeKey.class); if ( indexRangeKey != null ) { attributeName = indexRangeKey.attributeName(); if ( attributeName != null && attributeName.length() > 0 ) return cacheAttributeName(getter, attributeName); } DynamoDBAttribute attribute = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBAttribute.class); if ( attribute != null ) { attributeName = attribute.attributeName(); if ( attributeName != null && attributeName.length() > 0 ) return cacheAttributeName(getter, attributeName); } DynamoDBVersionAttribute version = ReflectionUtils.getAnnotationFromGetterOrField(getter, DynamoDBVersionAttribute.class); if ( version != null ) { attributeName = version.attributeName(); if ( attributeName != null && attributeName.length() > 0 ) return cacheAttributeName(getter, attributeName); } // Default to the camel-cased field name of the getter method, inferred // according to the Java naming convention. attributeName = ReflectionUtils.getFieldNameByGetter(getter, true); return cacheAttributeName(getter, attributeName); } private String cacheAttributeName(Method getter, String attributeName) { writeLockAttrName.lock(); try { attributeNameCache.put(getter, attributeName); } finally { writeLockAttrName.unlock(); } return attributeName; } /** * Returns the setter corresponding to the getter given, or null if no such * setter exists. */ Method getSetter(Method getter) { synchronized (setterCache) { if ( !setterCache.containsKey(getter) ) { String fieldName = ReflectionUtils.getFieldNameByGetter(getter, false); String setterName = "set" + fieldName; Method setter = null; try { setter = getter.getDeclaringClass().getMethod(setterName, getter.getReturnType()); } catch ( NoSuchMethodException e ) { throw new DynamoDBMappingException("Expected a public, one-argument method called " + setterName + " on class " + getter.getDeclaringClass(), e); } catch ( SecurityException e ) { throw new DynamoDBMappingException("No access to public, one-argument method called " + setterName + " on class " + getter.getDeclaringClass(), e); } setterCache.put(getter, setter); } return setterCache.get(getter); } } /** * Returns a marshaller that knows how to provide an AttributeValue for the * getter method given. Also increments the value of the getterReturnResult * given. */ ArgumentMarshaller getVersionedArgumentMarshaller(final Method getter, Object getterReturnResult) { synchronized (versionArgumentMarshallerCache) { if ( !versionArgumentMarshallerCache.containsKey(getter) ) { ArgumentMarshaller marshaller = null; final Class returnType = getter.getReturnType(); if ( BigInteger.class.isAssignableFrom(returnType) ) { marshaller = new ArgumentMarshaller() { @Override public AttributeValue marshall(Object obj) { if ( obj == null ) obj = BigInteger.ZERO; Object newValue = ((BigInteger) obj).add(BigInteger.ONE); return getArgumentMarshaller(getter).marshall(newValue); } }; } else if ( Integer.class.isAssignableFrom(returnType) ) { marshaller = new ArgumentMarshaller() { @Override public AttributeValue marshall(Object obj) { if ( obj == null ) obj = new Integer(0); Object newValue = ((Integer) obj).intValue() + 1; return getArgumentMarshaller(getter).marshall(newValue); } }; } else if ( Byte.class.isAssignableFrom(returnType) ) { marshaller = new ArgumentMarshaller() { @Override public AttributeValue marshall(Object obj) { if ( obj == null ) obj = new Byte((byte) 0); Object newValue = (byte) ((((Byte) obj).byteValue() + 1) % Byte.MAX_VALUE); return getArgumentMarshaller(getter).marshall(newValue); } }; } else if ( Long.class.isAssignableFrom(returnType) ) { marshaller = new ArgumentMarshaller() { @Override public AttributeValue marshall(Object obj) { if ( obj == null ) obj = new Long(0); Object newValue = ((Long) obj).longValue() + 1L; return getArgumentMarshaller(getter).marshall(newValue); } }; } else { throw new DynamoDBMappingException("Unsupported parameter type for " + DynamoDBVersionAttribute.class + ": " + returnType + ". Must be a whole-number type."); } versionArgumentMarshallerCache.put(getter, marshaller); } return versionArgumentMarshallerCache.get(getter); } } /** * Returns a marshaller for the auto-generated key returned by the getter given. */ ArgumentMarshaller getAutoGeneratedKeyArgumentMarshaller(final Method getter) { synchronized (keyArgumentMarshallerCache) { if ( !keyArgumentMarshallerCache.containsKey(getter) ) { ArgumentMarshaller marshaller = null; Class returnType = getter.getReturnType(); if ( String.class.isAssignableFrom(returnType) ) { marshaller = new ArgumentMarshaller() { @Override public AttributeValue marshall(Object obj) { String newValue = UUID.randomUUID().toString(); return getArgumentMarshaller(getter).marshall(newValue); } }; } else { throw new DynamoDBMappingException("Unsupported type for " + getter + ": " + returnType + ". Only Strings are supported when auto-generating keys."); } keyArgumentMarshallerCache.put(getter, marshaller); } return keyArgumentMarshallerCache.get(getter); } } /** * Returns whether the method given is an annotated, no-args getter of a * version attribute. */ boolean isVersionAttributeGetter(Method getter) { synchronized (versionAttributeGetterCache) { if ( !versionAttributeGetterCache.containsKey(getter) ) { versionAttributeGetterCache.put( getter, getter.getName().startsWith("get") && getter.getParameterTypes().length == 0 && ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBVersionAttribute.class)); } return versionAttributeGetterCache.get(getter); } } /** * Returns whether the method given is an assignable key getter. */ boolean isAssignableKey(Method getter) { synchronized (autoGeneratedKeyGetterCache) { if ( !autoGeneratedKeyGetterCache.containsKey(getter) ) { autoGeneratedKeyGetterCache.put( getter, ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBAutoGeneratedKey.class) && ( ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBHashKey.class) || ReflectionUtils.getterOrFieldHasAnnotation(getter, DynamoDBRangeKey.class))); } return autoGeneratedKeyGetterCache.get(getter); } } /** * Returns the name of the primary hash key. */ String getPrimaryHashKeyName(Class clazz) { return getAttributeName(getPrimaryHashKeyGetter(clazz)); } /** * Returns the name of the primary range key, or null if the table does not * one. */ String getPrimaryRangeKeyName(Class clazz) { Method primaryRangeKeyGetter = getPrimaryHashKeyGetter(clazz); return primaryRangeKeyGetter == null ? null : getAttributeName(getPrimaryRangeKeyGetter(clazz)); } /** * Returns true if and only if the specified class has declared a * primary range key. */ boolean hasPrimaryRangeKey(Class clazz) { return getPrimaryRangeKeyGetter(clazz) != null; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy