aws-sdk-java-v2 icon indicating copy to clipboard operation
aws-sdk-java-v2 copied to clipboard

DynamoDb error with Optional<Map<String, String>> field

Open marcogramy opened this issue 4 years ago • 11 comments

Describe the issue

I'm trying to write to DynamoDb an object model with an Optional<Map<String, String>> field, but it does not work. I'm using a custom Optional attribute converter:

        MapAttributeConverter<Map<String, String>> mapConverter = MapAttributeConverter.builder(EnhancedType.mapOf(String.class, String.class))
                .mapConstructor(HashMap::new)
                .keyConverter(StringStringConverter.create())
                .valueConverter(StringAttributeConverter.create())
                .build();
        OptionalAttributeConverter<Map<String, String>> optionalMapAttributeConverter = OptionalAttributeConverter.create(mapConverter);

Any help is appreciated!

Steps to Reproduce

This is a sample project that reproduce the issue: https://github.com/marcogramy/dynamodbdemo

Current behavior

I'm obtaining this error:

Caused by: java.lang.IllegalStateException: Converter not found for EnhancedType(java.util.Optional<java.util.Map<java.lang.String, java.lang.String>>) at software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider.lambda$converterFor$0(DefaultAttributeConverterProvider.java:134) ~[dynamodb-enhanced-2.17.121.jar:na] at java.util.Optional.orElseThrow(Optional.java:290) ~[na:1.8.0_312] at software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider.converterFor(DefaultAttributeConverterProvider.java:134) ~[dynamodb-enhanced-2.17.121.jar:na] at java.util.HashMap.computeIfAbsent(HashMap.java:1128) ~[na:1.8.0_312] at com.example.dynamodbdemo.converter.CustomAttributeConverterProvider.converterFor(CustomAttributeConverterProvider.java:39) ~[classes/:na] at software.amazon.awssdk.enhanced.dynamodb.internal.converter.ChainConverterProvider.lambda$converterFor$0(ChainConverterProvider.java:66) ~[dynamodb-enhanced-2.17.121.jar:na] at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174) ~[na:1.8.0_312] at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1361) ~[na:1.8.0_312] at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126) ~[na:1.8.0_312] at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:499) ~[na:1.8.0_312] at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:486) ~[na:1.8.0_312] at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) ~[na:1.8.0_312] at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152) ~[na:1.8.0_312] at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_312] at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:531) ~[na:1.8.0_312] at software.amazon.awssdk.enhanced.dynamodb.internal.converter.ChainConverterProvider.converterFor(ChainConverterProvider.java:68) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableAttribute.converterFrom(ImmutableAttribute.java:166) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableAttribute.resolve(ImmutableAttribute.java:162) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.lambda$new$0(StaticImmutableTableSchema.java:153) ~[dynamodb-enhanced-2.17.121.jar:na] at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_312] at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384) ~[na:1.8.0_312] at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[na:1.8.0_312] at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) ~[na:1.8.0_312] at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313) ~[na:1.8.0_312] at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742) ~[na:1.8.0_312] at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647) ~[na:1.8.0_312] at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.(StaticImmutableTableSchema.java:159) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.(StaticImmutableTableSchema.java:77) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema$Builder.build(StaticImmutableTableSchema.java:425) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema.(StaticTableSchema.java:66) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema.(StaticTableSchema.java:64) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema$Builder.build(StaticTableSchema.java:255) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.createStaticTableSchema(BeanTableSchema.java:208) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:129) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:121) ~[dynamodb-enhanced-2.17.121.jar:na] at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromBean(TableSchema.java:81) ~[dynamodb-enhanced-2.17.121.jar:na] at com.example.dynamodbdemo.EntityRepository.(EntityRepository.java:16) ~[classes/:na] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_312] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_312] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_312] at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_312] at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211) ~[spring-beans-5.3.14.jar:5.3.14] ... 34 common frames omitted

AWS Java SDK version used

dynamodb-enhanced version 2.17.121

JDK version used

openjdk version "1.8.0_312"

Operating System and version

Linux 5.4.0-91-generic #102-Ubuntu

marcogramy avatar Feb 01 '22 16:02 marcogramy

Hi @marcogramy, Similar to #2937, you seem to be missing the implementation methods for converting Optional variables.

Once, you define the converter class, the converter provider would work as intended using the custom converter resolving the issue.

Hope this helps!

aaoswal avatar Feb 01 '22 18:02 aaoswal

Hi @aaoswal, thank you for your reply! Unfortunately my code dosn't work, reporting the specified error.

I've updated my repo, following your guidelines; here you can find, from branch HashMap1, the first implementation using OptionalAttributeConverter that uses my custom HashMapAttributeConverter class.

Here, from branch HashMap2, the second implementation, using MapAttributeConverter from AWS SDK. In both cases you can see the error while the project is starting up.

Starting project in debug mode I see that my custom Provider contains a convert for EnhancedType(java.util.Optional<java.util.Map>) but the EnhancedType required is EnhancedType(java.util.Optional<java.util.Map<java.lang.String, java.lang.String>>) This generates the error.

Thank you again for your help!

marcogramy avatar Feb 03 '22 11:02 marcogramy

Hi @marcogramy, I apologize but the issue is not reproducible on my end. After running the code provided, it seems to be running successfully without any errors in both cases.

Is there any other changes that are expected on my end before trying to reproduce the issue?

aaoswal avatar Feb 03 '22 19:02 aaoswal

Hi @aaoswal, which version of java are you using to running it? Thanks

davidemarrone avatar Feb 04 '22 07:02 davidemarrone

Hi @davidemarrone, Java 1.8.0_332 is the version I am currently running.

aaoswal avatar Feb 07 '22 10:02 aaoswal

Hi @aaoswal, is quite strange that the same code works for you, I have just done a test with a new test machine, in attachment my output, I have tested it also on a newer version of java but still doesn't works. regards output.txt

davidemarrone avatar Feb 07 '22 11:02 davidemarrone

Hi @aaoswal, these are the commands to reproduce: git clone https://github.com/marcogramy/dynamodbdemo.git Go inside the project folder: cd dynamodbdemo git checkout HashMap1 mvn clean install cd target java -jar dynamodbdemo-0.0.1-SNAPSHOT.jar Error should appear while project is starting up. What is the behaviour on your side?

Thank you again!

marcogramy avatar Feb 10 '22 09:02 marcogramy

@debora-ito do you think you can help us with this issue? Thank you

davidemarrone avatar Feb 15 '22 09:02 davidemarrone

I see the error but still not sure why it's happening. Still investigating. Have you tried using the provided MapAttributeConverter instead of the custom HashMapAttributeConverter?

debora-ito avatar Feb 24 '22 19:02 debora-ito

@debora-ito Yes, I've tried. You can see in my repo, branch HashMap2 here: https://github.com/marcogramy/dynamodbdemo/tree/HashMap2

Please tell me if this works or throws the same error, thank you!

marcogramy avatar Feb 25 '22 11:02 marcogramy

I discovered the issue is with OptionalAttributeConverter.type(), which calls EnhancedType.optionalOf(this.delegate.type().rawClass()). rawClass() does not contain the generics information, so this is a type erasure bug that causes it to lose all the generic type information for Map.

The workaround I used is to extract a java.reflect.type via https://stackoverflow.com/a/48155403, create an EnhancedType using it via EnhancedType.of(type), and then check against that EnhancedType during AttributeConverterProvider.converterFor()

sgao-1649714433 avatar Apr 11 '22 22:04 sgao-1649714433