diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java index 266735a42a..6b202d3112 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java @@ -20,11 +20,11 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.core.Constants; import org.springframework.lang.Nullable; import org.springframework.transaction.IllegalTransactionStateException; import org.springframework.transaction.InvalidTimeoutException; @@ -35,6 +35,7 @@ import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionSuspensionNotSupportedException; import org.springframework.transaction.UnexpectedRollbackException; +import org.springframework.util.Assert; /** * Abstract base class that implements Spring's standard transaction workflow, @@ -74,6 +75,7 @@ import org.springframework.transaction.UnexpectedRollbackException; * to Java serialization rules) if they need to restore any transient state. * * @author Juergen Hoeller + * @author Sam Brannen * @since 28.03.2003 * @see #setTransactionSynchronization * @see TransactionSynchronizationManager @@ -107,8 +109,15 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran public static final int SYNCHRONIZATION_NEVER = 2; - /** Constants instance for AbstractPlatformTransactionManager. */ - private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class); + /** + * Map of constant names to constant values for the transaction synchronization + * constants defined in this class. + */ + static final Map constants = Map.of( + "SYNCHRONIZATION_ALWAYS", SYNCHRONIZATION_ALWAYS, + "SYNCHRONIZATION_ON_ACTUAL_TRANSACTION", SYNCHRONIZATION_ON_ACTUAL_TRANSACTION, + "SYNCHRONIZATION_NEVER", SYNCHRONIZATION_NEVER + ); protected transient Log logger = LogFactory.getLog(getClass()); @@ -130,12 +139,18 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran /** * Set the transaction synchronization by the name of the corresponding constant - * in this class, e.g. "SYNCHRONIZATION_ALWAYS". + * in this class — for example, {@code "SYNCHRONIZATION_ALWAYS"}. * @param constantName name of the constant * @see #SYNCHRONIZATION_ALWAYS + * @see #SYNCHRONIZATION_ON_ACTUAL_TRANSACTION + * @see #SYNCHRONIZATION_NEVER + * @see #setTransactionSynchronization(int) */ public final void setTransactionSynchronizationName(String constantName) { - setTransactionSynchronization(constants.asNumber(constantName).intValue()); + Assert.hasText(constantName, "'constantName' must not be null or blank"); + Integer transactionSynchronization = constants.get(constantName); + Assert.notNull(transactionSynchronization, "Only transaction synchronization constants allowed"); + this.transactionSynchronization = transactionSynchronization; } /** diff --git a/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java b/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java index 387ca4c5ef..842f7508f1 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/support/TransactionSupportTests.java @@ -43,6 +43,7 @@ import static org.springframework.transaction.TransactionDefinition.ISOLATION_SE import static org.springframework.transaction.TransactionDefinition.PROPAGATION_MANDATORY; import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED; import static org.springframework.transaction.TransactionDefinition.PROPAGATION_SUPPORTS; +import static org.springframework.transaction.support.AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION; import static org.springframework.transaction.support.DefaultTransactionDefinition.PREFIX_ISOLATION; import static org.springframework.transaction.support.DefaultTransactionDefinition.PREFIX_PROPAGATION; @@ -264,6 +265,53 @@ class TransactionSupportTests { assertThat(template3).isEqualTo(template2); } + @Nested + class AbstractPlatformTransactionManagerConfigurationTests { + + private final AbstractPlatformTransactionManager tm = new TestTransactionManager(false, true); + + + @Test + void setTransactionSynchronizationNameToUnsupportedValues() { + assertThatIllegalArgumentException().isThrownBy(() -> tm.setTransactionSynchronizationName(null)); + assertThatIllegalArgumentException().isThrownBy(() -> tm.setTransactionSynchronizationName(" ")); + assertThatIllegalArgumentException().isThrownBy(() -> tm.setTransactionSynchronizationName("bogus")); + } + + /** + * Verify that the internal 'constants' map is properly configured for all + * SYNCHRONIZATION_ constants defined in {@link AbstractPlatformTransactionManager}. + */ + @Test + void setTransactionSynchronizationNameToAllSupportedValues() { + Set uniqueValues = new HashSet<>(); + streamSynchronizationConstants() + .forEach(name -> { + tm.setTransactionSynchronizationName(name); + int transactionSynchronization = tm.getTransactionSynchronization(); + int expected = AbstractPlatformTransactionManager.constants.get(name); + assertThat(transactionSynchronization).isEqualTo(expected); + uniqueValues.add(transactionSynchronization); + }); + assertThat(uniqueValues).hasSize(AbstractPlatformTransactionManager.constants.size()); + } + + @Test + void setTransactionSynchronization() { + tm.setTransactionSynchronization(SYNCHRONIZATION_ON_ACTUAL_TRANSACTION); + assertThat(tm.getTransactionSynchronization()).isEqualTo(SYNCHRONIZATION_ON_ACTUAL_TRANSACTION); + } + + + private static Stream streamSynchronizationConstants() { + return Arrays.stream(AbstractPlatformTransactionManager.class.getFields()) + .filter(ReflectionUtils::isPublicStaticFinal) + .map(Field::getName) + .filter(name -> name.startsWith("SYNCHRONIZATION_")); + } + + } + @Nested class TransactionTemplateConfigurationTests {