/* * 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); } } }