DynamoDb error with Optional<Map<String, String>> field
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
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!
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!
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?
Hi @aaoswal, which version of java are you using to running it? Thanks
Hi @davidemarrone,
Java 1.8.0_332 is the version I am currently running.
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
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!
@debora-ito do you think you can help us with this issue? Thank you
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 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!
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()