parent
900085628a
commit
ef02cc9bff
|
|
@ -12,8 +12,6 @@ description = "Spring Boot AutoConfigure"
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":spring-boot-project:spring-boot"))
|
api(project(":spring-boot-project:spring-boot"))
|
||||||
|
|
||||||
optional("com.atomikos:transactions-jdbc")
|
|
||||||
optional("com.atomikos:transactions-jta")
|
|
||||||
optional("com.fasterxml.jackson.core:jackson-databind")
|
optional("com.fasterxml.jackson.core:jackson-databind")
|
||||||
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor")
|
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor")
|
||||||
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
|
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
|
||||||
|
|
@ -94,6 +92,7 @@ dependencies {
|
||||||
optional("org.aspectj:aspectjweaver")
|
optional("org.aspectj:aspectjweaver")
|
||||||
optional("org.eclipse.jetty:jetty-webapp") {
|
optional("org.eclipse.jetty:jetty-webapp") {
|
||||||
exclude group: "javax.servlet", module: "javax.servlet-api"
|
exclude group: "javax.servlet", module: "javax.servlet-api"
|
||||||
|
exclude(group: "org.eclipse.jetty", module: "jetty-jndi")
|
||||||
}
|
}
|
||||||
optional("org.eclipse.jetty:jetty-reactive-httpclient")
|
optional("org.eclipse.jetty:jetty-reactive-httpclient")
|
||||||
optional("org.eclipse.jetty.websocket:javax-websocket-server-impl") {
|
optional("org.eclipse.jetty.websocket:javax-websocket-server-impl") {
|
||||||
|
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2020 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
|
|
||||||
*
|
|
||||||
* https://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.util.Properties;
|
|
||||||
|
|
||||||
import javax.jms.Message;
|
|
||||||
import javax.transaction.TransactionManager;
|
|
||||||
import javax.transaction.UserTransaction;
|
|
||||||
|
|
||||||
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.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.jdbc.XADataSourceWrapper;
|
|
||||||
import org.springframework.boot.jms.XAConnectionFactoryWrapper;
|
|
||||||
import org.springframework.boot.jta.atomikos.AtomikosDependsOnBeanFactoryPostProcessor;
|
|
||||||
import org.springframework.boot.jta.atomikos.AtomikosProperties;
|
|
||||||
import org.springframework.boot.jta.atomikos.AtomikosXAConnectionFactoryWrapper;
|
|
||||||
import org.springframework.boot.jta.atomikos.AtomikosXADataSourceWrapper;
|
|
||||||
import org.springframework.boot.system.ApplicationHome;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JTA Configuration for <A href="https://www.atomikos.com/">Atomikos</a>.
|
|
||||||
*
|
|
||||||
* @author Josh Long
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @author Kazuki Shimizu
|
|
||||||
*/
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@EnableConfigurationProperties({ AtomikosProperties.class, JtaProperties.class })
|
|
||||||
@ConditionalOnClass({ JtaTransactionManager.class, UserTransactionManager.class })
|
|
||||||
@ConditionalOnMissingBean(org.springframework.transaction.TransactionManager.class)
|
|
||||||
class AtomikosJtaConfiguration {
|
|
||||||
|
|
||||||
@Bean(initMethod = "init", destroyMethod = "shutdownWait")
|
|
||||||
@ConditionalOnMissingBean(UserTransactionService.class)
|
|
||||||
UserTransactionServiceImp userTransactionService(AtomikosProperties atomikosProperties,
|
|
||||||
JtaProperties jtaProperties) {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
if (StringUtils.hasText(jtaProperties.getTransactionManagerId())) {
|
|
||||||
properties.setProperty("com.atomikos.icatch.tm_unique_name", jtaProperties.getTransactionManagerId());
|
|
||||||
}
|
|
||||||
properties.setProperty("com.atomikos.icatch.log_base_dir", getLogBaseDir(jtaProperties));
|
|
||||||
properties.putAll(atomikosProperties.asProperties());
|
|
||||||
return new UserTransactionServiceImp(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getLogBaseDir(JtaProperties jtaProperties) {
|
|
||||||
if (StringUtils.hasLength(jtaProperties.getLogDir())) {
|
|
||||||
return jtaProperties.getLogDir();
|
|
||||||
}
|
|
||||||
File home = new ApplicationHome().getDir();
|
|
||||||
return new File(home, "transaction-logs").getAbsolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(initMethod = "init", destroyMethod = "close")
|
|
||||||
@ConditionalOnMissingBean(TransactionManager.class)
|
|
||||||
UserTransactionManager atomikosTransactionManager(UserTransactionService userTransactionService) throws Exception {
|
|
||||||
UserTransactionManager manager = new UserTransactionManager();
|
|
||||||
manager.setStartupTransactionService(false);
|
|
||||||
manager.setForceShutdown(true);
|
|
||||||
return manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(XADataSourceWrapper.class)
|
|
||||||
AtomikosXADataSourceWrapper xaDataSourceWrapper() {
|
|
||||||
return new AtomikosXADataSourceWrapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
static AtomikosDependsOnBeanFactoryPostProcessor atomikosDependsOnBeanFactoryPostProcessor() {
|
|
||||||
return new AtomikosDependsOnBeanFactoryPostProcessor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
JtaTransactionManager transactionManager(UserTransaction userTransaction, TransactionManager transactionManager,
|
|
||||||
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
|
||||||
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, transactionManager);
|
|
||||||
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(jtaTransactionManager));
|
|
||||||
return jtaTransactionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@ConditionalOnClass(Message.class)
|
|
||||||
static class AtomikosJtaJmsConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(XAConnectionFactoryWrapper.class)
|
|
||||||
AtomikosXAConnectionFactoryWrapper xaConnectionFactoryWrapper() {
|
|
||||||
return new AtomikosXAConnectionFactoryWrapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -40,7 +40,7 @@ import org.springframework.context.annotation.Import;
|
||||||
@ConditionalOnProperty(prefix = "spring.jta", value = "enabled", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = "spring.jta", value = "enabled", matchIfMissing = true)
|
||||||
@AutoConfigureBefore({ XADataSourceAutoConfiguration.class, ActiveMQAutoConfiguration.class,
|
@AutoConfigureBefore({ XADataSourceAutoConfiguration.class, ActiveMQAutoConfiguration.class,
|
||||||
ArtemisAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
|
ArtemisAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
|
||||||
@Import({ JndiJtaConfiguration.class, AtomikosJtaConfiguration.class })
|
@Import(JndiJtaConfiguration.class)
|
||||||
public class JtaAutoConfiguration {
|
public class JtaAutoConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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 org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* External configuration properties for a {@link JtaTransactionManager} created by
|
|
||||||
* Spring. All {@literal spring.jta.} properties are also applied to the appropriate
|
|
||||||
* vendor specific configuration.
|
|
||||||
*
|
|
||||||
* @author Josh Long
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
@ConfigurationProperties(prefix = "spring.jta", ignoreUnknownFields = true)
|
|
||||||
public class JtaProperties {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction logs directory.
|
|
||||||
*/
|
|
||||||
private String logDir;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction manager unique identifier.
|
|
||||||
*/
|
|
||||||
private String transactionManagerId;
|
|
||||||
|
|
||||||
public void setLogDir(String logDir) {
|
|
||||||
this.logDir = logDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLogDir() {
|
|
||||||
return this.logDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTransactionManagerId() {
|
|
||||||
return this.transactionManagerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTransactionManagerId(String transactionManagerId) {
|
|
||||||
this.transactionManagerId = transactionManagerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -75,6 +75,7 @@ import org.springframework.orm.jpa.JpaVendorAdapter;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
|
|
@ -214,7 +215,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void jtaDefaultPlatform() {
|
void jtaDefaultPlatform() {
|
||||||
contextRunner().withConfiguration(AutoConfigurations.of(JtaAutoConfiguration.class))
|
contextRunner().withUserConfiguration(JtaTransactionManagerConfiguration.class)
|
||||||
.run(assertJtaPlatform(SpringJtaPlatform.class));
|
.run(assertJtaPlatform(SpringJtaPlatform.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +299,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void providerDisablesAutoCommitIsNotConfiguredWithJta() {
|
void providerDisablesAutoCommitIsNotConfiguredWithJta() {
|
||||||
contextRunner().withConfiguration(AutoConfigurations.of(JtaAutoConfiguration.class))
|
contextRunner().withUserConfiguration(JtaTransactionManagerConfiguration.class)
|
||||||
.withPropertyValues("spring.datasource.type:" + HikariDataSource.class.getName(),
|
.withPropertyValues("spring.datasource.type:" + HikariDataSource.class.getName(),
|
||||||
"spring.datasource.hikari.auto-commit:false")
|
"spring.datasource.hikari.auto-commit:false")
|
||||||
.run((context) -> {
|
.run((context) -> {
|
||||||
|
|
@ -677,4 +678,17 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class JtaTransactionManagerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
JtaTransactionManager jtaTransactionManager() {
|
||||||
|
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
|
||||||
|
jtaTransactionManager.setUserTransaction(mock(UserTransaction.class));
|
||||||
|
jtaTransactionManager.setTransactionManager(mock(TransactionManager.class));
|
||||||
|
return jtaTransactionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,29 +16,16 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.transaction.jta;
|
package org.springframework.boot.autoconfigure.transaction.jta;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.jms.ConnectionFactory;
|
|
||||||
import javax.jms.TemporaryQueue;
|
|
||||||
import javax.jms.XAConnection;
|
|
||||||
import javax.jms.XAConnectionFactory;
|
|
||||||
import javax.jms.XASession;
|
|
||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
import javax.naming.InitialContext;
|
import javax.naming.InitialContext;
|
||||||
import javax.naming.NamingException;
|
import javax.naming.NamingException;
|
||||||
import javax.sql.DataSource;
|
|
||||||
import javax.sql.XADataSource;
|
|
||||||
import javax.transaction.TransactionManager;
|
import javax.transaction.TransactionManager;
|
||||||
import javax.transaction.UserTransaction;
|
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.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||||
|
|
@ -49,21 +36,13 @@ import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
|
||||||
import org.junit.jupiter.api.extension.ParameterContext;
|
import org.junit.jupiter.api.extension.ParameterContext;
|
||||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||||
import org.junit.jupiter.api.extension.ParameterResolver;
|
import org.junit.jupiter.api.extension.ParameterResolver;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.osjava.sj.loader.JndiLoader;
|
import org.osjava.sj.loader.JndiLoader;
|
||||||
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
|
|
||||||
import org.springframework.boot.jdbc.XADataSourceWrapper;
|
|
||||||
import org.springframework.boot.jms.XAConnectionFactoryWrapper;
|
|
||||||
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.test.util.TestPropertyValues;
|
import org.springframework.boot.test.util.TestPropertyValues;
|
||||||
import org.springframework.boot.testsupport.BuildOutput;
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
@ -72,7 +51,6 @@ import org.springframework.transaction.jta.UserTransactionAdapter;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.mockito.BDDMockito.given;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -86,9 +64,6 @@ import static org.mockito.Mockito.mock;
|
||||||
*/
|
*/
|
||||||
class JtaAutoConfigurationTests {
|
class JtaAutoConfigurationTests {
|
||||||
|
|
||||||
private final File atomikosLogs = new File(new BuildOutput(JtaAutoConfigurationTests.class).getRootLocation(),
|
|
||||||
"atomikos-logs");
|
|
||||||
|
|
||||||
private AnnotationConfigApplicationContext context;
|
private AnnotationConfigApplicationContext context;
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
|
|
@ -132,81 +107,14 @@ class JtaAutoConfigurationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void disableJtaSupport() {
|
@ExtendWith(JndiExtension.class)
|
||||||
|
void disableJtaSupport(InitialContext initialContext) throws NamingException {
|
||||||
|
new JndiEntry("java:comp/UserTransaction", UserTransaction.class).register(initialContext);
|
||||||
this.context = new AnnotationConfigApplicationContext();
|
this.context = new AnnotationConfigApplicationContext();
|
||||||
TestPropertyValues.of("spring.jta.enabled:false").applyTo(this.context);
|
TestPropertyValues.of("spring.jta.enabled:false").applyTo(this.context);
|
||||||
this.context.register(JtaAutoConfiguration.class);
|
this.context.register(JtaAutoConfiguration.class);
|
||||||
this.context.refresh();
|
this.context.refresh();
|
||||||
assertThat(this.context.getBeansOfType(JtaTransactionManager.class)).isEmpty();
|
assertThat(this.context.getBeansOfType(JtaTransactionManager.class)).isEmpty();
|
||||||
assertThat(this.context.getBeansOfType(XADataSourceWrapper.class)).isEmpty();
|
|
||||||
assertThat(this.context.getBeansOfType(XAConnectionFactoryWrapper.class)).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void atomikosSanityCheck() {
|
|
||||||
this.context = new AnnotationConfigApplicationContext();
|
|
||||||
TestPropertyValues.of("spring.jta.log-dir:" + this.atomikosLogs).applyTo(this.context);
|
|
||||||
this.context.register(JtaProperties.class, AtomikosJtaConfiguration.class);
|
|
||||||
this.context.refresh();
|
|
||||||
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
|
|
||||||
void defaultAtomikosTransactionManagerName(@TempDir Path dir) {
|
|
||||||
this.context = new AnnotationConfigApplicationContext();
|
|
||||||
File logs = new File(dir.toFile(), "jta");
|
|
||||||
TestPropertyValues.of("spring.jta.logDir:" + logs.getAbsolutePath()).applyTo(this.context);
|
|
||||||
this.context.register(AtomikosJtaConfiguration.class);
|
|
||||||
this.context.refresh();
|
|
||||||
|
|
||||||
File epochFile = new File(logs, "tmlog0.log");
|
|
||||||
assertThat(epochFile.isFile()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void atomikosConnectionFactoryPoolConfiguration() {
|
|
||||||
this.context = new AnnotationConfigApplicationContext();
|
|
||||||
TestPropertyValues.of("spring.jta.atomikos.connectionfactory.minPoolSize:5",
|
|
||||||
"spring.jta.atomikos.connectionfactory.maxPoolSize:10", "spring.jta.log-dir:" + this.atomikosLogs)
|
|
||||||
.applyTo(this.context);
|
|
||||||
this.context.register(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
|
|
||||||
void atomikosDataSourcePoolConfiguration() {
|
|
||||||
this.context = new AnnotationConfigApplicationContext();
|
|
||||||
TestPropertyValues.of("spring.jta.atomikos.datasource.minPoolSize:5",
|
|
||||||
"spring.jta.atomikos.datasource.maxPoolSize:10", "spring.jta.log-dir:" + this.atomikosLogs)
|
|
||||||
.applyTo(this.context);
|
|
||||||
this.context.register(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
|
|
||||||
void atomikosCustomizeJtaTransactionManagerUsingProperties() {
|
|
||||||
this.context = new AnnotationConfigApplicationContext();
|
|
||||||
TestPropertyValues.of("spring.transaction.default-timeout:30",
|
|
||||||
"spring.transaction.rollback-on-commit-failure:true", "spring.jta.log-dir:" + this.atomikosLogs)
|
|
||||||
.applyTo(this.context);
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
|
@ -219,31 +127,6 @@ class JtaAutoConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
static class PoolConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
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
|
|
||||||
DataSource pooledDataSource(XADataSourceWrapper wrapper) throws Exception {
|
|
||||||
XADataSource dataSource = mock(XADataSource.class);
|
|
||||||
return wrapper.wrapDataSource(dataSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class JndiEntry {
|
private static final class JndiEntry {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
|
||||||
|
|
@ -793,10 +793,9 @@ features.caching.provider.caffeine=io.caching.provider.caffeine
|
||||||
features.caching.provider.simple=io.caching.provider.simple
|
features.caching.provider.simple=io.caching.provider.simple
|
||||||
features.caching.provider.none=io.caching.provider.none
|
features.caching.provider.none=io.caching.provider.none
|
||||||
features.jta=io.jta
|
features.jta=io.jta
|
||||||
features.jta.atomikos=io.jta.atomikos
|
features.jta.javaee=io.jta.jakartaee
|
||||||
features.jta.javaee=io.jta.javaee
|
|
||||||
features.jta.mixing-xa-and-non-xa-connections=io.jta.mixing-xa-and-non-xa-connections
|
features.jta.mixing-xa-and-non-xa-connections=io.jta.mixing-xa-and-non-xa-connections
|
||||||
features.jta.supporting-alternative-embedded-transaction-manager=io.jta.supporting-alternative-embedded-transaction-manager
|
features.jta.supporting-alternative-embedded-transaction-manager=io.jta.supporting-embedded-transaction-manager
|
||||||
features.email=io.email
|
features.email=io.email
|
||||||
features.quartz=io.quartz
|
features.quartz=io.quartz
|
||||||
features.resttemplate=io.rest-client.resttemplate
|
features.resttemplate=io.rest-client.resttemplate
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
[[io.jta]]
|
[[io.jta]]
|
||||||
== Distributed Transactions with JTA
|
== Distributed Transactions with JTA
|
||||||
Spring Boot supports distributed JTA transactions across multiple XA resources by using an https://www.atomikos.com/[Atomikos] embedded transaction manager.
|
Spring Boot supports distributed JTA transactions across multiple XA resources by using a transaction manager retrieved from JNDI.
|
||||||
JTA transactions are also supported when deploying to a suitable Java EE Application Server.
|
|
||||||
|
|
||||||
When a JTA environment is detected, Spring's `JtaTransactionManager` is used to manage transactions.
|
When a JTA environment is detected, Spring's `JtaTransactionManager` is used to manage transactions.
|
||||||
Auto-configured JMS, DataSource, and JPA beans are upgraded to support XA transactions.
|
Auto-configured JMS, DataSource, and JPA beans are upgraded to support XA transactions.
|
||||||
|
|
@ -10,28 +9,11 @@ If you are within a JTA environment and still want to use local transactions, yo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[io.jta.atomikos]]
|
[[io.jta.jakartaee]]
|
||||||
=== Using an Atomikos Transaction Manager
|
=== Using a Jakarta EE Managed Transaction Manager
|
||||||
https://www.atomikos.com/[Atomikos] is a popular open source transaction manager which can be embedded into your Spring Boot application.
|
If you package your Spring Boot application as a `war` or `ear` file and deploy it to a Jakarta EE application server, you can use your application server's built-in transaction manager.
|
||||||
You can use the `spring-boot-starter-jta-atomikos` starter to pull in the appropriate Atomikos libraries.
|
|
||||||
Spring Boot auto-configures Atomikos and ensures that appropriate `depends-on` settings are applied to your Spring beans for correct startup and shutdown ordering.
|
|
||||||
|
|
||||||
By default, Atomikos transaction logs are written to a `transaction-logs` directory in your application's home directory (the directory in which your application jar file resides).
|
|
||||||
You can customize the location of this directory by setting a configprop:spring.jta.log-dir[] property in your `application.properties` file.
|
|
||||||
Properties starting with `spring.jta.atomikos.properties` can also be used to customize the Atomikos `UserTransactionServiceImp`.
|
|
||||||
See the {spring-boot-module-api}/jta/atomikos/AtomikosProperties.html[`AtomikosProperties` Javadoc] for complete details.
|
|
||||||
|
|
||||||
NOTE: To ensure that multiple transaction managers can safely coordinate the same resource managers, each Atomikos instance must be configured with a unique ID.
|
|
||||||
By default, this ID is the IP address of the machine on which Atomikos is running.
|
|
||||||
To ensure uniqueness in production, you should configure the configprop:spring.jta.transaction-manager-id[] property with a different value for each instance of your application.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[io.jta.javaee]]
|
|
||||||
=== Using a Java EE Managed Transaction Manager
|
|
||||||
If you package your Spring Boot application as a `war` or `ear` file and deploy it to a Java EE application server, you can use your application server's built-in transaction manager.
|
|
||||||
Spring Boot tries to auto-configure a transaction manager by looking at common JNDI locations (`java:comp/UserTransaction`, `java:comp/TransactionManager`, and so on).
|
Spring Boot tries to auto-configure a transaction manager by looking at common JNDI locations (`java:comp/UserTransaction`, `java:comp/TransactionManager`, and so on).
|
||||||
If you use a transaction service provided by your application server, you generally also want to ensure that all resources are managed by the server and exposed over JNDI.
|
When using a transaction service provided by your application server, you generally also want to ensure that all resources are managed by the server and exposed over JNDI.
|
||||||
Spring Boot tries to auto-configure JMS by looking for a `ConnectionFactory` at the JNDI path (`java:/JmsXA` or `java:/XAConnectionFactory`), and you can use the <<data#data.sql.datasource.jndi, configprop:spring.datasource.jndi-name[] property>> to configure your `DataSource`.
|
Spring Boot tries to auto-configure JMS by looking for a `ConnectionFactory` at the JNDI path (`java:/JmsXA` or `java:/XAConnectionFactory`), and you can use the <<data#data.sql.datasource.jndi, configprop:spring.datasource.jndi-name[] property>> to configure your `DataSource`.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,10 +47,8 @@ include::{docs-java}/io/jta/mixingxaandnonxaconnections/xa/MyBean.java[tag=*]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[io.jta.supporting-alternative-embedded-transaction-manager]]
|
[[io.jta.supporting-embedded-transaction-manager]]
|
||||||
=== Supporting an Alternative Embedded Transaction Manager
|
=== Supporting an Embedded Transaction Manager
|
||||||
The {spring-boot-module-code}/jms/XAConnectionFactoryWrapper.java[`XAConnectionFactoryWrapper`] and {spring-boot-module-code}/jdbc/XADataSourceWrapper.java[`XADataSourceWrapper`] interfaces can be used to support alternative embedded transaction managers.
|
The {spring-boot-module-code}/jms/XAConnectionFactoryWrapper.java[`XAConnectionFactoryWrapper`] and {spring-boot-module-code}/jdbc/XADataSourceWrapper.java[`XADataSourceWrapper`] interfaces can be used to support embedded transaction managers.
|
||||||
The interfaces are responsible for wrapping `XAConnectionFactory` and `XADataSource` beans and exposing them as regular `ConnectionFactory` and `DataSource` beans, which transparently enroll in the distributed transaction.
|
The interfaces are responsible for wrapping `XAConnectionFactory` and `XADataSource` beans and exposing them as regular `ConnectionFactory` and `DataSource` beans, which transparently enroll in the distributed transaction.
|
||||||
DataSource and JMS auto-configuration use JTA variants, provided you have a `JtaTransactionManager` bean and appropriate XA wrapper beans registered within your `ApplicationContext`.
|
DataSource and JMS auto-configuration use JTA variants, provided you have a `JtaTransactionManager` bean and appropriate XA wrapper beans registered within your `ApplicationContext`.
|
||||||
|
|
||||||
The {spring-boot-module-code}/jta/atomikos/AtomikosXAConnectionFactoryWrapper.java[AtomikosXAConnectionFactoryWrapper] and {spring-boot-module-code}/jta/atomikos/AtomikosXADataSourceWrapper.java[AtomikosXADataSourceWrapper] provide good examples of how to write XA wrappers.
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
plugins {
|
|
||||||
id "org.springframework.boot.starter"
|
|
||||||
}
|
|
||||||
|
|
||||||
description = "Starter for JTA transactions using Atomikos"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
|
|
||||||
api("com.atomikos:transactions-jms")
|
|
||||||
api("com.atomikos:transactions-jta")
|
|
||||||
api("com.atomikos:transactions-jdbc")
|
|
||||||
api("jakarta.transaction:jakarta.transaction-api")
|
|
||||||
}
|
|
||||||
|
|
@ -22,9 +22,6 @@ dependencies {
|
||||||
api("org.springframework:spring-context")
|
api("org.springframework:spring-context")
|
||||||
|
|
||||||
optional("ch.qos.logback:logback-classic")
|
optional("ch.qos.logback:logback-classic")
|
||||||
optional("com.atomikos:transactions-jdbc")
|
|
||||||
optional("com.atomikos:transactions-jms")
|
|
||||||
optional("com.atomikos:transactions-jta")
|
|
||||||
optional("com.fasterxml.jackson.core:jackson-databind")
|
optional("com.fasterxml.jackson.core:jackson-databind")
|
||||||
optional("com.h2database:h2")
|
optional("com.h2database:h2")
|
||||||
optional("com.google.code.gson:gson")
|
optional("com.google.code.gson:gson")
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanNameAware;
|
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring friendly version of {@link com.atomikos.jms.AtomikosConnectionFactoryBean}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
@ConfigurationProperties(prefix = "spring.jta.atomikos.connectionfactory")
|
|
||||||
public class AtomikosConnectionFactoryBean extends com.atomikos.jms.AtomikosConnectionFactoryBean
|
|
||||||
implements BeanNameAware, InitializingBean, DisposableBean {
|
|
||||||
|
|
||||||
private String beanName;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBeanName(String name) {
|
|
||||||
this.beanName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
|
||||||
if (!StringUtils.hasLength(getUniqueResourceName())) {
|
|
||||||
setUniqueResourceName(this.beanName);
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() throws Exception {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanNameAware;
|
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring friendly version of {@link com.atomikos.jdbc.AtomikosDataSourceBean}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
@ConfigurationProperties(prefix = "spring.jta.atomikos.datasource")
|
|
||||||
public class AtomikosDataSourceBean extends com.atomikos.jdbc.AtomikosDataSourceBean
|
|
||||||
implements BeanNameAware, InitializingBean, DisposableBean {
|
|
||||||
|
|
||||||
private String beanName;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBeanName(String name) {
|
|
||||||
this.beanName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
|
||||||
if (!StringUtils.hasLength(getUniqueResourceName())) {
|
|
||||||
setUniqueResourceName(this.beanName);
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() throws Exception {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.atomikos.icatch.jta.UserTransactionManager;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|
||||||
import org.springframework.core.Ordered;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link BeanFactoryPostProcessor} to automatically setup the recommended
|
|
||||||
* {@link BeanDefinition#setDependsOn(String[]) dependsOn} settings for
|
|
||||||
* <a href="https://www.atomikos.com/Documentation/SpringIntegration">correct Atomikos
|
|
||||||
* ordering</a>.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
public class AtomikosDependsOnBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
|
|
||||||
|
|
||||||
private static final String[] NO_BEANS = {};
|
|
||||||
|
|
||||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
|
||||||
String[] transactionManagers = beanFactory.getBeanNamesForType(UserTransactionManager.class, true, false);
|
|
||||||
for (String transactionManager : transactionManagers) {
|
|
||||||
addTransactionManagerDependencies(beanFactory, transactionManager);
|
|
||||||
}
|
|
||||||
addMessageDrivenContainerDependencies(beanFactory, transactionManagers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTransactionManagerDependencies(ConfigurableListableBeanFactory beanFactory,
|
|
||||||
String transactionManager) {
|
|
||||||
BeanDefinition bean = beanFactory.getBeanDefinition(transactionManager);
|
|
||||||
Set<String> dependsOn = new LinkedHashSet<>(asList(bean.getDependsOn()));
|
|
||||||
int initialSize = dependsOn.size();
|
|
||||||
addDependencies(beanFactory, "javax.jms.ConnectionFactory", dependsOn);
|
|
||||||
addDependencies(beanFactory, "javax.sql.DataSource", dependsOn);
|
|
||||||
if (dependsOn.size() != initialSize) {
|
|
||||||
bean.setDependsOn(StringUtils.toStringArray(dependsOn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addMessageDrivenContainerDependencies(ConfigurableListableBeanFactory beanFactory,
|
|
||||||
String[] transactionManagers) {
|
|
||||||
String[] messageDrivenContainers = getBeanNamesForType(beanFactory,
|
|
||||||
"com.atomikos.jms.extra.MessageDrivenContainer");
|
|
||||||
for (String messageDrivenContainer : messageDrivenContainers) {
|
|
||||||
BeanDefinition bean = beanFactory.getBeanDefinition(messageDrivenContainer);
|
|
||||||
Set<String> dependsOn = new LinkedHashSet<>(asList(bean.getDependsOn()));
|
|
||||||
dependsOn.addAll(asList(transactionManagers));
|
|
||||||
bean.setDependsOn(StringUtils.toStringArray(dependsOn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addDependencies(ConfigurableListableBeanFactory beanFactory, String type, Set<String> dependsOn) {
|
|
||||||
dependsOn.addAll(asList(getBeanNamesForType(beanFactory, type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getBeanNamesForType(ConfigurableListableBeanFactory beanFactory, String type) {
|
|
||||||
try {
|
|
||||||
return beanFactory.getBeanNamesForType(Class.forName(type), true, false);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException | NoClassDefFoundError ex) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
return NO_BEANS;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> asList(String[] array) {
|
|
||||||
return (array != null) ? Arrays.asList(array) : Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOrder() {
|
|
||||||
return this.order;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOrder(int order) {
|
|
||||||
this.order = order;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,425 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bean friendly variant of
|
|
||||||
* <a href="https://www.atomikos.com/Documentation/JtaProperties">Atomikos configuration
|
|
||||||
* properties</a>. Allows for setter based configuration and is amiable to relaxed data
|
|
||||||
* binding.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @since 1.2.0
|
|
||||||
* @see #asProperties()
|
|
||||||
*/
|
|
||||||
@ConfigurationProperties(prefix = "spring.jta.atomikos.properties")
|
|
||||||
public class AtomikosProperties {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction manager implementation that should be started.
|
|
||||||
*/
|
|
||||||
private String service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum timeout that can be allowed for transactions.
|
|
||||||
*/
|
|
||||||
private Duration maxTimeout = Duration.ofMillis(300000);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default timeout for JTA transactions.
|
|
||||||
*/
|
|
||||||
private Duration defaultJtaTimeout = Duration.ofMillis(10000);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of active transactions.
|
|
||||||
*/
|
|
||||||
private int maxActives = 50;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to enable disk logging.
|
|
||||||
*/
|
|
||||||
private boolean enableLogging = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The transaction manager's unique name. Defaults to the machine's IP address. If you
|
|
||||||
* plan to run more than one transaction manager against one database you must set
|
|
||||||
* this property to a unique value.
|
|
||||||
*/
|
|
||||||
private String transactionManagerUniqueName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether sub-transactions should be joined when possible.
|
|
||||||
*/
|
|
||||||
private boolean serialJtaTransactions = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify whether sub-transactions are allowed.
|
|
||||||
*/
|
|
||||||
private boolean allowSubTransactions = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether a VM shutdown should trigger forced shutdown of the transaction core.
|
|
||||||
*/
|
|
||||||
private boolean forceShutdownOnVmExit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How long should normal shutdown (no-force) wait for transactions to complete.
|
|
||||||
*/
|
|
||||||
private long defaultMaxWaitTimeOnShutdown = Long.MAX_VALUE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transactions log file base name.
|
|
||||||
*/
|
|
||||||
private String logBaseName = "tmlog";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Directory in which the log files should be stored. Defaults to the current working
|
|
||||||
* directory.
|
|
||||||
*/
|
|
||||||
private String logBaseDir;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interval between checkpoints, expressed as the number of log writes between two
|
|
||||||
* checkpoints. A checkpoint reduces the log file size at the expense of adding some
|
|
||||||
* overhead in the runtime.
|
|
||||||
*/
|
|
||||||
private long checkpointInterval = 500;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to use different (and concurrent) threads for two-phase commit on the
|
|
||||||
* participating resources.
|
|
||||||
*/
|
|
||||||
private boolean threadedTwoPhaseCommit;
|
|
||||||
|
|
||||||
private final Recovery recovery = new Recovery();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the transaction manager implementation that should be started. There is
|
|
||||||
* no default value and this must be set. Generally,
|
|
||||||
* {@literal com.atomikos.icatch.standalone.UserTransactionServiceFactory} is the
|
|
||||||
* value you should set.
|
|
||||||
* @param service the service
|
|
||||||
*/
|
|
||||||
public void setService(String service) {
|
|
||||||
this.service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getService() {
|
|
||||||
return this.service;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the maximum timeout that can be allowed for transactions. Defaults to
|
|
||||||
* {@literal 300000}. This means that calls to UserTransaction.setTransactionTimeout()
|
|
||||||
* with a value higher than configured here will be max'ed to this value.
|
|
||||||
* @param maxTimeout the max timeout
|
|
||||||
*/
|
|
||||||
public void setMaxTimeout(Duration maxTimeout) {
|
|
||||||
this.maxTimeout = maxTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Duration getMaxTimeout() {
|
|
||||||
return this.maxTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default timeout for JTA transactions (optional, defaults to {@literal 10000}
|
|
||||||
* ms).
|
|
||||||
* @param defaultJtaTimeout the default JTA timeout
|
|
||||||
*/
|
|
||||||
public void setDefaultJtaTimeout(Duration defaultJtaTimeout) {
|
|
||||||
this.defaultJtaTimeout = defaultJtaTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Duration getDefaultJtaTimeout() {
|
|
||||||
return this.defaultJtaTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the maximum number of active transactions. Defaults to {@literal 50}. A
|
|
||||||
* negative value means infinite amount. You will get an {@code IllegalStateException}
|
|
||||||
* with error message "Max number of active transactions reached" if you call
|
|
||||||
* {@code UserTransaction.begin()} while there are already n concurrent transactions
|
|
||||||
* running, n being this value.
|
|
||||||
* @param maxActives the max activities
|
|
||||||
*/
|
|
||||||
public void setMaxActives(int maxActives) {
|
|
||||||
this.maxActives = maxActives;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxActives() {
|
|
||||||
return this.maxActives;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies if disk logging should be enabled or not. Defaults to true. It is useful
|
|
||||||
* for JUnit testing, or to profile code without seeing the transaction manager's
|
|
||||||
* activity as a hot spot but this should never be disabled on production or data
|
|
||||||
* integrity cannot be guaranteed.
|
|
||||||
* @param enableLogging if logging is enabled
|
|
||||||
*/
|
|
||||||
public void setEnableLogging(boolean enableLogging) {
|
|
||||||
this.enableLogging = enableLogging;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnableLogging() {
|
|
||||||
return this.enableLogging;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the transaction manager's unique name. Defaults to the machine's IP
|
|
||||||
* address. If you plan to run more than one transaction manager against one database
|
|
||||||
* you must set this property to a unique value or you might run into duplicate
|
|
||||||
* transaction ID (XID) problems that can be quite subtle (example:
|
|
||||||
* {@literal https://fogbugz.atomikos.com/default.asp?community.6.2225.7}). If
|
|
||||||
* multiple instances need to use the same properties file then the easiest way to
|
|
||||||
* ensure uniqueness for this property is by referencing a system property specified
|
|
||||||
* at VM startup.
|
|
||||||
* @param uniqueName the unique name
|
|
||||||
*/
|
|
||||||
public void setTransactionManagerUniqueName(String uniqueName) {
|
|
||||||
this.transactionManagerUniqueName = uniqueName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTransactionManagerUniqueName() {
|
|
||||||
return this.transactionManagerUniqueName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies if subtransactions should be joined when possible. Defaults to true. When
|
|
||||||
* false, no attempt to call {@code XAResource.start(TM_JOIN)} will be made for
|
|
||||||
* different but related subtransactions. This setting has no effect on resource
|
|
||||||
* access within one and the same transaction. If you don't use subtransactions then
|
|
||||||
* this setting can be ignored.
|
|
||||||
* @param serialJtaTransactions if serial JTA transactions are supported
|
|
||||||
*/
|
|
||||||
public void setSerialJtaTransactions(boolean serialJtaTransactions) {
|
|
||||||
this.serialJtaTransactions = serialJtaTransactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSerialJtaTransactions() {
|
|
||||||
return this.serialJtaTransactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAllowSubTransactions(boolean allowSubTransactions) {
|
|
||||||
this.allowSubTransactions = allowSubTransactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAllowSubTransactions() {
|
|
||||||
return this.allowSubTransactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether VM shutdown should trigger forced shutdown of the transaction
|
|
||||||
* core. Defaults to false.
|
|
||||||
* @param forceShutdownOnVmExit if VM shutdown should be forced
|
|
||||||
*/
|
|
||||||
public void setForceShutdownOnVmExit(boolean forceShutdownOnVmExit) {
|
|
||||||
this.forceShutdownOnVmExit = forceShutdownOnVmExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isForceShutdownOnVmExit() {
|
|
||||||
return this.forceShutdownOnVmExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies how long should a normal shutdown (no-force) wait for transactions to
|
|
||||||
* complete. Defaults to {@literal Long.MAX_VALUE}.
|
|
||||||
* @param defaultMaxWaitTimeOnShutdown the default max wait time on shutdown
|
|
||||||
*/
|
|
||||||
public void setDefaultMaxWaitTimeOnShutdown(long defaultMaxWaitTimeOnShutdown) {
|
|
||||||
this.defaultMaxWaitTimeOnShutdown = defaultMaxWaitTimeOnShutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDefaultMaxWaitTimeOnShutdown() {
|
|
||||||
return this.defaultMaxWaitTimeOnShutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the transactions log file base name. Defaults to {@literal tmlog}. The
|
|
||||||
* transactions logs are stored in files using this name appended with a number and
|
|
||||||
* the extension {@literal .log}. At checkpoint, a new transactions log file is
|
|
||||||
* created and the number is incremented.
|
|
||||||
* @param logBaseName the log base name
|
|
||||||
*/
|
|
||||||
public void setLogBaseName(String logBaseName) {
|
|
||||||
this.logBaseName = logBaseName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLogBaseName() {
|
|
||||||
return this.logBaseName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the directory in which the log files should be stored. Defaults to the
|
|
||||||
* current working directory. This directory should be a stable storage like a SAN,
|
|
||||||
* RAID or at least backed up location. The transactions logs files are as important
|
|
||||||
* as the data themselves to guarantee consistency in case of failures.
|
|
||||||
* @param logBaseDir the log base dir
|
|
||||||
*/
|
|
||||||
public void setLogBaseDir(String logBaseDir) {
|
|
||||||
this.logBaseDir = logBaseDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLogBaseDir() {
|
|
||||||
return this.logBaseDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the interval between checkpoints. A checkpoint reduces the log file size
|
|
||||||
* at the expense of adding some overhead in the runtime. Defaults to {@literal 500}.
|
|
||||||
* @param checkpointInterval the checkpoint interval
|
|
||||||
*/
|
|
||||||
public void setCheckpointInterval(long checkpointInterval) {
|
|
||||||
this.checkpointInterval = checkpointInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCheckpointInterval() {
|
|
||||||
return this.checkpointInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether or not to use different (and concurrent) threads for two-phase
|
|
||||||
* commit on the participating resources. Setting this to {@literal true} implies that
|
|
||||||
* the commit is more efficient since waiting for acknowledgements is done in
|
|
||||||
* parallel. Defaults to {@literal true}. If you set this to {@literal false}, then
|
|
||||||
* commits will happen in the order that resources are accessed within the
|
|
||||||
* transaction.
|
|
||||||
* @param threadedTwoPhaseCommit if threaded two phase commits should be used
|
|
||||||
*/
|
|
||||||
public void setThreadedTwoPhaseCommit(boolean threadedTwoPhaseCommit) {
|
|
||||||
this.threadedTwoPhaseCommit = threadedTwoPhaseCommit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isThreadedTwoPhaseCommit() {
|
|
||||||
return this.threadedTwoPhaseCommit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Recovery getRecovery() {
|
|
||||||
return this.recovery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the properties as a {@link Properties} object that can be used with
|
|
||||||
* Atomikos.
|
|
||||||
* @return the properties
|
|
||||||
*/
|
|
||||||
public Properties asProperties() {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
set(properties, "service", getService());
|
|
||||||
set(properties, "max_timeout", getMaxTimeout());
|
|
||||||
set(properties, "default_jta_timeout", getDefaultJtaTimeout());
|
|
||||||
set(properties, "max_actives", getMaxActives());
|
|
||||||
set(properties, "enable_logging", isEnableLogging());
|
|
||||||
set(properties, "tm_unique_name", getTransactionManagerUniqueName());
|
|
||||||
set(properties, "serial_jta_transactions", isSerialJtaTransactions());
|
|
||||||
set(properties, "allow_subtransactions", isAllowSubTransactions());
|
|
||||||
set(properties, "force_shutdown_on_vm_exit", isForceShutdownOnVmExit());
|
|
||||||
set(properties, "default_max_wait_time_on_shutdown", getDefaultMaxWaitTimeOnShutdown());
|
|
||||||
set(properties, "log_base_name", getLogBaseName());
|
|
||||||
set(properties, "log_base_dir", getLogBaseDir());
|
|
||||||
set(properties, "checkpoint_interval", getCheckpointInterval());
|
|
||||||
set(properties, "threaded_2pc", isThreadedTwoPhaseCommit());
|
|
||||||
Recovery recovery = getRecovery();
|
|
||||||
set(properties, "forget_orphaned_log_entries_delay", recovery.getForgetOrphanedLogEntriesDelay());
|
|
||||||
set(properties, "recovery_delay", recovery.getDelay());
|
|
||||||
set(properties, "oltp_max_retries", recovery.getMaxRetries());
|
|
||||||
set(properties, "oltp_retry_interval", recovery.getRetryInterval());
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void set(Properties properties, String key, Object value) {
|
|
||||||
String id = "com.atomikos.icatch." + key;
|
|
||||||
if (value != null && !properties.containsKey(id)) {
|
|
||||||
properties.setProperty(id, asString(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String asString(Object value) {
|
|
||||||
if (value instanceof Duration) {
|
|
||||||
return String.valueOf(((Duration) value).toMillis());
|
|
||||||
}
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recovery specific settings.
|
|
||||||
*/
|
|
||||||
public static class Recovery {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delay after which recovery can cleanup pending ('orphaned') log entries.
|
|
||||||
*/
|
|
||||||
private Duration forgetOrphanedLogEntriesDelay = Duration.ofMillis(86400000);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delay between two recovery scans.
|
|
||||||
*/
|
|
||||||
private Duration delay = Duration.ofMillis(10000);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of retry attempts to commit the transaction before throwing an
|
|
||||||
* exception.
|
|
||||||
*/
|
|
||||||
private int maxRetries = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delay between retry attempts.
|
|
||||||
*/
|
|
||||||
private Duration retryInterval = Duration.ofMillis(10000);
|
|
||||||
|
|
||||||
public Duration getForgetOrphanedLogEntriesDelay() {
|
|
||||||
return this.forgetOrphanedLogEntriesDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setForgetOrphanedLogEntriesDelay(Duration forgetOrphanedLogEntriesDelay) {
|
|
||||||
this.forgetOrphanedLogEntriesDelay = forgetOrphanedLogEntriesDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Duration getDelay() {
|
|
||||||
return this.delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDelay(Duration delay) {
|
|
||||||
this.delay = delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxRetries() {
|
|
||||||
return this.maxRetries;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxRetries(int maxRetries) {
|
|
||||||
this.maxRetries = maxRetries;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Duration getRetryInterval() {
|
|
||||||
return this.retryInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRetryInterval(Duration retryInterval) {
|
|
||||||
this.retryInterval = retryInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import javax.jms.ConnectionFactory;
|
|
||||||
import javax.jms.XAConnectionFactory;
|
|
||||||
|
|
||||||
import org.springframework.boot.jms.XAConnectionFactoryWrapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link XAConnectionFactoryWrapper} that uses an {@link AtomikosConnectionFactoryBean}
|
|
||||||
* to wrap a {@link XAConnectionFactory}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
public class AtomikosXAConnectionFactoryWrapper implements XAConnectionFactoryWrapper {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConnectionFactory wrapConnectionFactory(XAConnectionFactory connectionFactory) {
|
|
||||||
AtomikosConnectionFactoryBean bean = new AtomikosConnectionFactoryBean();
|
|
||||||
bean.setXaConnectionFactory(connectionFactory);
|
|
||||||
return bean;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import javax.sql.XADataSource;
|
|
||||||
|
|
||||||
import org.springframework.boot.jdbc.XADataSourceWrapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link XADataSourceWrapper} that uses an {@link AtomikosDataSourceBean} to wrap a
|
|
||||||
* {@link XADataSource}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
public class AtomikosXADataSourceWrapper implements XADataSourceWrapper {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AtomikosDataSourceBean wrapDataSource(XADataSource dataSource) throws Exception {
|
|
||||||
AtomikosDataSourceBean bean = new AtomikosDataSourceBean();
|
|
||||||
bean.setXaDataSource(dataSource);
|
|
||||||
return bean;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Support classes for Atomikos JTA.
|
|
||||||
*/
|
|
||||||
package org.springframework.boot.jta.atomikos;
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Support for the Java Transaction API.
|
|
||||||
*/
|
|
||||||
package org.springframework.boot.jta;
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2021 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AtomikosConnectionFactoryBean}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
class AtomikosConnectionFactoryBeanTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void beanMethods() throws Exception {
|
|
||||||
MockAtomikosConnectionFactoryBean bean = spy(new MockAtomikosConnectionFactoryBean());
|
|
||||||
bean.setBeanName("bean");
|
|
||||||
bean.afterPropertiesSet();
|
|
||||||
assertThat(bean.getUniqueResourceName()).isEqualTo("bean");
|
|
||||||
verify(bean).init();
|
|
||||||
verify(bean, never()).close();
|
|
||||||
bean.destroy();
|
|
||||||
verify(bean).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MockAtomikosConnectionFactoryBean extends AtomikosConnectionFactoryBean {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2021 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AtomikosDataSourceBean}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
class AtomikosDataSourceBeanTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void beanMethods() throws Exception {
|
|
||||||
MockAtomikosDataSourceBean bean = spy(new MockAtomikosDataSourceBean());
|
|
||||||
bean.setBeanName("bean");
|
|
||||||
bean.afterPropertiesSet();
|
|
||||||
assertThat(bean.getUniqueResourceName()).isEqualTo("bean");
|
|
||||||
verify(bean).init();
|
|
||||||
verify(bean, never()).close();
|
|
||||||
bean.destroy();
|
|
||||||
verify(bean).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MockAtomikosDataSourceBean extends AtomikosDataSourceBean {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import javax.jms.ConnectionFactory;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import com.atomikos.icatch.jta.UserTransactionManager;
|
|
||||||
import com.atomikos.jms.extra.MessageDrivenContainer;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AtomikosDependsOnBeanFactoryPostProcessor}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
class AtomikosDependsOnBeanFactoryPostProcessorTests {
|
|
||||||
|
|
||||||
private AnnotationConfigApplicationContext context;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void setsDependsOn() {
|
|
||||||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
|
||||||
assertDependsOn("dataSource");
|
|
||||||
assertDependsOn("connectionFactory");
|
|
||||||
assertDependsOn("userTransactionManager", "dataSource", "connectionFactory");
|
|
||||||
assertDependsOn("messageDrivenContainer", "userTransactionManager");
|
|
||||||
this.context.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertDependsOn(String bean, String... expected) {
|
|
||||||
BeanDefinition definition = this.context.getBeanDefinition(bean);
|
|
||||||
if (definition.getDependsOn() == null) {
|
|
||||||
assertThat(expected).as("No dependsOn expected for " + bean).isEmpty();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
HashSet<String> dependsOn = new HashSet<>(Arrays.asList(definition.getDependsOn()));
|
|
||||||
assertThat(dependsOn).isEqualTo(new HashSet<>(Arrays.asList(expected)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
static class Config {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
DataSource dataSource() {
|
|
||||||
return mock(DataSource.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ConnectionFactory connectionFactory() {
|
|
||||||
return mock(ConnectionFactory.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
UserTransactionManager userTransactionManager() {
|
|
||||||
return mock(UserTransactionManager.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
MessageDrivenContainer messageDrivenContainer() {
|
|
||||||
return mock(MessageDrivenContainer.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
static AtomikosDependsOnBeanFactoryPostProcessor atomikosPostProcessor() {
|
|
||||||
return new AtomikosDependsOnBeanFactoryPostProcessor();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2020 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.assertj.core.data.MapEntry;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.entry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AtomikosProperties}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
*/
|
|
||||||
class AtomikosPropertiesTests {
|
|
||||||
|
|
||||||
private AtomikosProperties properties = new AtomikosProperties();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testProperties() {
|
|
||||||
this.properties.setService("service");
|
|
||||||
this.properties.setMaxTimeout(Duration.ofMillis(1));
|
|
||||||
this.properties.setDefaultJtaTimeout(Duration.ofMillis(2));
|
|
||||||
this.properties.setMaxActives(3);
|
|
||||||
this.properties.setEnableLogging(true);
|
|
||||||
this.properties.setTransactionManagerUniqueName("uniqueName");
|
|
||||||
this.properties.setSerialJtaTransactions(true);
|
|
||||||
this.properties.setAllowSubTransactions(false);
|
|
||||||
this.properties.setForceShutdownOnVmExit(true);
|
|
||||||
this.properties.setDefaultMaxWaitTimeOnShutdown(20);
|
|
||||||
this.properties.setLogBaseName("logBaseName");
|
|
||||||
this.properties.setLogBaseDir("logBaseDir");
|
|
||||||
this.properties.setCheckpointInterval(4);
|
|
||||||
this.properties.setThreadedTwoPhaseCommit(true);
|
|
||||||
this.properties.getRecovery().setForgetOrphanedLogEntriesDelay(Duration.ofMillis(2000));
|
|
||||||
this.properties.getRecovery().setDelay(Duration.ofMillis(3000));
|
|
||||||
this.properties.getRecovery().setMaxRetries(10);
|
|
||||||
this.properties.getRecovery().setRetryInterval(Duration.ofMillis(4000));
|
|
||||||
assertThat(this.properties.asProperties().size()).isEqualTo(18);
|
|
||||||
assertProperty("com.atomikos.icatch.service", "service");
|
|
||||||
assertProperty("com.atomikos.icatch.max_timeout", "1");
|
|
||||||
assertProperty("com.atomikos.icatch.default_jta_timeout", "2");
|
|
||||||
assertProperty("com.atomikos.icatch.max_actives", "3");
|
|
||||||
assertProperty("com.atomikos.icatch.enable_logging", "true");
|
|
||||||
assertProperty("com.atomikos.icatch.tm_unique_name", "uniqueName");
|
|
||||||
assertProperty("com.atomikos.icatch.serial_jta_transactions", "true");
|
|
||||||
assertProperty("com.atomikos.icatch.allow_subtransactions", "false");
|
|
||||||
assertProperty("com.atomikos.icatch.force_shutdown_on_vm_exit", "true");
|
|
||||||
assertProperty("com.atomikos.icatch.default_max_wait_time_on_shutdown", "20");
|
|
||||||
assertProperty("com.atomikos.icatch.log_base_name", "logBaseName");
|
|
||||||
assertProperty("com.atomikos.icatch.log_base_dir", "logBaseDir");
|
|
||||||
assertProperty("com.atomikos.icatch.checkpoint_interval", "4");
|
|
||||||
assertProperty("com.atomikos.icatch.threaded_2pc", "true");
|
|
||||||
assertProperty("com.atomikos.icatch.forget_orphaned_log_entries_delay", "2000");
|
|
||||||
assertProperty("com.atomikos.icatch.recovery_delay", "3000");
|
|
||||||
assertProperty("com.atomikos.icatch.oltp_max_retries", "10");
|
|
||||||
assertProperty("com.atomikos.icatch.oltp_retry_interval", "4000");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testDefaultProperties() {
|
|
||||||
Properties defaultSettings = loadDefaultSettings();
|
|
||||||
Properties properties = this.properties.asProperties();
|
|
||||||
assertThat(properties).contains(defaultOf(defaultSettings, "com.atomikos.icatch.max_timeout",
|
|
||||||
"com.atomikos.icatch.default_jta_timeout", "com.atomikos.icatch.max_actives",
|
|
||||||
"com.atomikos.icatch.enable_logging", "com.atomikos.icatch.serial_jta_transactions",
|
|
||||||
"com.atomikos.icatch.allow_subtransactions", "com.atomikos.icatch.force_shutdown_on_vm_exit",
|
|
||||||
"com.atomikos.icatch.default_max_wait_time_on_shutdown", "com.atomikos.icatch.log_base_name",
|
|
||||||
"com.atomikos.icatch.checkpoint_interval", "com.atomikos.icatch.threaded_2pc",
|
|
||||||
"com.atomikos.icatch.forget_orphaned_log_entries_delay", "com.atomikos.icatch.oltp_max_retries",
|
|
||||||
"com.atomikos.icatch.oltp_retry_interval"));
|
|
||||||
assertThat(properties).contains(entry("com.atomikos.icatch.recovery_delay",
|
|
||||||
defaultSettings.get("com.atomikos.icatch.default_jta_timeout")));
|
|
||||||
assertThat(properties).hasSize(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MapEntry<?, ?>[] defaultOf(Properties defaultSettings, String... keys) {
|
|
||||||
MapEntry<?, ?>[] entries = new MapEntry<?, ?>[keys.length];
|
|
||||||
for (int i = 0; i < keys.length; i++) {
|
|
||||||
String key = keys[i];
|
|
||||||
entries[i] = entry(key, defaultSettings.get(key));
|
|
||||||
}
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Properties loadDefaultSettings() {
|
|
||||||
try {
|
|
||||||
|
|
||||||
return PropertiesLoaderUtils.loadProperties(new ClassPathResource("transactions-defaults.properties"));
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new IllegalStateException("Failed to get default from Atomikos", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertProperty(String key, String value) {
|
|
||||||
assertThat(this.properties.asProperties().getProperty(key)).isEqualTo(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import javax.jms.ConnectionFactory;
|
|
||||||
import javax.jms.XAConnectionFactory;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AtomikosXAConnectionFactoryWrapper}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
class AtomikosXAConnectionFactoryWrapperTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void wrap() {
|
|
||||||
XAConnectionFactory connectionFactory = mock(XAConnectionFactory.class);
|
|
||||||
AtomikosXAConnectionFactoryWrapper wrapper = new AtomikosXAConnectionFactoryWrapper();
|
|
||||||
ConnectionFactory wrapped = wrapper.wrapConnectionFactory(connectionFactory);
|
|
||||||
assertThat(wrapped).isInstanceOf(AtomikosConnectionFactoryBean.class);
|
|
||||||
assertThat(((AtomikosConnectionFactoryBean) wrapped).getXaConnectionFactory()).isSameAs(connectionFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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.jta.atomikos;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import javax.sql.XADataSource;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AtomikosXADataSourceWrapper}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
class AtomikosXADataSourceWrapperTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void wrap() throws Exception {
|
|
||||||
XADataSource dataSource = mock(XADataSource.class);
|
|
||||||
AtomikosXADataSourceWrapper wrapper = new AtomikosXADataSourceWrapper();
|
|
||||||
DataSource wrapped = wrapper.wrapDataSource(dataSource);
|
|
||||||
assertThat(wrapped).isInstanceOf(AtomikosDataSourceBean.class);
|
|
||||||
assertThat(((AtomikosDataSourceBean) wrapped).getXaDataSource()).isSameAs(dataSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
plugins {
|
|
||||||
id "java"
|
|
||||||
id "org.springframework.boot.conventions"
|
|
||||||
}
|
|
||||||
|
|
||||||
description = "Spring Boot Atomikos JTA smoke test"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-artemis"))
|
|
||||||
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-data-jpa"))
|
|
||||||
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jta-atomikos"))
|
|
||||||
implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
|
|
||||||
if (JavaVersion.current().java9Compatible) {
|
|
||||||
implementation("jakarta.xml.bind:jakarta.xml.bind-api")
|
|
||||||
}
|
|
||||||
implementation("org.springframework:spring-jms")
|
|
||||||
|
|
||||||
runtimeOnly("com.h2database:h2")
|
|
||||||
runtimeOnly("org.apache.activemq:artemis-jms-server") {
|
|
||||||
exclude group: "commons-logging", module: "commons-logging"
|
|
||||||
exclude group: "org.apache.geronimo.specs", module: "geronimo-jms_2.0_spec"
|
|
||||||
exclude group: "org.apache.geronimo.specs", module: "geronimo-json_1.0_spec"
|
|
||||||
exclude group: "org.apache.geronimo.specs", module: "geronimo-jta_1.1_spec"
|
|
||||||
}
|
|
||||||
|
|
||||||
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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 smoketest.atomikos;
|
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
public class Account {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
Account() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Account(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return this.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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 smoketest.atomikos;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
public interface AccountRepository extends JpaRepository<Account, Long> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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 smoketest.atomikos;
|
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
|
||||||
|
|
||||||
import org.springframework.jms.core.JmsTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Transactional
|
|
||||||
public class AccountService {
|
|
||||||
|
|
||||||
private final JmsTemplate jmsTemplate;
|
|
||||||
|
|
||||||
private final AccountRepository accountRepository;
|
|
||||||
|
|
||||||
public AccountService(JmsTemplate jmsTemplate, AccountRepository accountRepository) {
|
|
||||||
this.jmsTemplate = jmsTemplate;
|
|
||||||
this.accountRepository = accountRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createAccountAndNotify(String username) {
|
|
||||||
this.jmsTemplate.convertAndSend("accounts", username);
|
|
||||||
this.accountRepository.save(new Account(username));
|
|
||||||
if ("error".equals(username)) {
|
|
||||||
throw new RuntimeException("Simulated error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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
|
|
||||||
*
|
|
||||||
* https://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 smoketest.atomikos;
|
|
||||||
|
|
||||||
import org.springframework.jms.annotation.JmsListener;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class Messages {
|
|
||||||
|
|
||||||
@JmsListener(destination = "accounts")
|
|
||||||
public void onMessage(String content) {
|
|
||||||
System.out.println("----> " + content);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2021 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
|
|
||||||
*
|
|
||||||
* https://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 smoketest.atomikos;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
public class SampleAtomikosApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
try (ConfigurableApplicationContext context = SpringApplication.run(SampleAtomikosApplication.class, args)) {
|
|
||||||
AccountService service = context.getBean(AccountService.class);
|
|
||||||
AccountRepository repository = context.getBean(AccountRepository.class);
|
|
||||||
service.createAccountAndNotify("josh");
|
|
||||||
System.out.println("Count is " + repository.count());
|
|
||||||
try {
|
|
||||||
service.createAccountAndNotify("error");
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
System.out.println(ex.getMessage());
|
|
||||||
}
|
|
||||||
System.out.println("Count is " + repository.count());
|
|
||||||
Thread.sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
logging.level.com.atomikos=WARN
|
|
||||||
spring.artemis.embedded.queues=accounts
|
|
||||||
spring.jpa.open-in-view=true
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2021 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
|
|
||||||
*
|
|
||||||
* https://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 smoketest.atomikos;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
|
|
||||||
import org.springframework.boot.test.system.CapturedOutput;
|
|
||||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
|
||||||
import org.springframework.boot.testsupport.BuildOutput;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic integration tests for demo application.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
@ExtendWith(OutputCaptureExtension.class)
|
|
||||||
class SampleAtomikosApplicationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testTransactionRollback(CapturedOutput output) throws Exception {
|
|
||||||
File logDir = new File(new BuildOutput(getClass()).getRootLocation(), "atomikos-logs");
|
|
||||||
SampleAtomikosApplication.main(new String[] { "--spring.jta.log-dir=" + logDir });
|
|
||||||
assertThat(output).satisfies(numberOfOccurrences("---->", 1));
|
|
||||||
assertThat(output).satisfies(numberOfOccurrences("----> josh", 1));
|
|
||||||
assertThat(output).satisfies(numberOfOccurrences("Count is 1", 2));
|
|
||||||
assertThat(output).satisfies(numberOfOccurrences("Simulated error", 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends CharSequence> Consumer<T> numberOfOccurrences(String substring, int expectedCount) {
|
|
||||||
return (charSequence) -> {
|
|
||||||
int count = StringUtils.countOccurrencesOf(charSequence.toString(), substring);
|
|
||||||
assertThat(count).isEqualTo(expectedCount);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue