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

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

The newest version!
/*
 * Copyright 2011-2015 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}
 * @deprecated These classes have been deprecated, please use the classes in the com.amazonaws.mobileconnectors namespace.
 */
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(DateUtils.parseISO8601Date(s)); } return argument; } }; } else { unmarshaller = new SUnmarshaller() { @Override public Object unmarshall(AttributeValue value) throws ParseException { return 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(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(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(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(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(DateUtils.formatISO8601Date((Date) obj)); } }; } else if ( Calendar.class.isAssignableFrom(returnType) ) { marshaller = new StringAttributeMarshaller() { @Override public AttributeValue marshall(Object obj) { return new AttributeValue().withS(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 - 2024 Weber Informatics LLC | Privacy Policy