From f22744c748665b11cb25182325e549a574d0632d Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 28 Dec 2016 20:53:20 -0800 Subject: [PATCH] Add support for TransactionManagerCustomizers Add a `TransactionManagerCustomizer` callback interface that can be used to customize auto-configured `PlatformTransactionManagers`. Also update `...transaction.*` properties under a single unified `spring.transaction...` key since the existing auto-configurations would often share a transaction manager (the technology specific transaction managers are `@ConditionalOnMissingBean` and may use a manager created by a previous auto-configuration). See gh-7561 --- .../batch/BasicBatchConfigurer.java | 22 +++++- .../batch/BatchAutoConfiguration.java | 12 ++- .../neo4j/Neo4jDataAutoConfiguration.java | 16 +++- .../jdbc/DataSourceProperties.java | 9 --- ...ceTransactionManagerAutoConfiguration.java | 13 +++- .../jpa/HibernateJpaAutoConfiguration.java | 7 +- .../orm/jpa/JpaBaseConfiguration.java | 12 ++- .../autoconfigure/orm/jpa/JpaProperties.java | 8 -- .../PlatformTransactionManagerCustomizer.java | 37 +++++++++ .../TransactionAutoConfiguration.java | 37 ++++++--- .../TransactionManagerCustomizers.java | 61 +++++++++++++++ .../transaction/TransactionProperties.java | 15 ++-- .../jta/AtomikosJtaConfiguration.java | 13 +++- .../jta/BitronixJtaConfiguration.java | 13 +++- .../transaction/jta/JndiJtaConfiguration.java | 18 +++-- .../transaction/jta/JtaProperties.java | 9 --- .../jta/NarayanaJtaConfiguration.java | 13 +++- .../batch/BatchAutoConfigurationTests.java | 23 ++++-- .../Neo4jDataAutoConfigurationTests.java | 7 +- ...nsactionManagerAutoConfigurationTests.java | 26 ++++--- .../AbstractJpaAutoConfigurationTests.java | 3 +- .../HibernateJpaAutoConfigurationTests.java | 4 +- .../TransactionAutoConfigurationTests.java | 14 ++++ .../TransactionManagerCustomizersTests.java | 76 +++++++++++++++++++ .../jta/JtaAutoConfigurationTests.java | 15 ++-- .../appendix-application-properties.adoc | 10 +-- 26 files changed, 389 insertions(+), 104 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/PlatformTransactionManagerCustomizer.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizers.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizersTests.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BasicBatchConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BasicBatchConfigurer.java index 5437f5e407..c52bf1da59 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BasicBatchConfigurer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BasicBatchConfigurer.java @@ -30,6 +30,8 @@ import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.support.SimpleJobLauncher; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.transaction.PlatformTransactionManager; @@ -55,6 +57,8 @@ public class BasicBatchConfigurer implements BatchConfigurer { private PlatformTransactionManager transactionManager; + private final TransactionManagerCustomizers transactionManagerCustomizers; + private JobRepository jobRepository; private JobLauncher jobLauncher; @@ -65,9 +69,12 @@ public class BasicBatchConfigurer implements BatchConfigurer { * Create a new {@link BasicBatchConfigurer} instance. * @param properties the batch properties * @param dataSource the underlying data source + * @param transactionManagerCustomizers transaction manager customizers (or + * {@code null}) */ - protected BasicBatchConfigurer(BatchProperties properties, DataSource dataSource) { - this(properties, dataSource, null); + protected BasicBatchConfigurer(BatchProperties properties, DataSource dataSource, + ObjectProvider transactionManagerCustomizers) { + this(properties, dataSource, null, transactionManagerCustomizers); } /** @@ -75,12 +82,17 @@ public class BasicBatchConfigurer implements BatchConfigurer { * @param properties the batch properties * @param dataSource the underlying data source * @param entityManagerFactory the entity manager factory (or {@code null}) + * @param transactionManagerCustomizers transaction manager customizers (or + * {@code null}) */ protected BasicBatchConfigurer(BatchProperties properties, DataSource dataSource, - EntityManagerFactory entityManagerFactory) { + EntityManagerFactory entityManagerFactory, + ObjectProvider transactionManagerCustomizers) { this.properties = properties; this.entityManagerFactory = entityManagerFactory; this.dataSource = dataSource; + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); } @Override @@ -153,7 +165,9 @@ public class BasicBatchConfigurer implements BatchConfigurer { protected PlatformTransactionManager createTransactionManager() { AbstractPlatformTransactionManager transactionManager = createAppropriateTransactionManager(); - this.properties.getTransaction().applyTo(transactionManager); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(transactionManager); + } return transactionManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java index fc9bafc204..74bab632db 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -153,15 +154,18 @@ public class BatchAutoConfiguration { @Bean @ConditionalOnBean(name = "entityManagerFactory") public BasicBatchConfigurer jpaBatchConfigurer(DataSource dataSource, - EntityManagerFactory entityManagerFactory) { + EntityManagerFactory entityManagerFactory, + ObjectProvider transactionManagerCustomizers) { return new BasicBatchConfigurer(this.properties, dataSource, - entityManagerFactory); + entityManagerFactory, transactionManagerCustomizers); } @Bean @ConditionalOnMissingBean(name = "entityManagerFactory") - public BasicBatchConfigurer basicBatchConfigurer(DataSource dataSource) { - return new BasicBatchConfigurer(this.properties, dataSource); + public BasicBatchConfigurer basicBatchConfigurer(DataSource dataSource, + ObjectProvider transactionManagerCustomizers) { + return new BasicBatchConfigurer(this.properties, dataSource, + transactionManagerCustomizers); } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java index b165a70b31..7d8ffa3a99 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java @@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.domain.EntityScanPackages; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -88,10 +89,17 @@ public class Neo4jDataAutoConfiguration { @Bean @ConditionalOnMissingBean(PlatformTransactionManager.class) public Neo4jTransactionManager transactionManager(SessionFactory sessionFactory, - Neo4jProperties properties) { - Neo4jTransactionManager transactionManager = new Neo4jTransactionManager( - sessionFactory); - properties.getTransaction().applyTo(transactionManager); + Neo4jProperties properties, + ObjectProvider transactionManagerCustomizers) { + return customize(new Neo4jTransactionManager(sessionFactory), + transactionManagerCustomizers.getIfAvailable()); + } + + private Neo4jTransactionManager customize(Neo4jTransactionManager transactionManager, + TransactionManagerCustomizers customizers) { + if (customizers != null) { + customizers.customize(transactionManager); + } return transactionManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java index be1b5a130d..6b79062f89 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java @@ -27,9 +27,7 @@ import javax.sql.DataSource; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.InitializingBean; -import org.springframework.boot.autoconfigure.transaction.TransactionProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; @@ -159,9 +157,6 @@ public class DataSourceProperties private String uniqueName; - @NestedConfigurationProperty - private final TransactionProperties transaction = new TransactionProperties(); - @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; @@ -478,10 +473,6 @@ public class DataSourceProperties this.xa = xa; } - public TransactionProperties getTransaction() { - return this.transaction; - } - /** * XA Specific datasource settings. */ diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java index 931da19c9c..d19ea11c71 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java @@ -18,11 +18,13 @@ package org.springframework.boot.autoconfigure.jdbc; import javax.sql.DataSource; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -54,8 +56,13 @@ public class DataSourceTransactionManagerAutoConfiguration { private final DataSource dataSource; - DataSourceTransactionManagerConfiguration(DataSource dataSource) { + private final TransactionManagerCustomizers transactionManagerCustomizers; + + DataSourceTransactionManagerConfiguration(DataSource dataSource, + ObjectProvider transactionManagerCustomizers) { this.dataSource = dataSource; + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); } @Bean @@ -64,7 +71,9 @@ public class DataSourceTransactionManagerAutoConfiguration { DataSourceProperties properties) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager( this.dataSource); - properties.getTransaction().applyTo(transactionManager); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(transactionManager); + } return transactionManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java index 3149bbbeaa..80204020a7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java @@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; @@ -88,8 +89,10 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { public HibernateJpaAutoConfiguration(DataSource dataSource, JpaProperties jpaProperties, - ObjectProvider jtaTransactionManager) { - super(dataSource, jpaProperties, jtaTransactionManager); + ObjectProvider jtaTransactionManager, + ObjectProvider transactionManagerCustomizers) { + super(dataSource, jpaProperties, jtaTransactionManager, + transactionManagerCustomizers); } @Override diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 7dfb12ee2f..3d4452151f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.domain.EntityScanPackages; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; @@ -71,20 +72,27 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { private final JtaTransactionManager jtaTransactionManager; + private final TransactionManagerCustomizers transactionManagerCustomizers; + private ConfigurableListableBeanFactory beanFactory; protected JpaBaseConfiguration(DataSource dataSource, JpaProperties properties, - ObjectProvider jtaTransactionManager) { + ObjectProvider jtaTransactionManager, + ObjectProvider transactionManagerCustomizers) { this.dataSource = dataSource; this.properties = properties; this.jtaTransactionManager = jtaTransactionManager.getIfAvailable(); + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); } @Bean @ConditionalOnMissingBean(PlatformTransactionManager.class) public PlatformTransactionManager transactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); - this.properties.getTransaction().applyTo(transactionManager); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(transactionManager); + } return transactionManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java index aee5121b81..ecb118b4ff 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java @@ -22,7 +22,6 @@ import java.util.Map; import javax.sql.DataSource; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; -import org.springframework.boot.autoconfigure.transaction.TransactionProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.orm.jpa.vendor.Database; @@ -68,9 +67,6 @@ public class JpaProperties { private Hibernate hibernate = new Hibernate(); - @NestedConfigurationProperty - private final TransactionProperties transaction = new TransactionProperties(); - public Map getProperties() { return this.properties; } @@ -129,10 +125,6 @@ public class JpaProperties { return this.hibernate.getAdditionalProperties(this.properties, dataSource); } - public TransactionProperties getTransaction() { - return this.transaction; - } - public static class Hibernate { private static final String USE_NEW_ID_GENERATOR_MAPPINGS = "hibernate.id." diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/PlatformTransactionManagerCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/PlatformTransactionManagerCustomizer.java new file mode 100644 index 0000000000..ca30bb169f --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/PlatformTransactionManagerCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.transaction; + +import org.springframework.transaction.PlatformTransactionManager; + +/** + * Callback interface that can be implemented by beans wishing to customize + * {@link PlatformTransactionManager PlatformTransactionManagers} whilst retaining default + * auto-configuration. + * + * @param The transaction manager type + * @author Phillip Webb + */ + +public interface PlatformTransactionManagerCustomizer { + /** + * Customize the given transaction manager. + * @param transactionManager the transaction manager to customize + */ + void customize(T transactionManager); + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.java index 58b05bb687..71aacea962 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.java @@ -16,6 +16,9 @@ package org.springframework.boot.autoconfigure.transaction; +import java.util.List; + +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -23,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandi import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration; import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; @@ -36,22 +40,35 @@ import org.springframework.transaction.support.TransactionTemplate; * @since 1.3.0 */ @Configuration -@ConditionalOnClass({ TransactionTemplate.class, PlatformTransactionManager.class }) -@ConditionalOnSingleCandidate(PlatformTransactionManager.class) +@ConditionalOnClass(PlatformTransactionManager.class) @AutoConfigureAfter({ JtaAutoConfiguration.class, JpaBaseConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class }) +@EnableConfigurationProperties(TransactionProperties.class) public class TransactionAutoConfiguration { - private final PlatformTransactionManager transactionManager; - - public TransactionAutoConfiguration(PlatformTransactionManager transactionManager) { - this.transactionManager = transactionManager; - } - @Bean @ConditionalOnMissingBean - public TransactionTemplate transactionTemplate() { - return new TransactionTemplate(this.transactionManager); + public TransactionManagerCustomizers platformTransactionManagerCustomizers( + ObjectProvider>> customizers) { + return new TransactionManagerCustomizers(customizers.getIfAvailable()); + } + + @Configuration + @ConditionalOnSingleCandidate(PlatformTransactionManager.class) + public static class TransactionTemplateConfiguration { + + private final PlatformTransactionManager transactionManager; + + public TransactionTemplateConfiguration( + PlatformTransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + @Bean + @ConditionalOnMissingBean + public TransactionTemplate transactionTemplate() { + return new TransactionTemplate(this.transactionManager); + } } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizers.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizers.java new file mode 100644 index 0000000000..a8c2ad3c9a --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizers.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.transaction; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.core.ResolvableType; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * A collection of {@link PlatformTransactionManagerCustomizer}. + * + * @author Phillip Webb + */ +public class TransactionManagerCustomizers { + + private final List> customizers; + + public TransactionManagerCustomizers( + Collection> customizers) { + this.customizers = (customizers == null ? null + : new ArrayList>(customizers)); + } + + public void customize(PlatformTransactionManager transactionManager) { + if (this.customizers != null) { + for (PlatformTransactionManagerCustomizer customizer : this.customizers) { + Class generic = ResolvableType + .forClass(PlatformTransactionManagerCustomizer.class, + customizer.getClass()) + .resolveGeneric(); + if (generic.isInstance(transactionManager)) { + customize(transactionManager, customizer); + } + } + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void customize(PlatformTransactionManager transactionManager, + PlatformTransactionManagerCustomizer customizer) { + customizer.customize(transactionManager); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionProperties.java index 619c368b84..9e8130ea35 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionProperties.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.transaction; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.transaction.support.AbstractPlatformTransactionManager; /** @@ -23,9 +24,12 @@ import org.springframework.transaction.support.AbstractPlatformTransactionManage * {@link AbstractPlatformTransactionManager}. * * @author Kazuki Shimizu + * @author Phillip Webb * @since 1.5.0 */ -public class TransactionProperties { +@ConfigurationProperties("spring.transaction") +public class TransactionProperties implements + PlatformTransactionManagerCustomizer { /** * Default transaction timeout in seconds. @@ -33,7 +37,7 @@ public class TransactionProperties { private Integer defaultTimeout; /** - * Perform the rollback on commit failurures. + * Perform the rollback on commit failures. */ private Boolean rollbackOnCommitFailure; @@ -53,11 +57,8 @@ public class TransactionProperties { this.rollbackOnCommitFailure = rollbackOnCommitFailure; } - /** - * Apply all transaction custom properties to the specified transaction manager. - * @param transactionManager the target transaction manager - */ - public void applyTo(AbstractPlatformTransactionManager transactionManager) { + @Override + public void customize(AbstractPlatformTransactionManager transactionManager) { if (this.defaultTimeout != null) { transactionManager.setDefaultTimeout(this.defaultTimeout); } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/AtomikosJtaConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/AtomikosJtaConfiguration.java index 308fe36e7c..051519838f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/AtomikosJtaConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/AtomikosJtaConfiguration.java @@ -27,9 +27,11 @@ import com.atomikos.icatch.config.UserTransactionService; import com.atomikos.icatch.config.UserTransactionServiceImp; import com.atomikos.icatch.jta.UserTransactionManager; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.ApplicationHome; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.boot.jta.XADataSourceWrapper; @@ -61,8 +63,13 @@ class AtomikosJtaConfiguration { private final JtaProperties jtaProperties; - AtomikosJtaConfiguration(JtaProperties jtaProperties) { + private final TransactionManagerCustomizers transactionManagerCustomizers; + + AtomikosJtaConfiguration(JtaProperties jtaProperties, + ObjectProvider transactionManagerCustomizers) { this.jtaProperties = jtaProperties; + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); } @Bean(initMethod = "init", destroyMethod = "shutdownForce") @@ -114,7 +121,9 @@ class AtomikosJtaConfiguration { TransactionManager transactionManager) { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager( userTransaction, transactionManager); - this.jtaProperties.getTransaction().applyTo(jtaTransactionManager); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(jtaTransactionManager); + } return jtaTransactionManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/BitronixJtaConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/BitronixJtaConfiguration.java index 36e27bc738..dbd3d54fef 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/BitronixJtaConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/BitronixJtaConfiguration.java @@ -25,9 +25,11 @@ import bitronix.tm.BitronixTransactionManager; import bitronix.tm.TransactionManagerServices; import bitronix.tm.jndi.BitronixContext; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.ApplicationHome; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jta.XAConnectionFactoryWrapper; @@ -58,8 +60,13 @@ class BitronixJtaConfiguration { private final JtaProperties jtaProperties; - BitronixJtaConfiguration(JtaProperties jtaProperties) { + private final TransactionManagerCustomizers transactionManagerCustomizers; + + BitronixJtaConfiguration(JtaProperties jtaProperties, + ObjectProvider transactionManagerCustomizers) { this.jtaProperties = jtaProperties; + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); } @Bean @@ -110,7 +117,9 @@ class BitronixJtaConfiguration { TransactionManager transactionManager) { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager( transactionManager); - this.jtaProperties.getTransaction().applyTo(jtaTransactionManager); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(jtaTransactionManager); + } return jtaTransactionManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JndiJtaConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JndiJtaConfiguration.java index 478a898a45..dc80b4a495 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JndiJtaConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JndiJtaConfiguration.java @@ -16,9 +16,11 @@ package org.springframework.boot.autoconfigure.transaction.jta; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; @@ -41,18 +43,22 @@ import org.springframework.transaction.jta.JtaTransactionManager; @ConditionalOnMissingBean(PlatformTransactionManager.class) class JndiJtaConfiguration { - private final JtaProperties jtaProperties; + private final TransactionManagerCustomizers transactionManagerCustomizers; - JndiJtaConfiguration(JtaProperties jtaProperties) { - this.jtaProperties = jtaProperties; + JndiJtaConfiguration( + ObjectProvider transactionManagerCustomizers) { + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); } @Bean public JtaTransactionManager transactionManager() { - JtaTransactionManager transactionManager = new JtaTransactionManagerFactoryBean() + JtaTransactionManager jtaTransactionManager = new JtaTransactionManagerFactoryBean() .getObject(); - this.jtaProperties.getTransaction().applyTo(transactionManager); - return transactionManager; + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(jtaTransactionManager); + } + return jtaTransactionManager; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JtaProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JtaProperties.java index bbf63e4b53..124bba9434 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JtaProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/JtaProperties.java @@ -16,9 +16,7 @@ package org.springframework.boot.autoconfigure.transaction.jta; -import org.springframework.boot.autoconfigure.transaction.TransactionProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.transaction.jta.JtaTransactionManager; /** @@ -44,9 +42,6 @@ public class JtaProperties { */ private String transactionManagerId; - @NestedConfigurationProperty - private final TransactionProperties transaction = new TransactionProperties(); - public void setLogDir(String logDir) { this.logDir = logDir; } @@ -63,8 +58,4 @@ public class JtaProperties { this.transactionManagerId = transactionManagerId; } - public TransactionProperties getTransaction() { - return this.transaction; - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/NarayanaJtaConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/NarayanaJtaConfiguration.java index ec97194374..2843b6e330 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/NarayanaJtaConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/jta/NarayanaJtaConfiguration.java @@ -25,9 +25,11 @@ import javax.transaction.UserTransaction; import com.arjuna.ats.jbossatx.jta.RecoveryManagerService; import org.jboss.tm.XAResourceRecoveryRegistry; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.ApplicationHome; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.boot.jta.XADataSourceWrapper; @@ -60,8 +62,13 @@ public class NarayanaJtaConfiguration { private final JtaProperties jtaProperties; - public NarayanaJtaConfiguration(JtaProperties jtaProperties) { + private final TransactionManagerCustomizers transactionManagerCustomizers; + + public NarayanaJtaConfiguration(JtaProperties jtaProperties, + ObjectProvider transactionManagerCustomizers) { this.jtaProperties = jtaProperties; + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); } @Bean @@ -121,7 +128,9 @@ public class NarayanaJtaConfiguration { TransactionManager transactionManager) { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager( userTransaction, transactionManager); - this.jtaProperties.getTransaction().applyTo(jtaTransactionManager); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(jtaTransactionManager); + } return jtaTransactionManager; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java index 327ee070ca..6505f7cba3 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java @@ -52,6 +52,7 @@ import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.test.City; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -91,6 +92,7 @@ public class BatchAutoConfigurationTests { this.context = new AnnotationConfigApplicationContext(); this.context.register(TestConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -106,6 +108,7 @@ public class BatchAutoConfigurationTests { public void testNoDatabase() throws Exception { this.context = new AnnotationConfigApplicationContext(); this.context.register(TestCustomConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -118,7 +121,7 @@ public class BatchAutoConfigurationTests { public void testNoBatchConfiguration() throws Exception { this.context = new AnnotationConfigApplicationContext(); this.context.register(EmptyConfiguration.class, BatchAutoConfiguration.class, - EmbeddedDataSourceConfiguration.class, + TransactionAutoConfiguration.class, EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBeanNamesForType(JobLauncher.class).length) @@ -132,6 +135,7 @@ public class BatchAutoConfigurationTests { this.context = new AnnotationConfigApplicationContext(); this.context.register(JobConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -147,6 +151,7 @@ public class BatchAutoConfigurationTests { "spring.batch.job.names:discreteRegisteredJob"); this.context.register(NamedJobConfigurationWithRegisteredJob.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); JobRepository repository = this.context.getBean(JobRepository.class); @@ -163,6 +168,7 @@ public class BatchAutoConfigurationTests { "spring.batch.job.names:discreteLocalJob"); this.context.register(NamedJobConfigurationWithLocalJob.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -179,6 +185,7 @@ public class BatchAutoConfigurationTests { "spring.batch.job.enabled:false"); this.context.register(JobConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -194,6 +201,7 @@ public class BatchAutoConfigurationTests { "spring.batch.initializer.enabled:false"); this.context.register(TestConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -212,6 +220,7 @@ public class BatchAutoConfigurationTests { this.context.register(TestConfiguration.class, EmbeddedDataSourceConfiguration.class, HibernateJpaAutoConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); PlatformTransactionManager transactionManager = this.context @@ -235,6 +244,7 @@ public class BatchAutoConfigurationTests { this.context.register(TestConfiguration.class, EmbeddedDataSourceConfiguration.class, HibernateJpaAutoConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -259,6 +269,7 @@ public class BatchAutoConfigurationTests { this.context.register(TestConfiguration.class, EmbeddedDataSourceConfiguration.class, HibernateJpaAutoConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(JobLauncher.class)).isNotNull(); @@ -274,11 +285,12 @@ public class BatchAutoConfigurationTests { public void testCustomizeJpaTransactionManagerUsingProperties() throws Exception { this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, - "spring.batch.transaction.default-timeout:30", - "spring.batch.transaction.rollback-on-commit-failure:true"); + "spring.transaction.default-timeout:30", + "spring.transaction.rollback-on-commit-failure:true"); this.context.register(TestConfiguration.class, EmbeddedDataSourceConfiguration.class, HibernateJpaAutoConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); this.context.getBean(BatchConfigurer.class); @@ -293,10 +305,11 @@ public class BatchAutoConfigurationTests { throws Exception { this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, - "spring.batch.transaction.default-timeout:30", - "spring.batch.transaction.rollback-on-commit-failure:true"); + "spring.transaction.default-timeout:30", + "spring.transaction.rollback-on-commit-failure:true"); this.context.register(TestConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, + TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); this.context.getBean(BatchConfigurer.class); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java index 38044b7488..23721c80ba 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java @@ -30,6 +30,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.data.neo4j.city.City; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -82,8 +83,8 @@ public class Neo4jDataAutoConfigurationTests { @Test public void customNeo4jTransactionManagerUsingProperties() { - load(null, "spring.data.neo4j.transaction.default-timeout=30", - "spring.data.neo4j.transaction.rollback-on-commit-failure:true"); + load(null, "spring.transaction.default-timeout=30", + "spring.transaction.rollback-on-commit-failure:true"); Neo4jTransactionManager transactionManager = this.context .getBean(Neo4jTransactionManager.class); assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30); @@ -154,7 +155,7 @@ public class Neo4jDataAutoConfigurationTests { ctx.register(config); } ctx.register(PropertyPlaceholderAutoConfiguration.class, - Neo4jDataAutoConfiguration.class); + Neo4jDataAutoConfiguration.class, TransactionAutoConfiguration.class); ctx.refresh(); this.context = ctx; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java index 16c331edc7..5a6af783d2 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java @@ -20,6 +20,7 @@ import javax.sql.DataSource; import org.junit.Test; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -46,7 +47,8 @@ public class DataSourceTransactionManagerAutoConfigurationTests { @Test public void testDataSourceExists() throws Exception { this.context.register(EmbeddedDataSourceConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class); + DataSourceTransactionManagerAutoConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSource.class)).isNotNull(); assertThat(this.context.getBean(DataSourceTransactionManager.class)).isNotNull(); @@ -56,7 +58,8 @@ public class DataSourceTransactionManagerAutoConfigurationTests { @Test public void testNoDataSourceExists() throws Exception { - this.context.register(DataSourceTransactionManagerAutoConfiguration.class); + this.context.register(DataSourceTransactionManagerAutoConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBeanNamesForType(DataSource.class)).isEmpty(); assertThat(this.context.getBeanNamesForType(DataSourceTransactionManager.class)) @@ -67,7 +70,8 @@ public class DataSourceTransactionManagerAutoConfigurationTests { public void testManualConfiguration() throws Exception { this.context.register(SwitchTransactionsOn.class, EmbeddedDataSourceConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class); + DataSourceTransactionManagerAutoConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSource.class)).isNotNull(); assertThat(this.context.getBean(DataSourceTransactionManager.class)).isNotNull(); @@ -78,7 +82,8 @@ public class DataSourceTransactionManagerAutoConfigurationTests { this.context.register(SwitchTransactionsOn.class, TransactionManagerConfiguration.class, EmbeddedDataSourceConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class); + DataSourceTransactionManagerAutoConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)) .hasSize(1); @@ -89,7 +94,8 @@ public class DataSourceTransactionManagerAutoConfigurationTests { @Test public void testMultiDataSource() throws Exception { this.context.register(MultiDataSourceConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class); + DataSourceTransactionManagerAutoConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)) .isEmpty(); @@ -100,7 +106,8 @@ public class DataSourceTransactionManagerAutoConfigurationTests { @Test public void testMultiDataSourceUsingPrimary() throws Exception { this.context.register(MultiDataSourceUsingPrimaryConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class); + DataSourceTransactionManagerAutoConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSourceTransactionManager.class)).isNotNull(); assertThat(this.context.getBean(AbstractTransactionManagementConfiguration.class)) @@ -111,10 +118,11 @@ public class DataSourceTransactionManagerAutoConfigurationTests { public void testCustomizeDataSourceTransactionManagerUsingProperties() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, - "spring.datasource.transaction.default-timeout:30", - "spring.datasource.transaction.rollback-on-commit-failure:true"); + "spring.transaction.default-timeout:30", + "spring.transaction.rollback-on-commit-failure:true"); this.context.register(EmbeddedDataSourceConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class); + DataSourceTransactionManagerAutoConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); DataSourceTransactionManager transactionManager = this.context .getBean(DataSourceTransactionManager.class); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java index 56c866708e..6aa692c297 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java @@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.test.City; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -202,7 +203,7 @@ public abstract class AbstractJpaAutoConfigurationTests { protected void setupTestConfiguration(Class configClass) { this.context.register(configClass, EmbeddedDataSourceConfiguration.class, - DataSourceAutoConfiguration.class, + DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, getAutoConfigureClass()); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index a45cbba8d5..bf4f0dec52 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -176,8 +176,8 @@ public class HibernateJpaAutoConfigurationTests @Test public void testCustomJpaTransactionManagerUsingProperties() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, - "spring.jpa.transaction.default-timeout:30", - "spring.jpa.transaction.rollback-on-commit-failure:true"); + "spring.transaction.default-timeout:30", + "spring.transaction.rollback-on-commit-failure:true"); setupTestConfiguration(); this.context.refresh(); JpaTransactionManager transactionManager = this.context diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java index cbb9ed50a2..1964c3e09f 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.transaction; +import java.util.List; import java.util.Map; import org.junit.After; @@ -27,6 +28,7 @@ import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; @@ -37,6 +39,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link TransactionAutoConfiguration}. * * @author Stephane Nicoll + * @author Phillip Webb */ public class TransactionAutoConfigurationTests { @@ -82,6 +85,17 @@ public class TransactionAutoConfigurationTests { assertThat(beans.containsKey("transactionTemplateFoo")).isTrue(); } + @Test + public void platformTransactionManagerCustomizers() throws Exception { + load(SeveralTransactionManagersConfiguration.class); + TransactionManagerCustomizers customizers = this.context + .getBean(TransactionManagerCustomizers.class); + List field = (List) ReflectionTestUtils.getField(customizers, + "customizers"); + assertThat(field).hasSize(1).first().isInstanceOf(TransactionProperties.class); + + } + private void load(Class... configs) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(configs); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizersTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizersTests.java new file mode 100644 index 0000000000..dd3d37d726 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizersTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.transaction; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.jta.JtaTransactionManager; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link TransactionManagerCustomizers}. + * + * @author Phillip Webb + */ +public class TransactionManagerCustomizersTests { + + @Test + public void customizeWithNullCustomizersShouldDoNothing() throws Exception { + new TransactionManagerCustomizers(null) + .customize(mock(PlatformTransactionManager.class)); + } + + @Test + public void customizeShouldCheckGeneric() throws Exception { + List> list = new ArrayList>(); + list.add(new TestCustomizer()); + list.add(new TestJtaCustomizer()); + TransactionManagerCustomizers customizers = new TransactionManagerCustomizers( + list); + customizers.customize(mock(PlatformTransactionManager.class)); + customizers.customize(mock(JtaTransactionManager.class)); + assertThat(list.get(0).getCount()).isEqualTo(2); + assertThat(list.get(1).getCount()).isEqualTo(1); + } + + private static class TestCustomizer + implements PlatformTransactionManagerCustomizer { + + private int count; + + @Override + public void customize(T transactionManager) { + this.count++; + } + + public int getCount() { + return this.count; + } + + } + + private static class TestJtaCustomizer extends TestCustomizer { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java index 70239874a6..e7548cea48 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java @@ -42,6 +42,7 @@ import org.junit.rules.ExpectedException; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.boot.jta.XADataSourceWrapper; @@ -250,9 +251,10 @@ public class JtaAutoConfigurationTests { public void atomikosCustomizeJtaTransactionManagerUsingProperties() throws Exception { this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, - "spring.jta.transaction.default-timeout:30", - "spring.jta.transaction.rollback-on-commit-failure:true"); - this.context.register(AtomikosJtaConfiguration.class); + "spring.transaction.default-timeout:30", + "spring.transaction.rollback-on-commit-failure:true"); + this.context.register(AtomikosJtaConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); JtaTransactionManager transactionManager = this.context .getBean(JtaTransactionManager.class); @@ -264,9 +266,10 @@ public class JtaAutoConfigurationTests { public void bitronixCustomizeJtaTransactionManagerUsingProperties() throws Exception { this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, - "spring.jta.transaction.default-timeout:30", - "spring.jta.transaction.rollback-on-commit-failure:true"); - this.context.register(BitronixJtaConfiguration.class); + "spring.transaction.default-timeout:30", + "spring.transaction.rollback-on-commit-failure:true"); + this.context.register(BitronixJtaConfiguration.class, + TransactionAutoConfiguration.class); this.context.refresh(); JtaTransactionManager transactionManager = this.context .getBean(JtaTransactionManager.class); diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 448213a9a8..1b237a314a 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -582,7 +582,6 @@ content into your application; rather pick only the properties that you need. spring.data.neo4j.open-in-view=false # Register OpenSessionInViewInterceptor. Binds a Neo4j Session to the thread for the entire processing of the request. spring.data.neo4j.password= # Login password of the server. spring.data.neo4j.repositories.enabled=true # Enable Neo4j repositories. - spring.data.neo4j.transaction.*= # Transaction manager settings spring.data.neo4j.uri= # URI used by the driver. Auto-detected by default. spring.data.neo4j.username= # Login user of the server. @@ -624,7 +623,6 @@ content into your application; rather pick only the properties that you need. spring.datasource.separator=; # Statement separator in SQL initialization scripts. spring.datasource.sql-script-encoding= # SQL scripts encoding. spring.datasource.tomcat.*= # Tomcat datasource specific settings - spring.datasource.transaction.*= # Transaction manager settings spring.datasource.type= # Fully qualified name of the connection pool implementation to use. By default, it is auto-detected from the classpath. spring.datasource.url= # JDBC url of the database. spring.datasource.username= @@ -661,12 +659,10 @@ content into your application; rather pick only the properties that you need. spring.jpa.open-in-view=true # Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request. spring.jpa.properties.*= # Additional native properties to set on the JPA provider. spring.jpa.show-sql=false # Enable logging of SQL statements. - spring.jpa.transaction.*= # Transaction manager settings # JTA ({sc-spring-boot-autoconfigure}/transaction/jta/JtaAutoConfiguration.{sc-ext}[JtaAutoConfiguration]) spring.jta.enabled=true # Enable JTA support. spring.jta.log-dir= # Transaction logs directory. - spring.jta.transaction.*= # Transaction manager settings spring.jta.transaction-manager-id= # Transaction manager unique identifier. # ATOMIKOS ({sc-spring-boot}/jta/atomikos/AtomikosProperties.{sc-ext}[AtomikosProperties]) @@ -813,6 +809,11 @@ content into your application; rather pick only the properties that you need. spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs. spring.redis.timeout=0 # Connection timeout in milliseconds. + # TRANSACTION ({sc-spring-boot-autoconfigure}/transaction/TransactionProperties.{sc-ext}[TransactionProperties]) + spring.transaction.default-timeout= # Default transaction timeout in seconds. + spring.transaction.rollback-on-commit-failure= # Perform the rollback on commit failures. + + # ---------------------------------------- # INTEGRATION PROPERTIES @@ -851,7 +852,6 @@ content into your application; rather pick only the properties that you need. spring.batch.job.names= # Comma-separated list of job names to execute on startup (For instance `job1,job2`). By default, all Jobs found in the context are executed. spring.batch.schema=classpath:org/springframework/batch/core/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema. spring.batch.table-prefix= # Table prefix for all the batch meta-data tables. - spring.batch.transaction.*= # Transaction manager settings # JMS ({sc-spring-boot-autoconfigure}/jms/JmsProperties.{sc-ext}[JmsProperties]) spring.jms.jndi-name= # Connection factory JNDI name. When set, takes precedence to others connection factory auto-configurations.