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

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

Go to download

The AWS Java SDK for Amazon DynamoDB module holds the client classes that are used for communicating with Amazon DynamoDB Service

There is a newer version: 1.12.772
Show newest version
/*
 * Copyright 2014-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * 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 com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.BinaryAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.BinarySetAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.BooleanAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.ListAttributeMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.ArgumentMarshaller.MapAttributeMarshaller;
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.datamodeling.DynamoDBMapperFieldModel.DynamoDBAttributeType;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties.Bean;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories.Rule;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories.RuleFactory;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.BooleanSetToNumberSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.BooleanToBooleanMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.BooleanToNumberMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteArraySetToBinarySetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteArrayToBinaryMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteBufferSetToBinarySetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ByteBufferToBinaryMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CalendarSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CalendarToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CollectionToListMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.CustomMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.DateSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.DateToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.MapToMapMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.NumberSetToNumberSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.NumberToNumberMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToMapMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.ObjectToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.S3LinkToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.StringSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.StringToStringMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.marshallers.UUIDSetToStringSetMarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigDecimalSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigDecimalUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigIntegerSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BigIntegerUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BooleanSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.BooleanUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteArraySetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteArrayUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteBufferSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteBufferUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ByteUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.CalendarSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.CalendarUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.CustomUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DateSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DateUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DoubleSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.DoubleUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.FloatSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.FloatUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.IntegerSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.IntegerUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ListUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.LongSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.LongUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.MapUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.NullableUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ObjectSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ObjectUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.S3LinkUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ShortSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.ShortUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.StringSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.StringUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.UUIDSetUnmarshaller;
import com.amazonaws.services.dynamodbv2.datamodeling.unmarshallers.UUIDUnmarshaller;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * Pre-defined strategies for mapping between Java types and DynamoDB types.
 */
public final class ConversionSchemas {

    private static final Log LOGGER =
            LogFactory.getLog(ConversionSchemas.class);

    /**
     * The V1 schema mapping, which retains strict backwards compatibility with
     * the original DynamoDB data model. In particular, it marshals Java
     * Booleans as DynamoDB Numbers rather than the newer Boolean type, and does
     * not support marshaling Lists or Maps. It can unmarshal
     * values written in newer formats to ease migration.
     * 

* Use me if you have other code still using an old version of the SDK that * does not understand the new List and Map types and want to ensure that * you don't accidentally start writing values using these types. */ public static final ConversionSchema V1 = v1Builder("V1ConversionSchema").build(); /** * A V2 conversion schema which retains backwards compatibility with the * V1 conversion schema for existing DynamoDB types, but adds the ability * to marshall recursive structures using the new List and Map types. This * is currently the default conversion schema. */ public static final ConversionSchema V2_COMPATIBLE = v2CompatibleBuilder( "V2CompatibleConversionSchema").build(); /** * The native V2 conversion schema. This schema breaks compatibility with * older versions of the mapper that only support the V1 schema by * storing booleans as native DynamoDB Booleans rather than as a 1 or 0 * in a DynamoDB Number. Switching to the V2 schema will prevent older * versions of the mapper from reading items you write that contain * booleans. */ public static final ConversionSchema V2 = v2Builder("V2ConversionSchema").build(); static final ConversionSchema DEFAULT = V2_COMPATIBLE; /** * A ConversionSchema builder that defaults to building {@link #V1}. */ public static Builder v1Builder(String name) { return new Builder(name, V1MarshallerSet.marshallers(), V1MarshallerSet.setMarshallers(), StandardUnmarshallerSet.unmarshallers(), StandardUnmarshallerSet.setUnmarshallers()); } /** * A ConversionSchema builder that defaults to building {@link #V2_COMPATIBLE}. */ public static Builder v2CompatibleBuilder(String name) { return new Builder(name, V2CompatibleMarshallerSet.marshallers(), V2CompatibleMarshallerSet.setMarshallers(), StandardUnmarshallerSet.unmarshallers(), StandardUnmarshallerSet.setUnmarshallers()); } /** * A ConversionSchema builder that defaults to building {@link #V2}. */ public static Builder v2Builder(String name) { return new Builder(name, V2MarshallerSet.marshallers(), V2MarshallerSet.setMarshallers(), StandardUnmarshallerSet.unmarshallers(), StandardUnmarshallerSet.setUnmarshallers()); } public static class Builder { private final String name; private final List> marshallers; private final List> setMarshallers; private final List> unmarshallers; private final List> setUnmarshallers; Builder(String name, List> marshallers, List> setMarshallers, List> unmarshallers, List> setUnmarshallers) { this.name = name; this.marshallers = marshallers; this.setMarshallers = setMarshallers; this.unmarshallers = unmarshallers; this.setUnmarshallers = setUnmarshallers; } /** * Adds marshaling of a type to the schema. Types are in LIFO order, so the last type added * will be the first matched. */ public Builder addFirstType(Class clazz, ArgumentMarshaller marshaller, ArgumentUnmarshaller unmarshaller) { this.marshallers.add(0, Pair.of(clazz, marshaller)); this.unmarshallers.add(0, Pair.of(clazz, unmarshaller)); return this; } /** * Adds marshaling of a Set of a type to the schema. Types are in LIFO order, so the last * type added will be the first matched. */ public Builder addFirstSetType(Class clazz, ArgumentMarshaller marshaller, ArgumentUnmarshaller unmarshaller) { this.setMarshallers.add(0, Pair.of(clazz, marshaller)); this.setUnmarshallers.add(0, Pair.of(clazz, unmarshaller)); return this; } public ConversionSchema build() { return new StandardConversionSchema(name, new AbstractMarshallerSet(marshallers, setMarshallers), new StandardUnmarshallerSet(unmarshallers, setUnmarshallers)); } } static class StandardConversionSchema implements ConversionSchema { private final String name; private final MarshallerSet marshallers; private final UnmarshallerSet unmarshallers; public StandardConversionSchema( String name, MarshallerSet marshallers, UnmarshallerSet unmarshallers) { this.name = name; this.marshallers = new CachingMarshallerSet( new AnnotationAwareMarshallerSet(marshallers)); this.unmarshallers = new CachingUnmarshallerSet( new AnnotationAwareUnmarshallerSet(unmarshallers)); } @Override public ItemConverter getConverter(Dependencies dependencies) { S3ClientCache s3cc = dependencies.get(S3ClientCache.class); return new StandardItemConverter( marshallers, unmarshallers, s3cc); } @Override public String toString() { return name; } } static class StandardItemConverter implements ItemConverter { private final MarshallerSet marshallerSet; private final UnmarshallerSet unmarshallerSet; private final S3ClientCache s3cc; public StandardItemConverter( MarshallerSet marshallerSet, UnmarshallerSet unmarshallerSet, S3ClientCache s3cc) { this.marshallerSet = marshallerSet; this.unmarshallerSet = unmarshallerSet; this.s3cc = s3cc; } @Override public DynamoDBMapperFieldModel getFieldModel(Method getter) { final ArgumentMarshaller marshaller = getMarshaller(getter); final DynamoDBAttributeType attributeType; if (marshaller instanceof StringAttributeMarshaller) { attributeType = DynamoDBAttributeType.S; } else if (marshaller instanceof NumberAttributeMarshaller) { attributeType = DynamoDBAttributeType.N; } else if (marshaller instanceof BinaryAttributeMarshaller) { attributeType = DynamoDBAttributeType.B; } else if (marshaller instanceof StringSetAttributeMarshaller) { attributeType = DynamoDBAttributeType.SS; } else if (marshaller instanceof NumberSetAttributeMarshaller) { attributeType = DynamoDBAttributeType.NS; } else if (marshaller instanceof BinarySetAttributeMarshaller) { attributeType = DynamoDBAttributeType.BS; } else if (marshaller instanceof BooleanAttributeMarshaller) { attributeType = DynamoDBAttributeType.BOOL; } else if (marshaller instanceof ListAttributeMarshaller) { attributeType = DynamoDBAttributeType.L; } else if (marshaller instanceof MapAttributeMarshaller) { attributeType = DynamoDBAttributeType.M; } else { throw new DynamoDBMappingException( "Unrecognized marshaller type for " + getter + ": " + marshaller); } // Note, generating the attribute name using this method is not // actually correct for @DynamoDBFlattened attributes, however, // its the best that can be done given only the method. The // proper way to get this information is using the model factory. final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter, null); final DynamoDBMapperFieldModel.Builder builder = new DynamoDBMapperFieldModel.Builder(void.class, annotations); builder.with(attributeType); return builder.build(); } @Override public AttributeValue convert(Method getter, Object object) { if (object == null) { return null; } ArgumentMarshaller marshaller = getMarshaller(getter); return marshaller.marshall(object); } @Override public Map convert(Object object) { if (object == null) { return null; } Class clazz = (Class)object.getClass(); Map result = new HashMap(); for (final Bean bean : StandardBeanProperties.of(clazz).map().values()) { Object getterResult = bean.reflect().get(object); if (getterResult != null) { AttributeValue value = convert(bean.type().getter(), getterResult); if (value != null) { result.put(bean.properties().attributeName(), value); } } } return result; } private ArgumentMarshaller getMarshaller(Method getter) { ArgumentMarshaller marshaller = marshallerSet.getMarshaller(getter); marshaller = augment(getter.getGenericReturnType(), marshaller); return marshaller; } private ArgumentMarshaller getMemberMarshaller(Type type) { ArgumentMarshaller marshaller = marshallerSet.getMemberMarshaller(type); marshaller = augment(type, marshaller); return marshaller; } private ArgumentMarshaller augment( Type type, ArgumentMarshaller marshaller) { if (marshaller instanceof CollectionToListMarshaller) { return getCollectionToListMarshaller(type); } if (marshaller instanceof MapToMapMarshaller) { return getMapToMapMarshaller(type); } if (marshaller instanceof ObjectToMapMarshaller) { return getObjectToMapMarshaller(type); } return marshaller; } private ArgumentMarshaller getCollectionToListMarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the " + "Collection type " + type + ", which is not " + "parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 1) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the " + "Collection type " + type + "; unexpected number of " + "type arguments."); } ArgumentMarshaller memberMarshaller = getMemberMarshaller(args[0]); return new CollectionToListMarshaller(memberMarshaller); } private ArgumentMarshaller getMapToMapMarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 2) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + "; unexpected number of type " + "arguments."); } if (args[0] != String.class) { throw new DynamoDBMappingException( "Only Map is supported."); } ArgumentMarshaller memberMarshaller = getMemberMarshaller(args[1]); return new MapToMapMarshaller(memberMarshaller); } private ArgumentMarshaller getObjectToMapMarshaller(Type type) { Type localType = type; if (localType instanceof ParameterizedType) { localType = ((ParameterizedType) localType).getRawType(); } if (!(localType instanceof Class)) { throw new DynamoDBMappingException( "Cannot convert " + type + " to a class"); } Class clazz = (Class) localType; if (StandardAnnotationMaps.of(clazz).attributeType() != DynamoDBAttributeType.M) { throw new DynamoDBMappingException( "Cannot marshall type " + type + " without a custom marshaler or @DynamoDBDocument " + "annotation."); } return new ObjectToMapMarshaller(this); } @Override public Object unconvert( Method getter, Method setter, AttributeValue value) { ArgumentUnmarshaller unmarshaller = getUnmarshaller(getter, setter); return unmarshall(unmarshaller, setter, value); } @Override public T unconvert( Class clazz, Map value) { T result = createObject(clazz); if (value == null || value.isEmpty()) { return result; } for (final Bean bean : StandardBeanProperties.of(clazz).map().values()) { AttributeValue av = value.get(bean.properties().attributeName()); if (av != null) { ArgumentUnmarshaller unmarshaller = getUnmarshaller(bean.type().getter(), bean.type().setter()); Object unmarshalled = unmarshall(unmarshaller, bean.type().setter(), av); bean.reflect().set(result, unmarshalled); } } return result; } private ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { ArgumentUnmarshaller unmarshaller = unmarshallerSet.getUnmarshaller(getter, setter); unmarshaller = augment( setter.getGenericParameterTypes()[0], unmarshaller); return new NullableUnmarshaller(unmarshaller); } private ArgumentUnmarshaller getMemberUnmarshaller(Type type) { ArgumentUnmarshaller unmarshaller = unmarshallerSet.getMemberUnmarshaller(type); unmarshaller = augment(type, unmarshaller); return new NullableUnmarshaller(unmarshaller); } private ArgumentUnmarshaller augment( Type type, ArgumentUnmarshaller unmarshaller) { // Inject our s3 client cache if it's an S3LinkUnmarshaller. if (unmarshaller instanceof S3LinkUnmarshaller) { return new S3LinkUnmarshaller(s3cc); } // Inject an appropriate member-type unmarshaller if it's a list, // object-set, or map unmarshaller. if (unmarshaller instanceof ObjectSetUnmarshaller) { return getObjectSetUnmarshaller(type); } if (unmarshaller instanceof ListUnmarshaller) { return getListUnmarshaller(type); } if (unmarshaller instanceof MapUnmarshaller) { return getMapUnmarshaller(type); } // Inject ourselves to recursively unmarshall things if it's an // ObjectUnmarshaller. if (unmarshaller instanceof ObjectUnmarshaller) { return getObjectUnmarshaller(type); } return unmarshaller; } private ArgumentUnmarshaller getObjectSetUnmarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Set " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 1) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Set " + "type " + type + "; unexpected number of type " + "arguments."); } ArgumentUnmarshaller memberUnmarshaller = getMemberUnmarshaller(args[0]); return new ObjectSetUnmarshaller(memberUnmarshaller); } private ArgumentUnmarshaller getListUnmarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the List " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 1) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the List " + "type " + type + "; unexpected number of type " + "arguments."); } ArgumentUnmarshaller memberUnmarshaller = getMemberUnmarshaller(args[0]); return new ListUnmarshaller(memberUnmarshaller); } private ArgumentUnmarshaller getMapUnmarshaller(Type type) { if (!(type instanceof ParameterizedType)) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + ", which is not parameterized."); } ParameterizedType ptype = (ParameterizedType) type; Type[] args = ptype.getActualTypeArguments(); if (args == null || args.length != 2) { throw new DynamoDBMappingException( "Cannot tell what type of objects belong in the Map " + "type " + type + "; unexpected number of type " + "arguments."); } if (args[0] != String.class) { throw new DynamoDBMappingException( "Only Map is supported."); } ArgumentUnmarshaller memberUnmarshaller = getMemberUnmarshaller(args[1]); return new MapUnmarshaller(memberUnmarshaller); } private ArgumentUnmarshaller getObjectUnmarshaller(Type type) { Type localType = type; if (localType instanceof ParameterizedType) { localType = ((ParameterizedType) type).getRawType(); } if (!(localType instanceof Class)) { throw new DynamoDBMappingException( "Cannot convert " + type + " to a class"); } Class clazz = (Class) localType; if (StandardAnnotationMaps.of(clazz).attributeType() != DynamoDBAttributeType.M) { throw new DynamoDBMappingException( "Cannot unmarshall to type " + type + " without a custom marshaler or @DynamoDBDocument " + "annotation."); } return new ObjectUnmarshaller(this, clazz); } private static Object unmarshall( ArgumentUnmarshaller unmarshaller, Method setter, AttributeValue value) { unmarshaller.typeCheck(value, setter); try { return unmarshaller.unmarshall(value); } catch (IllegalArgumentException e) { throw new DynamoDBMappingException( "Couldn't unmarshall value " + value + " for " + setter, e); } catch (ParseException e) { throw new DynamoDBMappingException( "Error attempting to parse date string " + value + " for " + setter, e); } } private static T createObject(Class clazz) { try { return clazz.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); } } } static interface MarshallerSet { ArgumentMarshaller getMarshaller(Method getter); ArgumentMarshaller getMemberMarshaller(Type memberType); } static interface UnmarshallerSet { ArgumentUnmarshaller getUnmarshaller(Method getter, Method setter); ArgumentUnmarshaller getMemberUnmarshaller(Type memberType); } static final class V2MarshallerSet { private static List> marshallers() { List> list = new ArrayList>(); // Use the new V2 boolean marshallers. addStandardDateMarshallers(list); addV2BooleanMarshallers(list); addStandardNumberMarshallers(list); addStandardStringMarshallers(list); addStandardBinaryMarshallers(list); addStandardS3LinkMarshallers(list); // Add marshallers for the new list and map types. list.add(Pair.of(List.class, CollectionToListMarshaller.instance())); list.add(Pair.of(Map.class, MapToMapMarshaller.instance())); // Make sure I'm last since I'll catch anything. list.add(Pair.of(Object.class, ObjectToMapMarshaller.instance())); return list; } private static List> setMarshallers() { List> list = new ArrayList>(); // No more Set -> NS or Set -> SS marshallers addStandardDateSetMarshallers(list); addStandardNumberSetMarshallers(list); addStandardStringSetMarshallers(list); addStandardBinarySetMarshallers(list); // Make sure I'm last since I'll catch anything. list.add(Pair.of( Object.class, CollectionToListMarshaller.instance())); return list; } } static final class V2CompatibleMarshallerSet { private static List> marshallers() { List> list = new ArrayList>(); // Keep the old v1 boolean marshallers for compatibility. addStandardDateMarshallers(list); addV1BooleanMarshallers(list); addStandardNumberMarshallers(list); addStandardStringMarshallers(list); addStandardBinaryMarshallers(list); addStandardS3LinkMarshallers(list); // Add marshallers for the new list and map types. list.add(Pair.of(List.class, CollectionToListMarshaller.instance())); list.add(Pair.of(Map.class, MapToMapMarshaller.instance())); // Make sure I'm last since I'll catch anything. list.add(Pair.of(Object.class, ObjectToMapMarshaller.instance())); return list; } private static List> setMarshallers() { List> list = new ArrayList>(); addStandardDateSetMarshallers(list); addV1BooleanSetMarshallers(list); addStandardNumberSetMarshallers(list); addStandardStringSetMarshallers(list); addStandardBinarySetMarshallers(list); // If all else fails, fall back to this default marshaler to // retain backwards-compatible behavior. list.add(Pair.of(Object.class, ObjectSetToStringSetMarshaller.instance())); return list; } } static final class V1MarshallerSet { private static List> marshallers() { List> list = new ArrayList>(); addStandardDateMarshallers(list); addV1BooleanMarshallers(list); addStandardNumberMarshallers(list); addStandardStringMarshallers(list); addStandardBinaryMarshallers(list); addStandardS3LinkMarshallers(list); return list; } private static List> setMarshallers() { List> list = new ArrayList>(); addStandardDateSetMarshallers(list); addV1BooleanSetMarshallers(list); addStandardNumberSetMarshallers(list); addStandardStringSetMarshallers(list); addStandardBinarySetMarshallers(list); // If all else fails, fall back to this default marshaler to // retain backwards-compatible behavior. list.add(Pair.of(Object.class, ObjectSetToStringSetMarshaller.instance())); return list; } } private static void addStandardDateMarshallers( List> list) { list.add(Pair.of(Date.class, DateToStringMarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarToStringMarshaller.instance())); } private static void addV1BooleanMarshallers( List> list) { list.add(Pair.of(Boolean.class, BooleanToNumberMarshaller.instance())); list.add(Pair.of(boolean.class, BooleanToNumberMarshaller.instance())); } private static void addV2BooleanMarshallers( List> list) { list.add(Pair.of(Boolean.class, BooleanToBooleanMarshaller.instance())); list.add(Pair.of(boolean.class, BooleanToBooleanMarshaller.instance())); } private static void addStandardNumberMarshallers( List> list) { list.add(Pair.of(Number.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(byte.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(short.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(int.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(long.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(float.class, NumberToNumberMarshaller.instance())); list.add(Pair.of(double.class, NumberToNumberMarshaller.instance())); } private static void addStandardStringMarshallers( List> list) { list.add(Pair.of(String.class, StringToStringMarshaller.instance())); list.add(Pair.of(UUID.class, ObjectToStringMarshaller.instance())); } private static void addStandardBinaryMarshallers( List> list) { list.add(Pair.of(ByteBuffer.class, ByteBufferToBinaryMarshaller.instance())); list.add(Pair.of(byte[].class, ByteArrayToBinaryMarshaller.instance())); } private static void addStandardS3LinkMarshallers( List> list) { list.add(Pair.of(S3Link.class, S3LinkToStringMarshaller.instance())); } private static void addStandardDateSetMarshallers( List> list) { list.add(Pair.of(Date.class, DateSetToStringSetMarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarSetToStringSetMarshaller.instance())); } private static void addStandardNumberSetMarshallers( List> list) { list.add(Pair.of(Number.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(byte.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(short.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(int.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(long.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(float.class, NumberSetToNumberSetMarshaller.instance())); list.add(Pair.of(double.class, NumberSetToNumberSetMarshaller.instance())); } private static void addStandardStringSetMarshallers( List> list) { list.add(Pair.of(String.class, StringSetToStringSetMarshaller.instance())); list.add(Pair.of(UUID.class, UUIDSetToStringSetMarshaller.instance())); } private static void addStandardBinarySetMarshallers( List> list) { list.add(Pair.of(ByteBuffer.class, ByteBufferSetToBinarySetMarshaller.instance())); list.add(Pair.of(byte[].class, ByteArraySetToBinarySetMarshaller.instance())); } private static void addV1BooleanSetMarshallers( List> list) { list.add(Pair.of(Boolean.class, BooleanSetToNumberSetMarshaller.instance())); list.add(Pair.of(boolean.class, BooleanSetToNumberSetMarshaller.instance())); } private static class AbstractMarshallerSet implements MarshallerSet { private final List> marshallers; private final List> setMarshallers; public AbstractMarshallerSet( List> marshallers, List> setMarshallers) { this.marshallers = marshallers; this.setMarshallers = setMarshallers; } @Override public ArgumentMarshaller getMarshaller(Method getter) { Class returnType = getter.getReturnType(); if (Set.class.isAssignableFrom(returnType)) { Class memberType = unwrapGenericSetParam(getter.getGenericReturnType()); return getSet(getter, memberType); } else { return getScalar(getter, returnType); } } @Override public ArgumentMarshaller getMemberMarshaller(Type memberType) { Class clazz = resolveClass(memberType); if (Set.class.isAssignableFrom(clazz)) { Class setMemberType = unwrapGenericSetParam(memberType); return getSet(null, setMemberType); } else { return getScalar(null, clazz); } } private ArgumentMarshaller getScalar(Method getter, Class type) { ArgumentMarshaller marshaller = find(type, marshallers); if (marshaller == null) { String className = "?"; String methodName = "?"; if (getter != null) { className = getter.getDeclaringClass().toString(); methodName = getter.getName(); } throw new DynamoDBMappingException( "Cannot marshall return type " + type + " of method " + className + "." + methodName + " without a custom marshaler."); } return marshaller; } private ArgumentMarshaller getSet(Method getter, Class memberType) { ArgumentMarshaller marshaller = find(memberType, setMarshallers); if (marshaller == null) { String className = "?"; String methodName = "?"; if (getter != null) { className = getter.getDeclaringClass().toString(); methodName = getter.getName(); } throw new DynamoDBMappingException( "Cannot marshall return type Set<" + memberType + "> of method " + className + "." + methodName + " without a custom marshaller."); } return marshaller; } } static class StandardUnmarshallerSet implements UnmarshallerSet { private final List> unmarshallers; private final List> setUnmarshallers; public StandardUnmarshallerSet() { this(unmarshallers(), setUnmarshallers()); } public StandardUnmarshallerSet( List> unmarshallers, List> setUnmarshallers) { this.unmarshallers = unmarshallers; this.setUnmarshallers = setUnmarshallers; } private static List> unmarshallers() { List> list = new ArrayList>(); list.add(Pair.of(double.class, DoubleUnmarshaller.instance())); list.add(Pair.of(Double.class, DoubleUnmarshaller.instance())); list.add(Pair.of(BigDecimal.class, BigDecimalUnmarshaller.instance())); list.add(Pair.of(BigInteger.class, BigIntegerUnmarshaller.instance())); list.add(Pair.of(int.class, IntegerUnmarshaller.instance())); list.add(Pair.of(Integer.class, IntegerUnmarshaller.instance())); list.add(Pair.of(float.class, FloatUnmarshaller.instance())); list.add(Pair.of(Float.class, FloatUnmarshaller.instance())); list.add(Pair.of(byte.class, ByteUnmarshaller.instance())); list.add(Pair.of(Byte.class, ByteUnmarshaller.instance())); list.add(Pair.of(long.class, LongUnmarshaller.instance())); list.add(Pair.of(Long.class, LongUnmarshaller.instance())); list.add(Pair.of(short.class, ShortUnmarshaller.instance())); list.add(Pair.of(Short.class, ShortUnmarshaller.instance())); list.add(Pair.of(boolean.class, BooleanUnmarshaller.instance())); list.add(Pair.of(Boolean.class, BooleanUnmarshaller.instance())); list.add(Pair.of(Date.class, DateUnmarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarUnmarshaller.instance())); list.add(Pair.of(ByteBuffer.class, ByteBufferUnmarshaller.instance())); list.add(Pair.of(byte[].class, ByteArrayUnmarshaller.instance())); list.add(Pair.of(S3Link.class, S3LinkUnmarshaller.instance())); list.add(Pair.of(UUID.class, UUIDUnmarshaller.instance())); list.add(Pair.of(String.class, StringUnmarshaller.instance())); list.add(Pair.of(List.class, ListUnmarshaller.instance())); list.add(Pair.of(Map.class, MapUnmarshaller.instance())); // Make sure I'm last since I'll catch all other types. list.add(Pair.of(Object.class, ObjectUnmarshaller.instance())); return list; } private static List> setUnmarshallers() { List> list = new ArrayList>(); list.add(Pair.of(double.class, DoubleSetUnmarshaller.instance())); list.add(Pair.of(Double.class, DoubleSetUnmarshaller.instance())); list.add(Pair.of(BigDecimal.class, BigDecimalSetUnmarshaller.instance())); list.add(Pair.of(BigInteger.class, BigIntegerSetUnmarshaller.instance())); list.add(Pair.of(int.class, IntegerSetUnmarshaller.instance())); list.add(Pair.of(Integer.class, IntegerSetUnmarshaller.instance())); list.add(Pair.of(float.class, FloatSetUnmarshaller.instance())); list.add(Pair.of(Float.class, FloatSetUnmarshaller.instance())); list.add(Pair.of(byte.class, ByteSetUnmarshaller.instance())); list.add(Pair.of(Byte.class, ByteSetUnmarshaller.instance())); list.add(Pair.of(long.class, LongSetUnmarshaller.instance())); list.add(Pair.of(Long.class, LongSetUnmarshaller.instance())); list.add(Pair.of(short.class, ShortSetUnmarshaller.instance())); list.add(Pair.of(Short.class, ShortSetUnmarshaller.instance())); list.add(Pair.of(boolean.class, BooleanSetUnmarshaller.instance())); list.add(Pair.of(Boolean.class, BooleanSetUnmarshaller.instance())); list.add(Pair.of(Date.class, DateSetUnmarshaller.instance())); list.add(Pair.of(Calendar.class, CalendarSetUnmarshaller.instance())); list.add(Pair.of(ByteBuffer.class, ByteBufferSetUnmarshaller.instance())); list.add(Pair.of(byte[].class, ByteArraySetUnmarshaller.instance())); list.add(Pair.of(UUID.class, UUIDSetUnmarshaller.instance())); list.add(Pair.of(String.class, StringSetUnmarshaller.instance())); // Make sure I'm last since I'll catch all other types. list.add(Pair.of(Object.class, ObjectSetUnmarshaller.instance())); return list; } @Override public ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { if (setter.getParameterTypes().length != 1) { throw new DynamoDBMappingException( "Expected exactly one agument to " + setter); } Class paramType = setter.getParameterTypes()[0]; if (Set.class.isAssignableFrom(paramType)) { paramType = unwrapGenericSetParam( setter.getGenericParameterTypes()[0]); return getSet(setter, paramType); } else { return getScalar(setter, paramType); } } @Override public ArgumentUnmarshaller getMemberUnmarshaller(Type memberType) { Class clazz = resolveClass(memberType); if (Set.class.isAssignableFrom(clazz)) { Class setMemberType = unwrapGenericSetParam(memberType); return getSet(null, setMemberType); } else { return getScalar(null, clazz); } } private ArgumentUnmarshaller getSet(Method setter, Class paramType) { ArgumentUnmarshaller unmarshaller = find(paramType, setUnmarshallers); String className = "?"; String methodName = "?"; if (setter != null) { className = setter.getDeclaringClass().toString(); methodName = setter.getName(); } if (unmarshaller == null) { throw new DynamoDBMappingException( "Cannot unmarshall to parameter type Set<" + paramType + "> of method " + className + "." + methodName + " without a custom " + "unmarshaler."); } return unmarshaller; } private ArgumentUnmarshaller getScalar(Method setter, Class type) { ArgumentUnmarshaller unmarshaller = find(type, unmarshallers); String className = "?"; String methodName = "?"; if (setter != null) { className = setter.getDeclaringClass().toString(); methodName = setter.getName(); } if (unmarshaller == null) { throw new DynamoDBMappingException( "Cannot unmarshall to parameter type " + type + "of method " + className + "." + methodName + " without a custom unmarshaler."); } return unmarshaller; } } private static Class unwrapGenericSetParam(Type setType) { if (!(setType instanceof ParameterizedType)) { LOGGER.warn("Set type " + setType + " is not a " + "ParameterizedType, using default marshaler and " + "unmarshaler!"); return Object.class; } ParameterizedType ptype = (ParameterizedType) setType; Type[] arguments = ptype.getActualTypeArguments(); if (arguments.length != 1) { LOGGER.warn("Set type " + setType + " does not have exactly one " + "type argument, using default marshaler and " + "unmarshaler!"); return Object.class; } if (arguments[0].toString().equals("byte[]")) { return byte[].class; } else { return (Class) arguments[0]; } } private static Class resolveClass(Type type) { Type localType = type; if (localType instanceof ParameterizedType) { localType = ((ParameterizedType) type).getRawType(); } if (!(localType instanceof Class)) { throw new DynamoDBMappingException("Cannot resolve class for type " + type); } return (Class) localType; } private static T find(Class needle, List> haystack) { for (Pair pair : haystack) { if (pair.key.isAssignableFrom(needle)) { return pair.value; } } return null; } private static class Pair { public static Pair of ( Class key, ArgumentMarshaller value) { return new Pair(key, value); } public static Pair of ( Class key, ArgumentUnmarshaller value) { return new Pair(key, value); } public final Class key; public final T value; private Pair(Class key, T value) { this.key = key; this.value = value; } } static class AnnotationAwareMarshallerSet implements MarshallerSet { private final MarshallerSet wrapped; public AnnotationAwareMarshallerSet(MarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentMarshaller getMarshaller(Method getter) { final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter, null); final DynamoDBMarshalling marshalling = annotations.actualOf(DynamoDBMarshalling.class); if (marshalling != null) { return new CustomMarshaller(marshalling.marshallerClass()); } else if (annotations.actualOf(DynamoDBNativeBoolean.class) != null) { return BooleanToBooleanMarshaller.instance(); } return wrapped.getMarshaller(getter); } @Override public ArgumentMarshaller getMemberMarshaller(Type memberType) { return wrapped.getMemberMarshaller(memberType); } } static class AnnotationAwareUnmarshallerSet implements UnmarshallerSet { private final UnmarshallerSet wrapped; public AnnotationAwareUnmarshallerSet(UnmarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter, null); final DynamoDBMarshalling marshalling = annotations.actualOf(DynamoDBMarshalling.class); if (marshalling != null) { return new CustomUnmarshaller(getter.getReturnType(), marshalling.marshallerClass()); } return wrapped.getUnmarshaller(getter, setter); } @Override public ArgumentUnmarshaller getMemberUnmarshaller(Type c) { return wrapped.getMemberUnmarshaller(c); } } static class CachingMarshallerSet implements MarshallerSet { private final Map cache = new HashMap(); private final Map memberCache = new HashMap(); private final MarshallerSet wrapped; public CachingMarshallerSet(MarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentMarshaller getMarshaller(Method getter) { synchronized (cache) { ArgumentMarshaller marshaler = cache.get(getter); if (marshaler != null) { return marshaler; } marshaler = wrapped.getMarshaller(getter); cache.put(getter, marshaler); return marshaler; } } @Override public ArgumentMarshaller getMemberMarshaller(Type memberType) { synchronized (memberCache) { ArgumentMarshaller marshaller = memberCache.get(memberType); if (marshaller != null) { return marshaller; } marshaller = wrapped.getMemberMarshaller(memberType); memberCache.put(memberType, marshaller); return marshaller; } } } static class CachingUnmarshallerSet implements UnmarshallerSet { private final Map cache = new HashMap(); private final Map memberCache = new HashMap(); private final UnmarshallerSet wrapped; public CachingUnmarshallerSet(UnmarshallerSet wrapped) { this.wrapped = wrapped; } @Override public ArgumentUnmarshaller getUnmarshaller( Method getter, Method setter) { synchronized (cache) { ArgumentUnmarshaller unmarshaler = cache.get(getter); if (unmarshaler != null) { return unmarshaler; } unmarshaler = wrapped.getUnmarshaller(getter, setter); cache.put(getter, unmarshaler); return unmarshaler; } } @Override public ArgumentUnmarshaller getMemberUnmarshaller(Type memberType) { synchronized (memberCache) { ArgumentUnmarshaller unmarshaller = memberCache.get(memberType); if (unmarshaller != null) { return unmarshaller; } unmarshaller = wrapped.getMemberUnmarshaller(memberType); memberCache.put(memberType, unmarshaller); return unmarshaller; } } } /** * {@link AttributeValue} converter with {@link ItemConverter} */ static class ItemConverterRuleFactory implements RuleFactory { private final RuleFactory typeConverters; private final ItemConverter converter; private final boolean customSchema; ItemConverterRuleFactory(DynamoDBMapperConfig config, S3Link.Factory s3Links, RuleFactory typeConverters) { final ConversionSchema.Dependencies depends = new ConversionSchema.Dependencies().with(S3ClientCache.class, s3Links.getS3ClientCache()); final ConversionSchema schema = config.getConversionSchema(); this.customSchema = (schema != V1 && schema != V2_COMPATIBLE && schema != V2); this.converter = schema.getConverter(depends); this.typeConverters = typeConverters; } @Override public Rule getRule(ConvertibleType type) { if (customSchema && type.typeConverter() == null) { return new ItemConverterRule(type); } else { return typeConverters.getRule(type); } } private final class ItemConverterRule implements Rule, DynamoDBTypeConverter { private final ConvertibleType type; private ItemConverterRule(final ConvertibleType type) { this.type = type; } @Override public boolean isAssignableFrom(ConvertibleType type) { return true; } @Override public DynamoDBTypeConverter newConverter(ConvertibleType type) { return this; } @Override public DynamoDBAttributeType getAttributeType() { try { return converter.getFieldModel(type.getter()).attributeType(); } catch (final DynamoDBMappingException no) {} return DynamoDBAttributeType.NULL; } @Override public AttributeValue convert(final V object) { return converter.convert(type.getter(), object); } @Override public V unconvert(final AttributeValue object) { return (V)converter.unconvert(type.getter(), type.setter(), object); } } } ConversionSchemas() { throw new UnsupportedOperationException(); } }