/*
* 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.jta;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.jms.ConnectionFactory;
import javax.jms.TemporaryQueue;
import javax.jms.XAConnection;
import javax.jms.XAConnectionFactory;
import javax.jms.XASession;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import javax.transaction.xa.XAResource;
import com.atomikos.icatch.config.UserTransactionService;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.jms.AtomikosConnectionFactoryBean;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
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;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.boot.jta.atomikos.AtomikosDependsOnBeanFactoryPostProcessor;
import org.springframework.boot.jta.atomikos.AtomikosProperties;
import org.springframework.boot.jta.bitronix.BitronixDependentBeanFactoryPostProcessor;
import org.springframework.boot.jta.bitronix.PoolingConnectionFactoryBean;
import org.springframework.boot.jta.bitronix.PoolingDataSourceBean;
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.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.FileSystemUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link JtaAutoConfiguration}.
*
* @author Josh Long
* @author Phillip Webb
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
public class JtaAutoConfigurationTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
private AnnotationConfigApplicationContext context;
@Before
public void cleanUpLogs() {
FileSystemUtils.deleteRecursively(new File("target/transaction-logs"));
}
@After
public void closeContext() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void customPlatformTransactionManager() throws Exception {
this.context = new AnnotationConfigApplicationContext(
CustomTransactionManagerConfig.class, JtaAutoConfiguration.class);
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(JtaTransactionManager.class);
}
@Test
public void disableJtaSupport() {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, "spring.jta.enabled:false");
this.context.register(JtaAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeansOfType(JtaTransactionManager.class)).isEmpty();
assertThat(this.context.getBeansOfType(XADataSourceWrapper.class)).isEmpty();
assertThat(this.context.getBeansOfType(XAConnectionFactoryWrapper.class))
.isEmpty();
}
@Test
public void atomikosSanityCheck() throws Exception {
this.context = new AnnotationConfigApplicationContext(JtaProperties.class,
AtomikosJtaConfiguration.class);
this.context.getBean(AtomikosProperties.class);
this.context.getBean(UserTransactionService.class);
this.context.getBean(UserTransactionManager.class);
this.context.getBean(UserTransaction.class);
this.context.getBean(XADataSourceWrapper.class);
this.context.getBean(XAConnectionFactoryWrapper.class);
this.context.getBean(AtomikosDependsOnBeanFactoryPostProcessor.class);
this.context.getBean(JtaTransactionManager.class);
}
@Test
public void bitronixSanityCheck() throws Exception {
this.context = new AnnotationConfigApplicationContext(JtaProperties.class,
BitronixJtaConfiguration.class);
this.context.getBean(bitronix.tm.Configuration.class);
this.context.getBean(TransactionManager.class);
this.context.getBean(XADataSourceWrapper.class);
this.context.getBean(XAConnectionFactoryWrapper.class);
this.context.getBean(BitronixDependentBeanFactoryPostProcessor.class);
this.context.getBean(JtaTransactionManager.class);
}
@Test
public void defaultBitronixServerId() throws UnknownHostException {
this.context = new AnnotationConfigApplicationContext(
JtaPropertiesConfiguration.class, BitronixJtaConfiguration.class);
String serverId = this.context.getBean(bitronix.tm.Configuration.class)
.getServerId();
assertThat(serverId).isEqualTo(InetAddress.getLocalHost().getHostAddress());
}
@Test
public void customBitronixServerId() throws UnknownHostException {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.transactionManagerId:custom");
this.context.register(JtaPropertiesConfiguration.class,
BitronixJtaConfiguration.class);
this.context.refresh();
String serverId = this.context.getBean(bitronix.tm.Configuration.class)
.getServerId();
assertThat(serverId).isEqualTo("custom");
}
@Test
public void defaultAtomikosTransactionManagerName() throws UnknownHostException {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.logDir:target/transaction-logs");
this.context.register(JtaPropertiesConfiguration.class,
AtomikosJtaConfiguration.class);
this.context.refresh();
File epochFile = new File("target/transaction-logs/"
+ InetAddress.getLocalHost().getHostAddress() + ".tm0.epoch");
assertThat(epochFile.isFile()).isTrue();
}
@Test
public void customAtomikosTransactionManagerName() throws BeansException, Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.transactionManagerId:custom",
"spring.jta.logDir:target/transaction-logs");
this.context.register(JtaPropertiesConfiguration.class,
AtomikosJtaConfiguration.class);
this.context.refresh();
File epochFile = new File("target/transaction-logs/custom0.epoch");
assertThat(epochFile.isFile()).isTrue();
}
@Test
public void atomikosConnectionFactoryPoolConfiguration() {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.atomikos.connectionfactory.minPoolSize:5",
"spring.jta.atomikos.connectionfactory.maxPoolSize:10");
this.context.register(JtaPropertiesConfiguration.class,
AtomikosJtaConfiguration.class, PoolConfiguration.class);
this.context.refresh();
AtomikosConnectionFactoryBean connectionFactory = this.context
.getBean(AtomikosConnectionFactoryBean.class);
assertThat(connectionFactory.getMinPoolSize()).isEqualTo(5);
assertThat(connectionFactory.getMaxPoolSize()).isEqualTo(10);
}
@Test
public void bitronixConnectionFactoryPoolConfiguration() {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.bitronix.connectionfactory.minPoolSize:5",
"spring.jta.bitronix.connectionfactory.maxPoolSize:10");
this.context.register(JtaPropertiesConfiguration.class,
BitronixJtaConfiguration.class, PoolConfiguration.class);
this.context.refresh();
PoolingConnectionFactoryBean connectionFactory = this.context
.getBean(PoolingConnectionFactoryBean.class);
assertThat(connectionFactory.getMinPoolSize()).isEqualTo(5);
assertThat(connectionFactory.getMaxPoolSize()).isEqualTo(10);
}
@Test
public void atomikosDataSourcePoolConfiguration() {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.atomikos.datasource.minPoolSize:5",
"spring.jta.atomikos.datasource.maxPoolSize:10");
this.context.register(JtaPropertiesConfiguration.class,
AtomikosJtaConfiguration.class, PoolConfiguration.class);
this.context.refresh();
AtomikosDataSourceBean dataSource = this.context
.getBean(AtomikosDataSourceBean.class);
assertThat(dataSource.getMinPoolSize()).isEqualTo(5);
assertThat(dataSource.getMaxPoolSize()).isEqualTo(10);
}
@Test
public void bitronixDataSourcePoolConfiguration() {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.bitronix.datasource.minPoolSize:5",
"spring.jta.bitronix.datasource.maxPoolSize:10");
this.context.register(JtaPropertiesConfiguration.class,
BitronixJtaConfiguration.class, PoolConfiguration.class);
this.context.refresh();
PoolingDataSourceBean dataSource = this.context
.getBean(PoolingDataSourceBean.class);
assertThat(dataSource.getMinPoolSize()).isEqualTo(5);
assertThat(dataSource.getMaxPoolSize()).isEqualTo(10);
}
@Test
public void atomikosCustomizeJtaTransactionManagerUsingProperties() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"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);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@Test
public void bitronixCustomizeJtaTransactionManagerUsingProperties() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"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);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@Configuration
@EnableConfigurationProperties(JtaProperties.class)
public static class JtaPropertiesConfiguration {
}
@Configuration
public static class CustomTransactionManagerConfig {
@Bean
public PlatformTransactionManager transactionManager() {
return mock(PlatformTransactionManager.class);
}
}
@Configuration
public static class PoolConfiguration {
@Bean
public ConnectionFactory pooledConnectionFactory(
XAConnectionFactoryWrapper wrapper) throws Exception {
XAConnectionFactory connectionFactory = mock(XAConnectionFactory.class);
XAConnection connection = mock(XAConnection.class);
XASession session = mock(XASession.class);
TemporaryQueue queue = mock(TemporaryQueue.class);
XAResource resource = mock(XAResource.class);
given(connectionFactory.createXAConnection()).willReturn(connection);
given(connection.createXASession()).willReturn(session);
given(session.createTemporaryQueue()).willReturn(queue);
given(session.getXAResource()).willReturn(resource);
return wrapper.wrapConnectionFactory(connectionFactory);
}
@Bean
public DataSource pooledDataSource(XADataSourceWrapper wrapper) throws Exception {
XADataSource dataSource = mock(XADataSource.class);
return wrapper.wrapDataSource(dataSource);
}
}
}