From 236026a43ab6399a5b02f66e2814915841409b9f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 2 Sep 2014 11:44:16 -0700 Subject: [PATCH] Support mixed XA/non-XA ConnectionFactory beans Update ActiveMQ and HornetQ XA configurations to also expose non-xa ConnectionFactory variants. Fixes gh-1461 --- ...iveMQXAConnectionFactoryConfiguration.java | 18 ++++++++++- ...rnetQXAConnectionFactoryConfiguration.java | 11 ++++++- .../main/asciidoc/spring-boot-features.adoc | 32 +++++++++++++++++++ .../SampleBitronixApplicationTests.java | 20 ++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java index a30fd80e61..b425fe4824 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java @@ -19,13 +19,16 @@ package org.springframework.boot.autoconfigure.jms.activemq; import javax.jms.ConnectionFactory; import javax.transaction.TransactionManager; +import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.ActiveMQXAConnectionFactory; +import org.apache.activemq.pool.PooledConnectionFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; /** * Configuration for ActiveMQ XA {@link ConnectionFactory}. @@ -39,7 +42,8 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnMissingBean(ConnectionFactory.class) class ActiveMQXAConnectionFactoryConfiguration { - @Bean + @Primary + @Bean(name = { "jmsConnectionFactory", "xaJmsConnectionFactory" }) public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties, XAConnectionFactoryWrapper wrapper) throws Exception { ActiveMQXAConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory( @@ -47,4 +51,16 @@ class ActiveMQXAConnectionFactoryConfiguration { return wrapper.wrapConnectionFactory(connectionFactory); } + @Bean + public ConnectionFactory nonXaJmsConnectionFactory(ActiveMQProperties properties) { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory( + properties).createConnectionFactory(ActiveMQConnectionFactory.class); + if (properties.isPooled()) { + PooledConnectionFactory pool = new PooledConnectionFactory(); + pool.setConnectionFactory(connectionFactory); + return pool; + } + return connectionFactory; + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/hornetq/HornetQXAConnectionFactoryConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/hornetq/HornetQXAConnectionFactoryConfiguration.java index 529322253e..bc06b8d6d2 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/hornetq/HornetQXAConnectionFactoryConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/hornetq/HornetQXAConnectionFactoryConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.jms.hornetq; import javax.jms.ConnectionFactory; import javax.transaction.TransactionManager; +import org.hornetq.jms.client.HornetQConnectionFactory; import org.hornetq.jms.client.HornetQXAConnectionFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -27,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; /** * Configuration for HornetQ XA {@link ConnectionFactory}. @@ -40,7 +42,8 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnBean(XAConnectionFactoryWrapper.class) class HornetQXAConnectionFactoryConfiguration { - @Bean + @Primary + @Bean(name = { "jmsConnectionFactory", "xaJmsConnectionFactory" }) public ConnectionFactory jmsConnectionFactory(ListableBeanFactory beanFactory, HornetQProperties properties, XAConnectionFactoryWrapper wrapper) throws Exception { @@ -49,4 +52,10 @@ class HornetQXAConnectionFactoryConfiguration { .createConnectionFactory(HornetQXAConnectionFactory.class)); } + @Bean + public ConnectionFactory nonXaJmsConnectionFactory(ListableBeanFactory beanFactory, + HornetQProperties properties) { + return new HornetQConnectionFactoryFactory(beanFactory, properties) + .createConnectionFactory(HornetQConnectionFactory.class); + } } diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 1753baeb2b..e6ca3e83b0 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1964,6 +1964,38 @@ to configure your `DataSource`. +=== Mixing XA and non-XA JMS connections +When using JTA, the primary JMS `ConnectionFactory` bean will be XA aware and participate +in distributed transactions. In some situations you might want to process certain JMS +messages using a non-XA `ConnectionFactory`. For example, your JMS processing logic might +take longer than the XA timeout. + +If you want to use a non-XA `ConnectionFactory` you can inject the +`nonXaJmsConnectionFactory` bean rather than the `@Primary` `jmsConnectionFactory` bean. +For consistency the `jmsConnectionFactory` bean is also provided using the bean alias +`xaJmsConnectionFactory`. + +For example: + +[source,java,indent=0,subs="verbatim,quotes,attributes"] +---- + // Inject the primary (XA aware) ConnectionFactory + @Autowired + private ConnectionFactory defaultConnectionFactory; + + // Inject the XA aware ConnectionFactory (uses the alias and injects the same as above) + @Autowired + @Qualifier("xaJmsConnectionFactory") + private ConnectionFactory xaConnectionFactory; + + // Inject the non-XA aware ConnectionFactory + @Autowired + @Qualifier("nonXaJmsConnectionFactory") + private ConnectionFactory nonXaConnectionFactory; +---- + + + === Supporting an alternative embedded transaction manager The {sc-spring-boot}/jta/XAConnectionFactoryWrapper.{sc-ext}[`XAConnectionFactoryWrapper`] and {sc-spring-boot}/jta/XADataSourceWrapper.{sc-ext}[`XADataSourceWrapper`] interfaces diff --git a/spring-boot-samples/spring-boot-sample-jta-bitronix/src/test/java/sample/bitronix/SampleBitronixApplicationTests.java b/spring-boot-samples/spring-boot-sample-jta-bitronix/src/test/java/sample/bitronix/SampleBitronixApplicationTests.java index 2c5186beaf..60491a67ec 100644 --- a/spring-boot-samples/spring-boot-sample-jta-bitronix/src/test/java/sample/bitronix/SampleBitronixApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-jta-bitronix/src/test/java/sample/bitronix/SampleBitronixApplicationTests.java @@ -20,8 +20,15 @@ import org.hamcrest.Matcher; import org.hamcrest.core.SubstringMatcher; import org.junit.Rule; import org.junit.Test; +import org.springframework.boot.SpringApplication; import org.springframework.boot.test.OutputCapture; +import org.springframework.context.ApplicationContext; +import bitronix.tm.resource.jms.PoolingConnectionFactory; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; /** @@ -44,6 +51,19 @@ public class SampleBitronixApplicationTests { assertThat(output, containsString(1, "Simulated error")); } + @Test + public void testExposesXaAndNonXa() throws Exception { + ApplicationContext context = SpringApplication + .run(SampleBitronixApplication.class); + Object jmsConnectionFactory = context.getBean("jmsConnectionFactory"); + Object xaJmsConnectionFactory = context.getBean("xaJmsConnectionFactory"); + Object nonXaJmsConnectionFactory = context.getBean("nonXaJmsConnectionFactory"); + assertThat(jmsConnectionFactory, sameInstance(xaJmsConnectionFactory)); + assertThat(jmsConnectionFactory, instanceOf(PoolingConnectionFactory.class)); + assertThat(nonXaJmsConnectionFactory, + not(instanceOf(PoolingConnectionFactory.class))); + } + private Matcher containsString(final int times, String s) { return new SubstringMatcher(s) {