Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] NPE when reading the resulting object after SimpleCosmosRepository.save #43755

Open
G0dC0der opened this issue Jan 10, 2025 · 5 comments
Open
Assignees
Labels
Client This issue points to a problem in the data-plane of the library. Cosmos customer-reported Issues that are reported by GitHub users external to the Azure organization. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that Service Attention Workflow: This issue is responsible by Azure service team.

Comments

@G0dC0der
Copy link

G0dC0der commented Jan 10, 2025

Describe the bug
I have a repository class:

import com.azure.spring.data.cosmos.repository.CosmosRepository;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Repository;

@Repository
public interface OrganizationRepository extends CosmosRepository<OrganizationEntity, String> {

    @Override
    @NotNull List<OrganizationEntity> findAll();

}

And I am calling the save method, defined in org.springframework.data.repository.CrudRepository. This results in a call to SimpleCosmosRepository, and eventually CosmosTemplate.upsertAndReturnEntity. This method(upsertAndReturnEntity) does mainly two things:

  1. Save the object in the database
  2. Create the response object based on the DB response(i e the return object after calling save).

The first part works fine, but the second part yields null pointer exeception, because CosmosItemResponse.getItem() returns null. This is not surprising, because the CosmosItemRequestOptions used does not set contentResponseOnWriteEnabled to true.

Exception or Stack Trace

Caused by: java.lang.NullPointerException: Cannot invoke "com.fasterxml.jackson.databind.JsonNode.isValueNode()" because "jsonNode" is null
	at com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter.readInternal(MappingCosmosConverter.java:90)
	at com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter.read(MappingCosmosConverter.java:79)
	at com.azure.spring.data.cosmos.core.CosmosTemplate.toDomainObject(CosmosTemplate.java:1303)
	at com.azure.spring.data.cosmos.core.CosmosTemplate.upsertAndReturnEntity(CosmosTemplate.java:553)
	at com.azure.spring.data.cosmos.repository.support.SimpleCosmosRepository.save(SimpleCosmosRepository.java:106)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354)

To Reproduce
Extend the interface CosmosRepository with your own, and save any object using using save(object).

Code Snippet

            organizationRepository.save(organizationEntity);

Expected behavior
I expect to get an object of OrganizationEntity and not a NPE.

Screenshots
n/a

Setup (please complete the following information):

  • OS: MacOS
  • IDE: IntelliJ
  • Library/Libraries: com.azure:azure-spring-data-cosmos:5.19.0
  • Java version: 21
  • App Server/Environment: Tomcat
  • Frameworks: Spring Boot 3.3.2

Question
Is there something I am not understanding? I feel like this shouldn't happen, but at the same time, I have a hard time believing this was missed during testing.

Additional Info
This is where the problem lies:
https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/spring/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/core/CosmosTemplate.java#L537
If you put a breakpoint, and edit options to have contentResponseOnWriteEnabled set to true, the problem get solved.

Workaround
I can use the save method defined in CosmosRepository, where we can customise the options. However, this is verbose and inconvenient. I. think CosmosTemplate.upsertAndReturnEntity should always set contentResponseOnWriteEnabled to true.

Information Checklist
Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report

  • [X ] Bug Description Added
  • [ X] Repro Steps Added
  • [ X] Setup information Added
@github-actions github-actions bot added Client This issue points to a problem in the data-plane of the library. Cosmos customer-reported Issues that are reported by GitHub users external to the Azure organization. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that Service Attention Workflow: This issue is responsible by Azure service team. labels Jan 10, 2025
Copy link

Thanks for the feedback! We are routing this to the appropriate team for follow-up. cc @kushagraThapar @pjohari-ms @TheovanKraay.

@kushagraThapar
Copy link
Member

@trande4884 can you please take a look at this issue, thanks!

@trande4884 trande4884 self-assigned this Jan 10, 2025
@trande4884
Copy link
Member

@G0dC0der can you please put the line of code fully as to how you are calling save and then where you are trying to retrieve the item where it throws the error? Thanks!

@trande4884
Copy link
Member

trande4884 commented Jan 10, 2025

@G0dC0der whatever the entity type you are saving should be the return type of the .save(), you shouldn't need to call .getItem() I believe, below you can see how save() can be used and the response is the item:

final AuditableEntity entity = new AuditableEntity();
final AuditableEntity savedEntity = auditableRepository.save(entity);

@G0dC0der
Copy link
Author

It's the call to the save method that yields a NPE. I use it exactly as you do in your example. I of course populate the fields of the entity before I dod that.

@Container(containerName = "organization")
public final class OrganizationEntity {

    @Id
    @PartitionKey
    @JsonProperty("id")
    private String databaseName;
    private Integer organizationNumber;
    private String contactPersonEmail;
    private String contactPersonPhone;
    private AccessLevel accessLevel;
    private String bucket;
    @Version
    private String _etag;

     //Getters and setters
}

@Repository
public interface OrganizationRepository extends CosmosRepository<OrganizationEntity, String> {
    @Override  @NotNull List<OrganizationEntity> findAll();
}

//Somehwere in a method:
organizationRepository.save(organizationEntity); //NPE: Cannot invoke "com.fasterxml.jackson.databind.JsonNode.isValueNode()" because "jsonNode" is null

Full stacktrace

Caused by: java.lang.NullPointerException: Cannot invoke "com.fasterxml.jackson.databind.JsonNode.isValueNode()" because "jsonNode" is null
	at com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter.readInternal(MappingCosmosConverter.java:90)
	at com.azure.spring.data.cosmos.core.convert.MappingCosmosConverter.read(MappingCosmosConverter.java:79)
	at com.pck.cms.persistence.CosmosMapper.read(CosmosMapper.java:25)
	at com.azure.spring.data.cosmos.core.CosmosTemplate.toDomainObject(CosmosTemplate.java:1303)
	at com.azure.spring.data.cosmos.core.CosmosTemplate.upsertAndReturnEntity(CosmosTemplate.java:553)
	at com.azure.spring.data.cosmos.repository.support.SimpleCosmosRepository.save(SimpleCosmosRepository.java:106)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:277)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158)
	at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516)
	at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:168)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
	at jdk.proxy2/jdk.proxy2.$Proxy144.save(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
	at jdk.proxy2/jdk.proxy2.$Proxy144.save(Unknown Source)
	at com.pck.cms.api.internal.organization.service.SysAdminService.lambda$onboardOrganization$0(SysAdminService.java:68)

The last line of this stacktrace is my own code, and it calls OrganizationRepository.save(object), as seen in the line above:
jdk.proxy2/jdk.proxy2.$Proxy144.save.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Client This issue points to a problem in the data-plane of the library. Cosmos customer-reported Issues that are reported by GitHub users external to the Azure organization. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that Service Attention Workflow: This issue is responsible by Azure service team.
Projects
None yet
Development

No branches or pull requests

3 participants