Describe the bug Cause a recursive call on a method getCurrentAuditor, then cause a stack overflow and internal 500 error server. I'm using keycloak as OAuth server and trying to create an audition in my program, but for some reason, this method is being recursive and causing a stack overflow when I try to update some entity, but in creation, this works fine.
@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
class AppApplication {
@Bean
fun auditorAware(userRepository: UserRepository): AuditorAware<User> {
return AuditorAwareImpl(userRepository)
}
}
fun main(args: Array<String>) {
runApplication<AppApplication>(*args)
}
@Component
@Configuration
public class AuditorAwareImpl implements AuditorAware<User> {
private final UserRepository userRepository;
public AuditorAwareImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@NotNull
@Override
public Optional<User> getCurrentAuditor() {
Authentication authentication = getAuthenticationStatic();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.empty();
}
return userRepository.findByEmail(authentication.getName());
}
public static Authentication getAuthenticationStatic() {
return SecurityContextHolder
.getContext()
.getAuthentication();
}
}
Of course, I have an entity with auditable properties like
@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
abstract class BaseAuditingEntity<T> : BaseEntity<T>() {
@CreatedBy
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "created_by", updatable = false)
var createdBy: User? = null
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_date", updatable = false, nullable = false)
var createdDate: Date? = null
@LastModifiedBy
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "last_modified_by")
var lastModifiedBy: User? = null
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
var lastModifiedDate: Date? = null
}
@Entity
@Table(name = "teste", schema = "vis")
@EntityListeners(AuditingEntityListener::class)
class TesteEntity : BaseAuditingEntity<Long>() {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
override var id: Long? = 0
@Column(name = "nome", nullable = false)
var nome: String? = null
}
Stack trace
2023-05-23T17:50:08.154-03:00 DEBUG 29744 --- [nio-8081-exec-1] o.s.security.web.FilterChainProxy : Securing PUT /teste/8
2023-05-23T17:50:08.157-03:00 DEBUG 29744 --- [nio-8081-exec-1] o.s.s.o.s.r.a.JwtAuthenticationProvider : Authenticated token
2023-05-23T17:50:08.157-03:00 DEBUG 29744 --- [nio-8081-exec-1] .s.r.w.a.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@6d033d9f, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[SCOPE_email, ROLE_admin, SCOPE_profile]]
2023-05-23T17:50:08.158-03:00 DEBUG 29744 --- [nio-8081-exec-1] o.s.security.web.FilterChainProxy : Secured PUT /teste/8
2023-05-23T17:50:08.162-03:00 DEBUG 29744 --- [nio-8081-exec-1] org.hibernate.SQL : select u1_0.id,u1_0.active,u1_0.cpf,u1_0.created_at,u1_0.email,u1_0.name,u1_0.photo,u1_0.keycloak_id from vis.user_vis u1_0 where u1_0.email=?
Hibernate: select u1_0.id,u1_0.active,u1_0.cpf,u1_0.created_at,u1_0.email,u1_0.name,u1_0.photo,u1_0.keycloak_id from vis.user_vis u1_0 where u1_0.email=?
2023-05-23T17:50:08.164-03:00 DEBUG 29744 --- [nio-8081-exec-1] org.hibernate.SQL : select r1_0.user_id,r1_1.id,r1_1.name from vis.role_user r1_0 join vis.role r1_1 on r1_1.id=r1_0.role_id where r1_0.user_id=?
Hibernate: select r1_0.user_id,r1_1.id,r1_1.name from vis.role_user r1_0 join vis.role r1_1 on r1_1.id=r1_0.role_id where r1_0.user_id=?
2023-05-23T17:50:08.166-03:00 DEBUG 29744 --- [nio-8081-exec-1] org.hibernate.SQL : select t1_0.id,t1_0.created_by,t1_0.created_date,t1_0.last_modified_by,t1_0.last_modified_date,t1_0.nome from vis.teste t1_0 where t1_0.id=?
Hibernate: select t1_0.id,t1_0.created_by,t1_0.created_date,t1_0.last_modified_by,t1_0.last_modified_date,t1_0.nome from vis.teste t1_0 where t1_0.id=?
2023-05-23T17:50:08.238-03:00 ERROR 29744 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/api/v1] threw exception [Request processing failed: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction] with root cause
java.lang.StackOverflowError: null
at org.hibernate.engine.internal.StatefulPersistenceContext.getNaturalIdResolutions(StatefulPersistenceContext.java:1922) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.engine.internal.StatefulPersistenceContext.setFlushing(StatefulPersistenceContext.java:1171) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:97) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:48) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1388) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:111) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:335) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:276) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:571) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:363) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1073) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.spi.AbstractSelectionQuery.getSingleResult(AbstractSelectionQuery.java:457) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.QuerySqmImpl.getSingleResult(QuerySqmImpl.java:1103) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:193) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:148) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:136) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:77) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:134) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:94) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) ~[spring-aop-6.0.8.jar:6.0.8]
at jdk.proxy4/jdk.proxy4.$Proxy160.findByEmail(Unknown Source) ~[na:na]
at br.com.vis.appvis.config.AuditorAwareImpl.getCurrentAuditor(AuditorAwareImpl.java:36) ~[main/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:577) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:216) ~[spring-aop-6.0.8.jar:6.0.8]
at jdk.proxy4/jdk.proxy4.$Proxy179.getCurrentAuditor(Unknown Source) ~[na:na]
at java.base/java.util.Optional.map(Optional.java:260) ~[na:na]
at org.springframework.data.auditing.AuditingHandler.getAuditor(AuditingHandler.java:103) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.auditing.AuditingHandler.markModified(AuditingHandler.java:98) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.jpa.domain.support.AuditingEntityListener.touchForUpdate(AuditingEntityListener.java:112) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:577) ~[na:na]
at org.hibernate.jpa.event.internal.ListenerCallback.performCallback(ListenerCallback.java:55) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.jpa.event.internal.CallbackRegistryImpl.callback(CallbackRegistryImpl.java:96) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.jpa.event.internal.CallbackRegistryImpl.preUpdate(CallbackRegistryImpl.java:70) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.invokeInterceptor(DefaultFlushEntityEventListener.java:342) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.handleInterception(DefaultFlushEntityEventListener.java:317) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:242) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:139) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:214) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:90) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:48) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1388) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:111) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:335) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:276) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:571) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:363) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1073) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.spi.AbstractSelectionQuery.getSingleResult(AbstractSelectionQuery.java:457) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.query.sqm.internal.QuerySqmImpl.getSingleResult(QuerySqmImpl.java:1103) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:193) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:148) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:136) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:77) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:134) ~[spring-data-jpa-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:94) ~[spring-data-commons-3.0.5.jar:3.0.5]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.8.jar:6.0.8]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) ~[spring-aop-6.0.8.jar:6.0.8]
at jdk.proxy4/jdk.proxy4.$Proxy160.findByEmail(Unknown Source) ~[na:na]
at br.com.vis.appvis.config.AuditorAwareImpl.getCurrentAuditor(AuditorAwareImpl.java:36) ~[main/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:577) ~[na:na]
at
Expected behavior Expect to instantiate my user entity and save this on the database
Comment From: marcusdacoregio
Hi @kaioenzo, thanks for the report.
Are you able to provide a sample that reproduces the behavior? Ideally, that sample should use something like H2 or Testcontainers to facilitate the setup.
Your configuration looks a bit strange to me, you are defining the AuditorAwareImpl as a @Bean but that class is already a bean since you annotated it with @Component. Can you try removing the method from the main class:
@Bean
fun auditorAware(userRepository: UserRepository): AuditorAware<User> {
return AuditorAwareImpl(userRepository)
}
And also remove the @Configuration from AuditorAwareImpl and see if it works?
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.