Allow the transaction manager to be overridden in DefaultBatchConfigurer

Before this commit, it was not possible to override the transaction
manager by subclassing DefaultBatchConfigurer and overriding the
getTransactionManager method.

This commit uses the getTransactionManager method in the initialize method
in order to take into account the transaction manager provided by the user.

Resolves BATCH-2294
This commit is contained in:
Mahmoud Ben Hassine
2018-08-27 23:58:08 +02:00
committed by Michael Minella
parent e83ed1db11
commit a68d70043a
5 changed files with 401 additions and 5 deletions

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2018 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.batch.core.configuration.annotation;
import javax.sql.DataSource;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.aop.Advisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
* @author Mahmoud Ben Hassine
*/
@RunWith(MockitoJUnitRunner.class)
public abstract class TransactionManagerConfigurationTests {
@Mock
protected static PlatformTransactionManager transactionManager;
@Mock
protected static PlatformTransactionManager transactionManager2;
/*
* The transaction manager set on JobRepositoryFactoryBean in DefaultBatchConfigurer.createJobRepository
* ends up in the TransactionInterceptor advise applied to the (proxied) JobRepository.
* This method extracts the advise from the proxy and returns the transaction manager.
*/
PlatformTransactionManager getTransactionManagerSetOnJobRepository(JobRepository jobRepository) throws Exception {
TargetSource targetSource = ((Advised) jobRepository).getTargetSource(); // proxy created in SimpleBatchConfiguration.createLazyProxy
Advised target = (Advised) targetSource.getTarget(); // initial proxy created in AbstractJobRepositoryFactoryBean.initializeProxy
Advisor[] advisors = target.getAdvisors();
for (Advisor advisor : advisors) {
if (advisor.getAdvice() instanceof TransactionInterceptor) {
TransactionInterceptor transactionInterceptor = (TransactionInterceptor) advisor.getAdvice();
return transactionInterceptor.getTransactionManager();
}
}
return null;
}
static DataSource createDataSource() {
return new EmbeddedDatabaseBuilder()
.addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
.addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
.build();
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2018 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.batch.core.configuration.annotation;
import javax.sql.DataSource;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.util.AopTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
/**
* @author Mahmoud Ben Hassine
*/
public class TransactionManagerConfigurationWithBatchConfigurerTests extends TransactionManagerConfigurationTests {
@Test
public void testConfigurationWithNoDataSourceAndNoTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndNoTransactionManager.class);
BatchConfigurer batchConfigurer = applicationContext.getBean(BatchConfigurer.class);
PlatformTransactionManager platformTransactionManager = batchConfigurer.getTransactionManager();
Assert.assertTrue(platformTransactionManager instanceof ResourcelessTransactionManager);
Assert.assertSame(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)), platformTransactionManager);
}
@Test
public void testConfigurationWithNoDataSourceAndTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndTransactionManager.class);
BatchConfigurer batchConfigurer = applicationContext.getBean(BatchConfigurer.class);
PlatformTransactionManager platformTransactionManager = batchConfigurer.getTransactionManager();
Assert.assertSame(transactionManager, platformTransactionManager);
Assert.assertSame(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)), transactionManager);
}
@Test
public void testConfigurationWithDataSourceAndNoTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithDataSourceAndNoTransactionManager.class);
BatchConfigurer batchConfigurer = applicationContext.getBean(BatchConfigurer.class);
PlatformTransactionManager platformTransactionManager = batchConfigurer.getTransactionManager();
Assert.assertTrue(platformTransactionManager instanceof DataSourceTransactionManager);
DataSourceTransactionManager dataSourceTransactionManager = AopTestUtils.getTargetObject(platformTransactionManager);
Assert.assertEquals(applicationContext.getBean(DataSource.class), dataSourceTransactionManager.getDataSource());
Assert.assertSame(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)), platformTransactionManager);
}
@Test
public void testConfigurationWithDataSourceAndTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithDataSourceAndTransactionManager.class);
BatchConfigurer batchConfigurer = applicationContext.getBean(BatchConfigurer.class);
PlatformTransactionManager platformTransactionManager = batchConfigurer.getTransactionManager();
Assert.assertSame(transactionManager, platformTransactionManager);
Assert.assertSame(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)), transactionManager);
}
@EnableBatchProcessing
public static class BatchConfigurationWithNoDataSourceAndNoTransactionManager {
@Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer();
}
}
@EnableBatchProcessing
public static class BatchConfigurationWithNoDataSourceAndTransactionManager {
@Bean
public PlatformTransactionManager transactionManager() {
return transactionManager;
}
@Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer() {
@Override
public PlatformTransactionManager getTransactionManager() {
return transactionManager();
}
};
}
}
@EnableBatchProcessing
public static class BatchConfigurationWithDataSourceAndNoTransactionManager {
@Bean
public DataSource dataSource() {
return createDataSource();
}
@Bean
public BatchConfigurer batchConfigurer(DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource);
}
}
@EnableBatchProcessing
public static class BatchConfigurationWithDataSourceAndTransactionManager {
@Bean
public DataSource dataSource() {
return createDataSource();
}
@Bean
public PlatformTransactionManager transactionManager() {
return transactionManager;
}
@Bean
public BatchConfigurer batchConfigurer(DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource) {
@Override
public PlatformTransactionManager getTransactionManager() {
return transactionManager();
}
};
}
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2018 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.batch.core.configuration.annotation;
import javax.sql.DataSource;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.util.AopTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
/**
* @author Mahmoud Ben Hassine
*/
public class TransactionManagerConfigurationWithoutBatchConfigurerTests extends TransactionManagerConfigurationTests {
@Test
public void testConfigurationWithNoDataSourceAndNoTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndNoTransactionManager.class);
Assert.assertTrue(applicationContext.containsBean("transactionManager"));
PlatformTransactionManager platformTransactionManager = applicationContext.getBean(PlatformTransactionManager.class);
Object targetObject = AopTestUtils.getTargetObject(platformTransactionManager);
Assert.assertTrue(targetObject instanceof ResourcelessTransactionManager);
Assert.assertSame(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)), targetObject);
}
@Test
public void testConfigurationWithNoDataSourceAndTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithNoDataSourceAndTransactionManager.class);
PlatformTransactionManager platformTransactionManager = applicationContext.getBean(PlatformTransactionManager.class);
Assert.assertSame(transactionManager, platformTransactionManager);
// In this case, the supplied transaction manager won't be used by batch and a ResourcelessTransactionManager will be used instead.
// The user has to provide a custom BatchConfigurer.
Assert.assertTrue(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)) instanceof ResourcelessTransactionManager);
}
@Test
public void testConfigurationWithDataSourceAndNoTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithDataSourceAndNoTransactionManager.class);
Assert.assertTrue(applicationContext.containsBean("transactionManager"));
PlatformTransactionManager platformTransactionManager = applicationContext.getBean(PlatformTransactionManager.class);
Object targetObject = AopTestUtils.getTargetObject(platformTransactionManager);
Assert.assertTrue(targetObject instanceof DataSourceTransactionManager);
DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) targetObject;
Assert.assertEquals(applicationContext.getBean(DataSource.class), dataSourceTransactionManager.getDataSource());
Assert.assertSame(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)), dataSourceTransactionManager);
}
@Test
public void testConfigurationWithDataSourceAndOneTransactionManager() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithDataSourceAndOneTransactionManager.class);
PlatformTransactionManager platformTransactionManager = applicationContext.getBean(PlatformTransactionManager.class);
Assert.assertSame(transactionManager, platformTransactionManager);
// In this case, the supplied transaction manager won't be used by batch and a DataSourceTransactionManager will be used instead.
// The user has to provide a custom BatchConfigurer.
Assert.assertTrue(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)) instanceof DataSourceTransactionManager);
}
@Test
public void testConfigurationWithDataSourceAndMultipleTransactionManagers() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfigurationWithDataSourceAndMultipleTransactionManagers.class);
PlatformTransactionManager platformTransactionManager = applicationContext.getBean(PlatformTransactionManager.class);
Assert.assertSame(transactionManager2, platformTransactionManager);
// In this case, the supplied primary transaction manager won't be used by batch and a DataSourceTransactionManager will be used instead.
// The user has to provide a custom BatchConfigurer.
Assert.assertTrue(getTransactionManagerSetOnJobRepository(applicationContext.getBean(JobRepository.class)) instanceof DataSourceTransactionManager);
}
@EnableBatchProcessing
public static class BatchConfigurationWithNoDataSourceAndNoTransactionManager {
}
@EnableBatchProcessing
public static class BatchConfigurationWithNoDataSourceAndTransactionManager {
@Bean
public PlatformTransactionManager transactionManager() {
return transactionManager;
}
}
@EnableBatchProcessing
public static class BatchConfigurationWithDataSourceAndNoTransactionManager {
@Bean
public DataSource dataSource() {
return createDataSource();
}
}
@EnableBatchProcessing
public static class BatchConfigurationWithDataSourceAndOneTransactionManager {
@Bean
public DataSource dataSource() {
return createDataSource();
}
@Bean
public PlatformTransactionManager transactionManager() {
return transactionManager;
}
}
@EnableBatchProcessing
public static class BatchConfigurationWithDataSourceAndMultipleTransactionManagers {
@Bean
public DataSource dataSource() {
return createDataSource();
}
@Bean
public PlatformTransactionManager transactionManager() {
return transactionManager;
}
@Primary
@Bean
public PlatformTransactionManager transactionManager2() {
return transactionManager2;
}
}
}