diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java index 93199684cea..e72ced10c1e 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java @@ -453,7 +453,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker () -> { CompletableFuture invokeResult = ((CompletableFuture) invokeOperation(invoker)); if (invokeResult == null) { - return null; + throw new IllegalStateException("Returned CompletableFuture must not be null: " + method); } return invokeResult.exceptionallyCompose(ex -> { invokeFailure.set(true); diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java index b57ac1828c7..565c35182d9 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java @@ -130,15 +130,14 @@ public class ReflectiveRuntimeHintsRegistrar { private ReflectiveProcessor instantiateClass(Class type) { try { - Constructor constructor = type.getDeclaredConstructor(); - ReflectionUtils.makeAccessible(constructor); - return constructor.newInstance(); + return ReflectionUtils.accessibleConstructor(type).newInstance(); } catch (Exception ex) { throw new IllegalStateException("Failed to instantiate " + type, ex); } } + private static class DelegatingReflectiveProcessor implements ReflectiveProcessor { private final Iterable processors; @@ -151,9 +150,10 @@ public class ReflectiveRuntimeHintsRegistrar { public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) { this.processors.forEach(processor -> processor.registerReflectionHints(hints, element)); } - } - private record Entry(AnnotatedElement element, ReflectiveProcessor processor) {} + + private record Entry(AnnotatedElement element, ReflectiveProcessor processor) { + } } diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PropertySourceProcessor.java b/spring-core/src/main/java/org/springframework/core/io/support/PropertySourceProcessor.java index 8922903985d..9b073381234 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PropertySourceProcessor.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PropertySourceProcessor.java @@ -18,7 +18,6 @@ package org.springframework.core.io.support; import java.io.FileNotFoundException; import java.io.IOException; -import java.lang.reflect.Constructor; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; @@ -147,9 +146,7 @@ public class PropertySourceProcessor { private static PropertySourceFactory instantiateClass(Class type) { try { - Constructor constructor = type.getDeclaredConstructor(); - ReflectionUtils.makeAccessible(constructor); - return constructor.newInstance(); + return ReflectionUtils.accessibleConstructor(type).newInstance(); } catch (Exception ex) { throw new IllegalStateException("Failed to instantiate " + type, ex); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java index 0298877a867..87b2b6f0a25 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java @@ -71,6 +71,11 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException "44" // With check violation ); + private static final Set PESSIMISTIC_LOCKING_FAILURE_CODES = Set.of( + "40", // Transaction rollback + "61" // Oracle: deadlock + ); + private static final Set DATA_ACCESS_RESOURCE_FAILURE_CODES = Set.of( "08", // Connection exception "53", // PostgreSQL: insufficient resources (for example, disk full) @@ -85,11 +90,6 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException "S1" // DB2: communication failure ); - private static final Set PESSIMISTIC_LOCKING_FAILURE_CODES = Set.of( - "40", // Transaction rollback - "61" // Oracle: deadlock - ); - private static final Set DUPLICATE_KEY_ERROR_CODES = Set.of( 1, // Oracle 301, // SAP HANA @@ -117,18 +117,21 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException } return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex); } - else if (DATA_ACCESS_RESOURCE_FAILURE_CODES.contains(classCode)) { - return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex); - } - else if (TRANSIENT_DATA_ACCESS_RESOURCE_CODES.contains(classCode)) { - return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex); - } else if (PESSIMISTIC_LOCKING_FAILURE_CODES.contains(classCode)) { if (indicatesCannotAcquireLock(sqlState)) { return new CannotAcquireLockException(buildMessage(task, sql, ex), ex); } return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex); } + else if (DATA_ACCESS_RESOURCE_FAILURE_CODES.contains(classCode)) { + if (indicatesQueryTimeout(sqlState)) { + return new QueryTimeoutException(buildMessage(task, sql, ex), ex); + } + return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex); + } + else if (TRANSIENT_DATA_ACCESS_RESOURCE_CODES.contains(classCode)) { + return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex); + } } // For MySQL: exception class name indicating a timeout? @@ -183,4 +186,13 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException return "40001".equals(sqlState); } + /** + * Check whether the given SQL state indicates a {@link QueryTimeoutException}, + * with SQL state 57014 as a specific indication. + * @param sqlState the SQL state value + */ + static boolean indicatesQueryTimeout(@Nullable String sqlState) { + return "57014".equals(sqlState); + } + } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java index 34b26e45915..71820d7259d 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java @@ -27,6 +27,7 @@ import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.PessimisticLockingFailureException; +import org.springframework.dao.QueryTimeoutException; import org.springframework.dao.TransientDataAccessResourceException; import org.springframework.jdbc.BadSqlGrammarException; @@ -109,6 +110,11 @@ class SQLStateSQLExceptionTranslatorTests { assertTranslation("40001", CannotAcquireLockException.class); } + @Test + void translateQueryTimeout() { + assertTranslation("57014", QueryTimeoutException.class); + } + @Test void translateUncategorized() { assertTranslation("00000000", null);