GoogleCloudPlatform / spring-cloud-gcp

New home for Spring Cloud GCP development starting with version 2.0.
Apache License 2.0
419 stars 312 forks source link

@EnableSpannerAuditing fails if saved entity contains UUID #3143

Open Klompara opened 2 months ago

Klompara commented 2 months ago

Runtime Java 21 Spring Boot 3.3.2 spring-cloud-gcp-dependencies 5.5.0

Describe the bug If a Spanner Entity contains UUID as a primary key and Spanner auditing is configured, repository save will throw a InaccessibleObjectException Spring framework uses reflection in order to make jdk internal fields accessible. This is disabled since JDK 16 and throws this exception. Only occurs if Spanner Auditing is enabled.

Stacktrace

java.lang.reflect.InaccessibleObjectException: Unable to make field private final long java.util.UUID.mostSigBits accessible: module java.base does not "opens java.util" to unnamed module @4f9a3314
        at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:391)
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:367)
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:315)
        at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:183)
        at java.base/java.lang.reflect.Field.setAccessible(Field.java:177)
        at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:804)
        at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:616)
        at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:728)
        at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:471)
        at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:424)
        at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:320)
        at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:334)
        at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:97)
        at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:147)
        at org.springframework.data.mapping.context.PersistentPropertyPathFactory.lambda$from$2(PersistentPropertyPathFactory.java:259)
        at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:321)
        at org.springframework.data.mapping.context.PersistentPropertyPathFactory.from(PersistentPropertyPathFactory.java:264)
        at org.springframework.data.mapping.context.PersistentPropertyPathFactory.from(PersistentPropertyPathFactory.java:235)
        at org.springframework.data.mapping.context.PersistentPropertyPathFactory.from(PersistentPropertyPathFactory.java:166)
        at org.springframework.data.mapping.context.AbstractMappingContext.doFindPersistentPropertyPaths(AbstractMappingContext.java:374)
        at org.springframework.data.mapping.context.AbstractMappingContext.findPersistentPropertyPaths(AbstractMappingContext.java:358)
        at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory$MappingAuditingMetadata.findPropertyPaths(MappingAuditableBeanWrapperFactory.java:150)
        at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory$MappingAuditingMetadata.<init>(MappingAuditableBeanWrapperFactory.java:124)
        at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.lambda$getBeanWrapperFor$0(MappingAuditableBeanWrapperFactory.java:85)
        at java.base/java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:330)
        at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.lambda$getBeanWrapperFor$1(MappingAuditableBeanWrapperFactory.java:84)
        at org.springframework.data.mapping.context.PersistentEntities.lambda$mapOnContext$2(PersistentEntities.java:144)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
        at java.base/java.util.Spliterators$ArraySpliterator.tryAdvance(Spliterators.java:1034)
        at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
        at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
        at org.springframework.data.mapping.context.PersistentEntities.mapOnContext(PersistentEntities.java:145)
        at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.lambda$getBeanWrapperFor$3(MappingAuditableBeanWrapperFactory.java:82)
        at java.base/java.util.Optional.flatMap(Optional.java:289)
        at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.getBeanWrapperFor(MappingAuditableBeanWrapperFactory.java:76)
        at org.springframework.data.auditing.AuditingHandlerSupport.touch(AuditingHandlerSupport.java:132)
        at org.springframework.data.auditing.AuditingHandlerSupport.markModified(AuditingHandlerSupport.java:127)
        at org.springframework.data.auditing.AuditingHandler.markModified(AuditingHandler.java:98)
        at java.base/java.util.Collections$SingletonList.forEach(Collections.java:5186)
        at com.google.cloud.spring.data.spanner.repository.support.SpannerAuditingEventListener.onApplicationEvent(SpannerAuditingEventListener.java:43)
        at com.google.cloud.spring.data.spanner.repository.support.SpannerAuditingEventListener.onApplicationEvent(SpannerAuditingEventListener.java:28)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:452)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:385)
        at com.google.cloud.spring.data.spanner.core.SpannerTemplate.maybeEmitEvent(SpannerTemplate.java:732)
        at com.google.cloud.spring.data.spanner.core.SpannerTemplate.applySaveMutations(SpannerTemplate.java:380)
        at com.google.cloud.spring.data.spanner.core.SpannerTemplate.upsert(SpannerTemplate.java:348)
        at com.google.cloud.spring.data.spanner.repository.support.SimpleSpannerRepository.save(SimpleSpannerRepository.java:81)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        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.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.$Proxy115.save(Unknown Source)
ldetmer commented 2 months ago

I was able to repro this by adding @EnableSpannerAuditing to our exist spanner test + adding UUIDs. I will have the team look into this and see how we can help. You could also try reaching out to Spring as it seems like this is an issue with their auditing handling.