From 65dbfd09b4f298a44dd49a45e8027507e03069b0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 18 Jun 2024 18:31:15 +0200 Subject: [PATCH] Defensive PersistenceExceptionTranslator bean retrieval on shutdown Closes gh-33067 --- ...stenceExceptionTranslationInterceptor.java | 12 +++++-- ...eExceptionTranslationInterceptorTests.java | 31 ++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java b/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java index e20bb7aff85..5844eb1c70c 100644 --- a/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java +++ b/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanCreationNotAllowedException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; @@ -146,7 +147,14 @@ public class PersistenceExceptionTranslationInterceptor if (translator == null) { Assert.state(this.beanFactory != null, "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory"); - translator = detectPersistenceExceptionTranslators(this.beanFactory); + try { + translator = detectPersistenceExceptionTranslators(this.beanFactory); + } + catch (BeanCreationNotAllowedException ex2) { + // Cannot create PersistenceExceptionTranslator bean on shutdown: + // fall back to rethrowing original exception without translation + throw ex; + } this.persistenceExceptionTranslator = translator; } throw DataAccessUtils.translateIfNecessary(ex, translator); diff --git a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java index fae53e1e7c5..a8dc874fdf0 100644 --- a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java +++ b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java @@ -18,6 +18,7 @@ package org.springframework.dao.annotation; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import org.aopalliance.intercept.MethodInvocation; import org.junit.jupiter.api.Test; @@ -29,6 +30,7 @@ import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.dao.DataAccessException; +import org.springframework.dao.support.ChainedPersistenceExceptionTranslator; import org.springframework.dao.support.PersistenceExceptionTranslationInterceptor; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.stereotype.Repository; @@ -78,10 +80,37 @@ class PersistenceExceptionTranslationInterceptorTests extends PersistenceExcepti given(invocation.proceed()).willThrow(exception); assertThatThrownBy(() -> interceptor.invoke(invocation)).isSameAs(exception); - assertThat(callOrder).containsExactly(10, 20, 30); } + @Test + void detectPersistenceExceptionTranslatorsOnShutdown() throws Throwable { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); + bf.registerBeanDefinition("peti", new RootBeanDefinition(PersistenceExceptionTranslationInterceptor.class)); + bf.registerBeanDefinition("pet", new RootBeanDefinition(ChainedPersistenceExceptionTranslator.class)); + + PersistenceExceptionTranslationInterceptor interceptor = + bf.getBean("peti", PersistenceExceptionTranslationInterceptor.class); + interceptor.setAlwaysTranslate(true); + + RuntimeException exception = new RuntimeException(); + MethodInvocation invocation = mock(); + given(invocation.proceed()).willThrow(exception); + + AtomicBoolean correctException = new AtomicBoolean(false); + bf.registerDisposableBean("disposable", () -> { + try { + interceptor.invoke(invocation); + } + catch (Throwable ex) { + correctException.set(ex == exception); + } + }); + bf.destroySingletons(); + assertThat(correctException).isTrue(); + } + private static class CallOrderAwareExceptionTranslator implements PersistenceExceptionTranslator, Ordered {