DynamoDB enhanced client with Immutables - can't use @Value.Default and @DynamoDbIgnore on the same method
- Dominant language
- Java
- Stars
- 2.6k
- Forks
- 1k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 51
Description
### Describe the bug
Hi, I'm struggling with something that seems like it should be simple using the DDB enhanced client with the Immutables library. I'm trying to annotate a method with both `@Value.Default` and `@DynamoDbIgnore`, but I keep getting `java.lang.IllegalArgumentException: A method was found on the immutable class builder that does not appear to have a matching getter on the immutable class. Use the @DynamoDbIgnore annotation on the method if you do not want it to be included in the TableSchema introspection.` which seems strange since the method is already so annotated.
It seems like the issue is that the `@DynamoDbIgnore` causes the method to be filtered out on the immutable class in ImmutableIntrospector, but the annotation isn't present on the builder class method so it's not filtered out for the builder class methods, resulting in a mismatch.
Is this use case simply not supported? Using Immutables `2.10.1`.
### Regression Issue
- [ ] Select this option if this issue appears to be a regression.
### Expected Behavior
Successfully generate an Immutable DynamoDB table with a value that defaults to the return value if not set in the builder, and is ignored for DynamoDB persistence.
### Current Behavior
Got the following exception when instantiating the table:
```
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable arg1] in constructor [com.abc.ddb.managers.impl.DynamoDbHelperTest(software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable,software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable)]: A method was found on the immutable class builder that does not appear to have a matching getter on the immutable class. Use the @DynamoDbIgnore annotation on the method if you do not want it to be included in the TableSchema introspection. [Method = "public final com.abc.ddb.models.CustomerBuilder com.abc.ddb.models.CustomerBuilder.name(java.lang.String)"]
at app//org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameter(ParameterResolutionUtils.java:159)
at app//org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameters(ParameterResolutionUtils.java:103)
at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:59)
at app//org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestClassConstructor(ClassBasedTestDescriptor.java:363)
at app//org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateTestClass(ClassBasedTestDescriptor.java:310)
at app//org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:79)
at app//org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:286)
at app//org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:278)
at java.base@17.0.14/java.util.Optional.orElseGet(Optional.java:364)
at app//org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:277)
at app//org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:105)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:104)
at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:68)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)
at java.base@17.0.14/java.util.ArrayList.forEach(ArrayList.java:1511)
at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base@17.0.14/java.util.ArrayList.forEach(ArrayList.java:1511)
at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at app//org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63)
at java.base@17.0.14/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.14/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.14/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.14/java.lang.reflect.Method.invoke(Method.java:569)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92)
at jdk.proxy2/jdk.proxy2.$Proxy6.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by:
java.lang.IllegalArgumentException: A method was found on the immutable class builder that does not appear to have a matching getter on the immutable class. Use the @DynamoDbIgnore annotation on the method if you do not want it to be included in the TableSchema introspection. [Method = "public final com.abc.ddb.models.CustomerBuilder com.abc.ddb.models.CustomerBuilder.name(java.lang.String)"]
at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.generateExceptionForMethod(ImmutableIntrospector.java:134)
at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.introspect(ImmutableIntrospector.java:96)
at software.amazon.awssdk.enhanced.dynamodb.internal.immutable.ImmutableIntrospector.getImmutableInfo(ImmutableIntrospector.java:65)
at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.createStaticImmutableTableSchema(ImmutableTableSchema.java:204)
at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.create(ImmutableTableSchema.java:172)
at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.lambda$create$0(ImmutableTableSchema.java:140)
at java.base/java.util.Map.computeIfAbsent(Map.java:1054)
at java.base/java.util.Collections$SynchronizedMap.computeIfAbsent(Collections.java:2760)
at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.create(ImmutableTableSchema.java:139)
at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema.create(ImmutableTableSchema.java:161)
at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromImmutableClass(TableSchema.java:164)
at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromClass(TableSchema.java:204)
at com.amazon.ddblocal.DefaultTableSchemaProvider.getTableSchema(DefaultTableSchemaProvider.java:13)
at com.amazon.ddblocal.DynamoDbEnhancedLocalExtension.resolveParameter(DynamoDbEnhancedLocalExtension.java:129)
at org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameter(ParameterResolutionUtils.java:136)
... 72 more
```
### Reproduction Steps
```java
import org.immutables.value.Value;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbIgnore;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;
@Value.Immutable
@Value.Style(
builtinContainerAttributes = false,
defaults = @Value.Immutable(copy = false),
depluralize = true,
get = {"get*", "has*", "is*"},
implementationNestedInBuilder = true,
overshadowImplementation = true,
strictBuilder = true,
typeBuilder = "*Builder",
passAnnotations = {
// Copy all Enhanced Client annotations to the immutable class
DynamoDbAttribute.class,
DynamoDbIgnore.class,
DynamoDbPartitionKey.class,
DynamoDbSecondaryPartitionKey.class,
DynamoDbSecondarySortKey.class,
DynamoDbSortKey.class
})
@DynamoDbImmutable(builder = CustomerBuilder.class)
public interface Customer {
@DynamoDbPartitionKey
String getCustomerId();
@Value.Default
@DynamoDbIgnore
default String getName() {
return "";
}
}
```
### Possible Solution
_No response_
### Additional Information/Context
_No response_
### AWS Java SDK version used
2.30.32
### JDK version used
openjdk 17.0.14 2025-01-21 LTS
### Operating System and version
Amazon Linux 2
Contributor guide
Assessment
This issue has not been assessed yet.