Browse Source

Reinstate Bitronix's default server ID, provide property to override it

Previously, Bitronix's server ID was hard-coded to be
spring-boot-jta-bitronix. This created the possibility of multiple
transaction managers performing recovery on each other's behalf as
they would be unable to identify their own XIDs due to the common
server ID.

This commit reinstates the default (which is the IP address of the
machine on which Bitronix is running), and introduces a new
property, spring.jta.transaction-manager-id, that can be used to
configure the id for both Atomikos and Bitronix. A cautionary note
has also been added to the documentation for Atomikos and Bitronix
explaining the need to configure this property.

Closes gh-1548
pull/1554/merge
Andy Wilkinson 11 years ago
parent
commit
01fd8cb8f3
  1. 5
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/AtomikosJtaConfiguration.java
  2. 5
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/BitronixJtaConfiguration.java
  3. 11
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JtaProperties.java
  4. 75
      spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jta/JtaAutoConfigurationTests.java
  5. 12
      spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

5
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/AtomikosJtaConfiguration.java

@ -49,6 +49,7 @@ import com.atomikos.icatch.jta.UserTransactionManager; @@ -49,6 +49,7 @@ import com.atomikos.icatch.jta.UserTransactionManager;
*
* @author Josh Long
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.2.0
*/
@Configuration
@ -71,6 +72,10 @@ class AtomikosJtaConfiguration { @@ -71,6 +72,10 @@ class AtomikosJtaConfiguration {
public UserTransactionService userTransactionService(
AtomikosProperties atomikosProperties) {
Properties properties = new Properties();
if (StringUtils.hasText(this.jtaProperties.getTransactionManagerId())) {
properties.setProperty("com.atomikos.icatch.tm_unique_name",
this.jtaProperties.getTransactionManagerId());
}
properties.setProperty("com.atomikos.icatch.log_base_dir", getLogBaseDir());
properties.putAll(atomikosProperties.asProperties());
return new UserTransactionServiceImp(properties);

5
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/BitronixJtaConfiguration.java

@ -45,6 +45,7 @@ import bitronix.tm.jndi.BitronixContext; @@ -45,6 +45,7 @@ import bitronix.tm.jndi.BitronixContext;
*
* @author Josh Long
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.2.0
*/
@Configuration
@ -60,7 +61,9 @@ class BitronixJtaConfiguration { @@ -60,7 +61,9 @@ class BitronixJtaConfiguration {
@ConfigurationProperties(prefix = JtaProperties.PREFIX)
public bitronix.tm.Configuration bitronixConfiguration() {
bitronix.tm.Configuration config = TransactionManagerServices.getConfiguration();
config.setServerId("spring-boot-jta-bitronix");
if (StringUtils.hasText(this.jtaProperties.getTransactionManagerId())) {
config.setServerId(this.jtaProperties.getTransactionManagerId());
}
File logBaseDir = getLogBaseDir();
config.setLogPart1Filename(new File(logBaseDir, "part1.btm").getAbsolutePath());
config.setLogPart2Filename(new File(logBaseDir, "part2.btm").getAbsolutePath());

11
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JtaProperties.java

@ -26,6 +26,7 @@ import org.springframework.transaction.jta.JtaTransactionManager; @@ -26,6 +26,7 @@ import org.springframework.transaction.jta.JtaTransactionManager;
*
* @author Josh Long
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.2.0
*/
@ConfigurationProperties(prefix = JtaProperties.PREFIX, ignoreUnknownFields = true)
@ -35,6 +36,8 @@ public class JtaProperties { @@ -35,6 +36,8 @@ public class JtaProperties {
private String logDir;
private String transactionManagerId;
public void setLogDir(String logDir) {
this.logDir = logDir;
}
@ -43,4 +46,12 @@ public class JtaProperties { @@ -43,4 +46,12 @@ public class JtaProperties {
return this.logDir;
}
public String getTransactionManagerId() {
return this.transactionManagerId;
}
public void setTransactionManagerId(String transactionManagerId) {
this.transactionManagerId = transactionManagerId;
}
}

75
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jta/JtaAutoConfigurationTests.java

@ -16,28 +16,41 @@ @@ -16,28 +16,41 @@
package org.springframework.boot.autoconfigure.jta;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jta.XAConnectionFactoryWrapper;
import org.springframework.boot.jta.XADataSourceWrapper;
import org.springframework.boot.jta.atomikos.AtomikosDependsOnBeanFactoryPostProcessor;
import org.springframework.boot.jta.atomikos.AtomikosProperties;
import org.springframework.boot.jta.bitronix.BitronixDependentBeanFactoryPostProcessor;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.FileSystemUtils;
import com.atomikos.icatch.config.UserTransactionService;
import com.atomikos.icatch.jta.UserTransactionManager;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
@ -45,6 +58,7 @@ import static org.mockito.Mockito.mock; @@ -45,6 +58,7 @@ import static org.mockito.Mockito.mock;
*
* @author Josh Long
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class JtaAutoConfigurationTests {
@ -53,6 +67,11 @@ public class JtaAutoConfigurationTests { @@ -53,6 +67,11 @@ public class JtaAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Before
public void cleanUpLogs() {
FileSystemUtils.deleteRecursively(new File("target/transaction-logs"));
}
@After
public void closeContext() {
if (this.context != null) {
@ -94,6 +113,62 @@ public class JtaAutoConfigurationTests { @@ -94,6 +113,62 @@ public class JtaAutoConfigurationTests {
this.context.getBean(JtaTransactionManager.class);
}
@Test
public void defaultBitronixServerId() throws UnknownHostException {
this.context = new AnnotationConfigApplicationContext(
JtaPropertiesConfiguration.class, BitronixJtaConfiguration.class);
String serverId = this.context.getBean(bitronix.tm.Configuration.class)
.getServerId();
assertThat(serverId, is(equalTo(InetAddress.getLocalHost().getHostAddress())));
}
@Test
public void customBitronixServerId() throws UnknownHostException {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.transactionManagerId:custom");
this.context.register(JtaPropertiesConfiguration.class,
BitronixJtaConfiguration.class);
this.context.refresh();
String serverId = this.context.getBean(bitronix.tm.Configuration.class)
.getServerId();
assertThat(serverId, is(equalTo("custom")));
}
@Test
public void defaultAtomikosTransactionManagerName() throws UnknownHostException {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.logDir:target/transaction-logs");
this.context.register(JtaPropertiesConfiguration.class,
AtomikosJtaConfiguration.class);
this.context.refresh();
File epochFile = new File("target/transaction-logs/"
+ InetAddress.getLocalHost().getHostAddress() + ".tm0.epoch");
assertTrue(epochFile.isFile());
}
@Test
public void customAtomikosTransactionManagerName() throws BeansException, Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.transactionManagerId:custom",
"spring.jta.logDir:target/transaction-logs");
this.context.register(JtaPropertiesConfiguration.class,
AtomikosJtaConfiguration.class);
this.context.refresh();
File epochFile = new File("target/transaction-logs/custom0.epoch");
assertTrue(epochFile.isFile());
}
@Configuration
@EnableConfigurationProperties(JtaProperties.class)
public static class JtaPropertiesConfiguration {
}
@Configuration
public static class CustomTransactionManagerConfig {

12
spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

@ -2047,6 +2047,12 @@ customize the Atomikos `UserTransactionServiceIml`. See the @@ -2047,6 +2047,12 @@ customize the Atomikos `UserTransactionServiceIml`. See the
{dc-spring-boot}/jta/atomikos/AtomikosProperties.{dc-ext}[`AtomikosProperties` javadoc]
for complete details.
CAUTION: 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 `spring.jta.transaction-manager-id`
property with a different value for each instance of your application.
=== Using a Bitronix transaction manager
@ -2063,6 +2069,12 @@ are also bound to the `bitronix.tm.Configuration` bean, allowing for complete @@ -2063,6 +2069,12 @@ are also bound to the `bitronix.tm.Configuration` bean, allowing for complete
customization. See the http://btm.codehaus.org/api/2.0.1/bitronix/tm/Configuration.html[Bitronix
documentation] for details.
CAUTION: To ensure that multiple transaction managers can safely coordinate the same
resource managers, each Bitronix instance must be configured with a unique ID. By default
this ID is the IP address of the machine on which Bitronix is running. To ensure
uniqueness in production, you should configure the `spring.jta.transaction-manager-id`
property with a different value for each instance of your application.
=== Using a Java EE managed transaction manager

Loading…
Cancel
Save