aws / aws-sdk-java-v2

The official AWS SDK for Java - Version 2
Apache License 2.0
2.13k stars 810 forks source link

DynamoDB Enhanced Client shouldn't assume every method is a property #3916

Open chrylis opened 1 year ago

chrylis commented 1 year ago

Describe the bug

When using @DynamoDbImmutable, the mapper throws an exception if any custom non-property method exists.

In the specific case of Groovy's @groovy.transform.EqualsAndHashCode, an entirely innocent method @Generated public boolean canEqual(Object) is created, and the mapper throws. Note that as this is a generated method, I don't even have the opportunity to annotate it.

Expected Behavior

I expected a plain (generated) method that does not map as a JavaBeans getter to be ignored by the mapper.

Current Behavior

java.lang.IllegalArgumentException: A method was found on the immutable class that does not appear to be a valid getter due to it having one or more parameters. Use the @DynamoDbIgnore annotation on the method if you do not want it to be included in the TableSchema introspection. [Method = "public boolean com.example.MyRecord.canEqual(java.lang.Object)"]
    at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.generateExceptionForMethod(ImmutableIntrospector.java:134)
    at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.validateGetter(ImmutableIntrospector.java:156)
    at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.lambda$introspect$2(ImmutableIntrospector.java:82)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.introspect(ImmutableIntrospector.java:93)
    at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.getImmutableInfo(ImmutableIntrospector.java:65)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.createStaticImmutableTableSchema(ImmutableTableSchema.java:168)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.create(ImmutableTableSchema.java:139)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.recursiveCreate(ImmutableTableSchema.java:162)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.convertTypeToEnhancedType(BeanTableSchema.java:301)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.staticAttributeBuilder(BeanTableSchema.java:250)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.lambda$createStaticTableSchema$2(BeanTableSchema.java:205)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.createStaticTableSchema(BeanTableSchema.java:193)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:138)
    at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:129)
    at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromBean(TableSchema.java:83)
    at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromClass(TableSchema.java:126)

Reproduction Steps

@DynamoDbImmutable(builder = Builder)
@EqualsAndHashCode
class MyRecord {
  String channel

  static class Builder {
    String channel
    MyRecord build() { new MyRecord(channel) }
  }

Possible Solution

ImmutableIntrospector should not assume in filterAndCollectGetterMethods and isMappableMethod that every method is a property getter.

Additional Information/Context

No response

AWS Java SDK version used

2.19.25

JDK version used

openjdk version "11.0.11" 2021-04-20 OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 (build 11.0.11+9) OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode)

Operating System and version

Gentoo Linux

debora-ito commented 1 year ago

Does it work if you add @DynamoDbIgnore to the method?

chrylis commented 1 year ago

As noted, I cannot add @DynamoDbIgnore to the method; it is generated code.

debora-ito commented 1 year ago

I apologize, missed that detail in the description.

Request acknowledged.

Community note: please vote by adding a 👍 reaction to the original issue to help us prioritize this request.

jpdev01 commented 11 months ago

Are you using Groovy for entity modeling? Does it work?

https://github.com/aws/aws-sdk-java-v2/issues/1954