I am trying to configure XA/distributed transactions for a spring batch / spring cloud task application configured with spring boot.
I have added the following dependency hoping to rely on spring boot auto configuration:
compile("org.springframework.boot:spring-boot-starter-jta-atomikos")
However the following two classes cause two transaction managers to be configured:
org.springframework.cloud.task.configuration.SimpleTaskConfiguration
org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration
See following message:
2016-07-18 21:46:19.952 INFO 18995 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'transactionManager' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration; factoryMethodName=transactionManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.task.configuration.SimpleTaskConfiguration; factoryMethodName=transactionManager; initMethodName=null; destroyMethodName=(inferred); defined in org.springframework.cloud.task.configuration.SimpleTaskConfiguration]
and then because a PlatformTransactionManager
named transactionManager
is configured, my atomikos auto-configuration is not picked up:
AtomikosJtaConfiguration did not match
- @ConditionalOnClass classes found: org.springframework.transaction.jta.JtaTransactionManager,com.atomikos.icatch.jta.UserTransactionManager (OnClassCondition)
- @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) found the following [transactionManager] (OnBeanCondition)
Can someone please help me prevent this unduly forcing of the transactionManager
beans caused by the two classes above?
I had the same issue and my solution was to implement BatchConfigurer (keeping @EnableBatchProcessing) and to add atomikos beans manually.
JobConfig:
@Configuration
@EnableBatchProcessing
public class JobConfig implements BatchConfigurer {
@Autowired
private DataSource dataSource;
@Autowired
private JtaTransactionManager jtaTransactionManager;
// ... skipping some code
@Override
public JobRepository getJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(jtaTransactionManager);
return factory.getObject();
}
@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return jtaTransactionManager;
}
@Override
public JobLauncher getJobLauncher() throws Exception {
SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(getJobRepository());
launcher.setTaskExecutor(new SimpleAsyncTaskExecutor());
return launcher;
}
@Override
public JobExplorer getJobExplorer() throws Exception {
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
return jobExplorerFactoryBean.getObject();
}
AtomikosConfig:
@Configuration
public class AtomikosConfig extends AbstractJtaPlatform {
@Bean(initMethod = "init", destroyMethod = "close")
@DependsOn("atomikosUserTransactionService")
public UserTransactionManager atomikosTransactionManager() {
UserTransactionManager manager = new UserTransactionManager();
manager.setForceShutdown(false);
manager.setStartupTransactionService(false);
return manager;
}
@Bean(initMethod = "init", destroyMethod = "shutdownForce")
public UserTransactionServiceImp atomikosUserTransactionService() {
Properties properties = new Properties();
return new UserTransactionServiceImp(properties);
}
@Bean
public UserTransactionImp atomikosUserTransaction() throws SystemException {
UserTransactionImp transaction = new UserTransactionImp();
transaction.setTransactionTimeout(300);
return transaction;
}
@Primary
@Bean
public JtaTransactionManager jtaTransactionManager() throws Exception {
JtaTransactionManager manager = new JtaTransactionManager();
manager.setTransactionManager(atomikosTransactionManager());
manager.setUserTransaction(atomikosUserTransaction());
manager.setAllowCustomIsolationLevels(true);
return manager;
}
@Bean
public ActiveMQXAConnectionFactory xaFactory() {
ActiveMQXAConnectionFactory factory = new ActiveMQXAConnectionFactory();
factory.setBrokerURL("tcp://localhost:61616");
factory.setUserName("admin");
factory.setPassword("admin");
//factory.setTrustAllPackages(true);
factory.setTransactedIndividualAck(true);
return factory;
}
@Bean(initMethod = "init", destroyMethod = "close")
public AtomikosConnectionFactoryBean connectionFactory() {
AtomikosConnectionFactoryBean factoryBean = new AtomikosConnectionFactoryBean();
factoryBean.setUniqueResourceName("amq1");
factoryBean.setXaConnectionFactory(xaFactory());
factoryBean.setMaxPoolSize(10);
return factoryBean;
}
@Bean
public AtomikosJtaPlatform springJtaPlatformAdapter() throws Exception {
AtomikosJtaPlatform platform = new AtomikosJtaPlatform();
platform.setJtaTransactionManager(jtaTransactionManager());
platform.setTransactionManager(atomikosTransactionManager());
platform.setUserTransaction(atomikosUserTransaction());
return platform;
}
@Override
protected TransactionManager locateTransactionManager() {
return atomikosTransactionManager();
}
@Override
protected UserTransaction locateUserTransaction() {
return atomikosTransactionManager();
}
After looking at your example, what I can tell you is this - there is no way to make auto-configuration work - even if you disable the auto configuration for transaction management, which you did try, task and batch auto-configurations (triggered by @EnableBatchProcessing
and @EnableTask
) would still register their own transaction managers and thus stop Atomikos Configuration from being triggered. The reason for this is because @EnableBatchProcessing
includes BatchConfigurationSelector
configuration class, which in turn includes either SimpleBatchConfiguration
or ModularBatchConfiguration
and both of them will always register a transaction manager - there's no conditional annotations on either of the bean definitions. @EnableTask
does a very similar thing, only with SimpleTaskConfiguration
.
So the only way out of this that I can see is for you to create batch and task configurations completely manually.
As for how to manually configure batch and tasks, I would recommend looking at SimpleTaskConfiguration and AbstractBatchConfiguration - you can see there all the beans that you'll need to register.
Alternatively, you can see a batch example on this Java Code Geeks page, you should just translate the XML configuration to Java config.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With