Skip to content

Enhanced DynamoDB NPE on putItem with null map value #2282

Open

Description

@xstex

A null pointer exception is hit in the dynamoDB enhanced client when performing a put of a map attribute with a null value.
This happens only when using a beanschema, not a staticschema.

Describe the bug

public class SDKTest {

  public static final StaticTableSchema<Chapter> CHAPTER_SCHEMA = StaticTableSchema.builder(Chapter.class)
    .newItemSupplier(Chapter::new)
    .addAttribute(Integer.class, a -> a.name("page").getter(Chapter::getPage).setter(Chapter::setPage))
    .addAttribute(String.class, a -> a.name("text").getter(Chapter::getText).setter(Chapter::setText))
    .build();

  public static final StaticTableSchema<Book> BOOK_SCHEMA = StaticTableSchema.builder(Book.class)
    .newItemSupplier(Book::new)
    .addAttribute(String.class, a -> a.name("id").tags(primaryPartitionKey()).getter(Book::getId).setter(Book::setId))
    .addAttribute(EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.documentOf(Chapter.class, CHAPTER_SCHEMA)),
      a -> a.name("map").getter(Book::getChapters).setter(Book::setChapters))
    .build();

  @DynamoDbBean
  public static class Book {
    private String id;
    private Map<String, Chapter> chapters;

    public Book() {}
    public Book(String id) { setId(id); }

    @DynamoDbPartitionKey public String getId() {  return id; }
    public void setId(String id) {  this.id = id; }

    public Map<String, Chapter> getChapters() { return chapters; }
    public void setChapters(Map<String, Chapter> chapters) {  this.chapters = chapters;  }
  }

  @DynamoDbBean
  public static class Chapter {
    private Integer page;
    private String text;

    public Integer getPage() {  return page;  }
    public void setPage(Integer page) {  this.page = page;  }

    public String getText() {  return text;  }
    public void setText(String text) {  this.text = text;  }
  }

  public void testStatic(DynamoDbClient client, DynamoDbEnhancedClient enhancedClient) throws Exception {
    DynamoDbTable<Book> table = enhancedClient.table("books", BOOK_SCHEMA);
    table.createTable();

    client.putItem(b -> b.tableName("books")
      .item(new HashMap<String, AttributeValue>() {{
        put("id", AttributeValue.builder().s("123").build());
        put("chapters", AttributeValue.builder()
          .m(Collections.singletonMap("First", AttributeValue.builder().nul(true).build()))
          .build());
      }}));

    Book item = table.getItem(new Book("123"));
    table.putItem(item);
  }

  public void testBean(DynamoDbClient client, DynamoDbEnhancedClient enhancedClient) throws Exception {
    DynamoDbTable<Book> table = enhancedClient.table("books", TableSchema.fromBean(Book.class));
    table.createTable();

    client.putItem(b -> b.tableName("books")
      .item(new HashMap<String, AttributeValue>() {{
        put("id", AttributeValue.builder().s("123").build());
        put("chapters", AttributeValue.builder()
          .m(Collections.singletonMap("First", AttributeValue.builder().nul(true).build()))
          .build());
      }}));

    Book item = table.getItem(new Book("123"));
    table.putItem(item); // <------------------------------LINE THAT THROWS EXCEPTION
  }
}

Behavior

testStatic works, while testBean throws NPE with the following trace:

java.lang.NullPointerException
	at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.ResolvedImmutableAttribute.lambda$create$0(ResolvedImmutableAttribute.java:48)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.lambda$itemToMap$5(StaticImmutableTableSchema.java:491)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.itemToMap(StaticImmutableTableSchema.java:489)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.WrappedTableSchema.itemToMap(WrappedTableSchema.java:59)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.WrappedTableSchema.itemToMap(WrappedTableSchema.java:59)
	at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.DocumentAttributeConverter.transformFrom(DocumentAttributeConverter.java:47)
	at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.MapAttributeConverter$Delegate.lambda$toAttributeValue$0(MapAttributeConverter.java:165)
	at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.MapAttributeConverter$Delegate.toAttributeValue(MapAttributeConverter.java:165)
	at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.MapAttributeConverter.transformFrom(MapAttributeConverter.java:138)
	at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.MapAttributeConverter.transformFrom(MapAttributeConverter.java:76)
	at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.StaticAttributeType.objectToAttributeValue(StaticAttributeType.java:40)
	at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.ResolvedImmutableAttribute.lambda$create$0(ResolvedImmutableAttribute.java:49)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.lambda$itemToMap$5(StaticImmutableTableSchema.java:491)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.itemToMap(StaticImmutableTableSchema.java:489)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.WrappedTableSchema.itemToMap(WrappedTableSchema.java:59)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.WrappedTableSchema.itemToMap(WrappedTableSchema.java:59)
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.PutItemOperation.generateRequest(PutItemOperation.java:71)
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.PutItemOperation.generateRequest(PutItemOperation.java:40)
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.CommonOperation.execute(CommonOperation.java:113)
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.TableOperation.executeOnPrimaryIndex(TableOperation.java:59)
	at software.amazon.awssdk.enhanced.dynamodb.internal.client.DefaultDynamoDbTable.putItem(DefaultDynamoDbTable.java:179)
	at software.amazon.awssdk.enhanced.dynamodb.internal.client.DefaultDynamoDbTable.putItem(DefaultDynamoDbTable.java:187)
	at software.amazon.awssdk.enhanced.dynamodb.internal.client.DefaultDynamoDbTable.putItem(DefaultDynamoDbTable.java:192)
	at com.magicbinder.core.SDKTest.testBean

Your Environment

  • AWS Java SDK version used: 2.15.81
  • JDK version used: java 8
  • Operating System and version: macOs

Activity

  1. added
    bugThis issue is a bug.
    needs-triageThis issue or PR still needs to be triaged.
    on Feb 15, 2021
  2. debora-ito commented on Feb 17, 2021

    @debora-ito
    Member

    @xstex thank you for reaching out. I've run your code sample and I'm seeing two issues:

    1. When using the StaticTableSchema, the Book object obtained after getItem() does not contain a chapters map. That's why calling the DynamoDB Enhanced client putItem() does not throw any exception, but the new added book item only contains id and the "First" -> null map is lost.
    2. When using the Bean schema, the Book obtained after getItem() contains both id and chapters as expected, but calling putItem() is throwing a null pointer exception.

    I'm still investigating what can be the cause.

  3. added
    investigatingThis issue is being investigated and/or work is in progress to resolve the issue.
    and removed
    needs-triageThis issue or PR still needs to be triaged.
    on Feb 17, 2021
  4. removed
    investigatingThis issue is being investigated and/or work is in progress to resolve the issue.
    on Feb 24, 2021
  5. svenallers commented on May 21, 2021

    @svenallers

    We currently do have the same issue. Are there any updates on this topic?
    I think that I found sth. that could have caused the issue. When looking into the Delegate of the MapAttributeConverter, then it seems like that it is not considering that the value could be null in the toAttributeValue() function. I might be able to contribute a fix, but I probably won't find time to work on that topic before Tuesday

  6. matios13 commented on Mar 8, 2024

    @matios13

    Any progres on that bug?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.dynamodb-enhancedp3This is a minor priority issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions