Update JMS auto-configuration to support XA

Update JMS auto-configuration for ActiveMQ and HornetQ to support XA
transactions.

See gh-947
pull/1323/merge
Phillip Webb 10 years ago
parent 8219f2be4c
commit da88bb4791

@ -19,19 +19,16 @@ package org.springframework.boot.autoconfigure.jms.activemq;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.transport.vm.VMTransportFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.boot.autoconfigure.jta.JtaAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* {@link EnableAutoConfiguration Auto-configuration} to integrate with an ActiveMQ
@ -39,67 +36,16 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
* embedded broker.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.1.0
*/
@Configuration
@AutoConfigureBefore(JmsAutoConfiguration.class)
@AutoConfigureAfter(JtaAutoConfiguration.class)
@ConditionalOnClass({ ConnectionFactory.class, ActiveMQConnectionFactory.class })
@ConditionalOnMissingBean(ConnectionFactory.class)
@EnableConfigurationProperties(ActiveMQProperties.class)
@Import({ ActiveMQXAConnectionFactoryConfiguration.class, ActiveMQConnectionFactoryConfiguration.class })
public class ActiveMQAutoConfiguration {
@Configuration
@ConditionalOnClass(VMTransportFactory.class)
@Conditional(EmbeddedBrokerCondition.class)
@Import(ActiveMQConnectionFactoryConfiguration.class)
protected static class EmbeddedBroker {
}
@Configuration
@Conditional(NonEmbeddedBrokerCondition.class)
@Import(ActiveMQConnectionFactoryConfiguration.class)
protected static class NetworkBroker {
}
static abstract class BrokerTypeCondition extends SpringBootCondition {
private final boolean embedded;
BrokerTypeCondition(boolean embedded) {
this.embedded = embedded;
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String brokerUrl = ActiveMQProperties.determineBrokerUrl(context
.getEnvironment());
boolean match = brokerUrl.contains("vm://");
boolean outcome = (match == this.embedded);
return new ConditionOutcome(outcome, buildMessage(brokerUrl, outcome));
}
protected String buildMessage(String brokerUrl, boolean outcome) {
String brokerType = this.embedded ? "Embedded" : "Network";
String detected = outcome ? "detected" : "not detected";
return brokerType + " ActiveMQ broker " + detected + " - brokerUrl '"
+ brokerUrl + "'";
}
}
static class EmbeddedBrokerCondition extends BrokerTypeCondition {
EmbeddedBrokerCondition() {
super(true);
}
}
static class NonEmbeddedBrokerCondition extends BrokerTypeCondition {
NonEmbeddedBrokerCondition() {
super(false);
}
}
}

@ -18,27 +18,34 @@ package org.springframework.boot.autoconfigure.jms.activemq;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.pool.PooledConnectionFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Creates a {@link ConnectionFactory} based on {@link ActiveMQProperties}.
* Configuration for ActiveMQ {@link ConnectionFactory}.
*
* @author Greg Turnquist
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.1.0
*/
@Configuration
@EnableConfigurationProperties(ActiveMQProperties.class)
@ConditionalOnMissingBean(ConnectionFactory.class)
class ActiveMQConnectionFactoryConfiguration {
@Autowired
private ActiveMQProperties properties;
@Bean
public ConnectionFactory jmsConnectionFactory() {
return this.properties.createConnectionFactory();
public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties) {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(
properties).createConnectionFactory(ActiveMQConnectionFactory.class);
if (properties.isPooled()) {
PooledConnectionFactory pool = new PooledConnectionFactory();
pool.setConnectionFactory(connectionFactory);
return pool;
}
return connectionFactory;
}
}

@ -0,0 +1,76 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jms.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Factory to create a {@link ActiveMQConnectionFactory} instance from properties defined
* in {@link ActiveMQProperties}.
*
* @author Phillip Webb
* @since 1.2.0
*/
class ActiveMQConnectionFactoryFactory {
private static final String DEFAULT_EMBEDDED_BROKER_URL = "vm://localhost?broker.persistent=false";
private static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616";
private final ActiveMQProperties properties;
public ActiveMQConnectionFactoryFactory(ActiveMQProperties properties) {
Assert.notNull(properties, "Properties must not be null");
this.properties = properties;
}
public <T extends ActiveMQConnectionFactory> T createConnectionFactory(
Class<T> factoryClass) {
try {
return doCreateConnectionFactory(factoryClass);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create "
+ "ActiveMQConnectionFactory", ex);
}
}
private <T extends ActiveMQConnectionFactory> T doCreateConnectionFactory(
Class<T> factoryClass) throws Exception {
String brokerUrl = determineBrokerUrl();
String user = this.properties.getUser();
String password = this.properties.getPassword();
if (StringUtils.hasLength(user) && StringUtils.hasLength(password)) {
return factoryClass.getConstructor(String.class, String.class, String.class)
.newInstance(user, password, brokerUrl);
}
return factoryClass.getConstructor(String.class).newInstance(brokerUrl);
}
String determineBrokerUrl() {
if (this.properties.getBrokerUrl() != null) {
return this.properties.getBrokerUrl();
}
if (this.properties.isInMemory()) {
return DEFAULT_EMBEDDED_BROKER_URL;
}
return DEFAULT_NETWORK_BROKER_URL;
}
}

@ -16,15 +16,7 @@
package org.springframework.boot.autoconfigure.jms.activemq;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.pool.PooledConnectionFactory;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertyResolver;
import org.springframework.util.StringUtils;
/**
* Configuration properties for ActiveMQ
@ -35,10 +27,6 @@ import org.springframework.util.StringUtils;
@ConfigurationProperties(prefix = "spring.activemq")
public class ActiveMQProperties {
public static final String DEFAULT_EMBEDDED_BROKER_URL = "vm://localhost?broker.persistent=false";
public static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616";
private String brokerUrl;
private boolean inMemory = true;
@ -93,52 +81,4 @@ public class ActiveMQProperties {
this.password = password;
}
/**
* Return a new {@link ConnectionFactory} from these properties.
*/
public ConnectionFactory createConnectionFactory() {
ConnectionFactory connectionFactory = createActiveMQConnectionFactory();
if (isPooled()) {
PooledConnectionFactory pool = new PooledConnectionFactory();
pool.setConnectionFactory(connectionFactory);
return pool;
}
return connectionFactory;
}
private ConnectionFactory createActiveMQConnectionFactory() {
String brokerUrl = determineBrokerUrl();
if (StringUtils.hasLength(this.user) && StringUtils.hasLength(this.password)) {
return new ActiveMQConnectionFactory(this.user, this.password, brokerUrl);
}
return new ActiveMQConnectionFactory(brokerUrl);
}
String determineBrokerUrl() {
return determineBrokerUrl(this.brokerUrl, this.inMemory);
}
/**
* Determine the broker url to use for the specified {@link Environment}. If no broker
* url is specified through configuration, a default broker is provided, that is
* {@value #DEFAULT_EMBEDDED_BROKER_URL} if the {@code inMemory} flag is {@code null}
* or {@code true}, {@value #DEFAULT_NETWORK_BROKER_URL} otherwise.
* @param environment the environment to extract configuration from
* @return the broker url to use
*/
public static String determineBrokerUrl(Environment environment) {
PropertyResolver resolver = new RelaxedPropertyResolver(environment,
"spring.activemq.");
String brokerUrl = resolver.getProperty("brokerUrl");
Boolean inMemory = resolver.getProperty("inMemory", Boolean.class);
return determineBrokerUrl(brokerUrl, inMemory);
}
private static String determineBrokerUrl(String brokerUrl, Boolean inMemory) {
if (brokerUrl != null) {
return brokerUrl;
}
boolean embedded = inMemory == null || inMemory;
return (embedded ? DEFAULT_EMBEDDED_BROKER_URL : DEFAULT_NETWORK_BROKER_URL);
}
}

@ -0,0 +1,50 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jms.activemq;
import javax.jms.ConnectionFactory;
import javax.transaction.TransactionManager;
import org.apache.activemq.ActiveMQXAConnectionFactory;
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;
/**
* Configuration for ActiveMQ XA {@link ConnectionFactory}.
*
* @author Phillip Webb
* @since 1.2.0
*/
@Configuration
@ConditionalOnClass(TransactionManager.class)
@ConditionalOnBean(XAConnectionFactoryWrapper.class)
@ConditionalOnMissingBean(ConnectionFactory.class)
class ActiveMQXAConnectionFactoryConfiguration {
@Bean
public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties,
XAConnectionFactoryWrapper wrapper) throws Exception {
ActiveMQXAConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(
properties).createConnectionFactory(ActiveMQXAConnectionFactory.class);
return wrapper.wrapConnectionFactory(connectionFactory);
}
}

@ -16,48 +16,25 @@
package org.springframework.boot.autoconfigure.jms.hornetq;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jms.ConnectionFactory;
import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.core.client.HornetQClient;
import org.hornetq.api.core.client.ServerLocator;
import org.hornetq.api.jms.HornetQJMSClient;
import org.hornetq.api.jms.JMSFactoryType;
import org.hornetq.core.remoting.impl.invm.InVMConnectorFactory;
import org.hornetq.core.remoting.impl.netty.NettyConnectorFactory;
import org.hornetq.core.remoting.impl.netty.TransportConstants;
import org.hornetq.jms.client.HornetQConnectionFactory;
import org.hornetq.jms.server.config.JMSConfiguration;
import org.hornetq.jms.server.config.JMSQueueConfiguration;
import org.hornetq.jms.server.config.TopicConfiguration;
import org.hornetq.jms.server.config.impl.JMSConfigurationImpl;
import org.hornetq.jms.server.config.impl.JMSQueueConfigurationImpl;
import org.hornetq.jms.server.config.impl.TopicConfigurationImpl;
import org.hornetq.jms.server.embedded.EmbeddedJMS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.ClassUtils;
import org.springframework.context.annotation.Import;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} to integrate with an HornetQ broker. If the necessary classes are
* present, embed the broker in the application by default. Otherwise, connect to a broker
* available on the local machine with the default settings.
* {@link EnableAutoConfiguration Auto-configuration} to integrate with an HornetQ broker.
* If the necessary classes are present, embed the broker in the application by default.
* Otherwise, connect to a broker available on the local machine with the default
* settings.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.1.0
* @see HornetQProperties
*/
@ -65,151 +42,9 @@ import org.springframework.util.ClassUtils;
@AutoConfigureBefore(JmsAutoConfiguration.class)
@ConditionalOnClass({ ConnectionFactory.class, HornetQJMSClient.class })
@EnableConfigurationProperties(HornetQProperties.class)
@Import({ HornetQEmbeddedServerConfiguration.class,
HornetQXAConnectionFactoryConfiguration.class,
HornetQConnectionFactoryConfiguration.class })
public class HornetQAutoConfiguration {
private static final String EMBEDDED_JMS_CLASS = "org.hornetq.jms.server.embedded.EmbeddedJMS";
@Autowired
private HornetQProperties properties;
/**
* Create the {@link ConnectionFactory} to use if none is provided. If no
* {@linkplain HornetQProperties#getMode() mode} has been explicitly set, start an
* embedded server unless it has been explicitly disabled, connect to a broker
* available on the local machine with the default settings otherwise.
*/
@Bean
@ConditionalOnMissingBean
public ConnectionFactory jmsConnectionFactory() {
HornetQMode mode = this.properties.getMode();
if (mode == null) {
mode = deduceMode();
}
if (mode == HornetQMode.EMBEDDED) {
return createEmbeddedConnectionFactory();
}
return createNativeConnectionFactory();
}
/**
* Deduce the {@link HornetQMode} to use if none has been set.
*/
private HornetQMode deduceMode() {
if (this.properties.getEmbedded().isEnabled()
&& ClassUtils.isPresent(EMBEDDED_JMS_CLASS, null)) {
return HornetQMode.EMBEDDED;
}
return HornetQMode.NATIVE;
}
private ConnectionFactory createEmbeddedConnectionFactory() {
try {
TransportConfiguration transportConfiguration = new TransportConfiguration(
InVMConnectorFactory.class.getName(), this.properties.getEmbedded()
.generateTransportParameters());
ServerLocator serviceLocator = HornetQClient
.createServerLocatorWithoutHA(transportConfiguration);
return new HornetQConnectionFactory(serviceLocator);
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Unable to create InVM "
+ "HornetQ connection, ensure that hornet-jms-server.jar "
+ "is in the classpath", ex);
}
}
private ConnectionFactory createNativeConnectionFactory() {
Map<String, Object> params = new HashMap<String, Object>();
params.put(TransportConstants.HOST_PROP_NAME, this.properties.getHost());
params.put(TransportConstants.PORT_PROP_NAME, this.properties.getPort());
TransportConfiguration transportConfiguration = new TransportConfiguration(
NettyConnectorFactory.class.getName(), params);
return HornetQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF,
transportConfiguration);
}
/**
* Configuration used to create the embedded HornetQ server.
*/
@Configuration
@ConditionalOnClass(name = EMBEDDED_JMS_CLASS)
@ConditionalOnExpression("${spring.hornetq.embedded.enabled:true}")
static class EmbeddedServerConfiguration {
@Autowired
private HornetQProperties properties;
@Autowired(required = false)
private List<HornetQConfigurationCustomizer> configurationCustomizers;
@Autowired(required = false)
private List<JMSQueueConfiguration> queuesConfiguration;
@Autowired(required = false)
private List<TopicConfiguration> topicsConfiguration;
@Bean
@ConditionalOnMissingBean
public org.hornetq.core.config.Configuration hornetQConfiguration() {
return new HornetQEmbeddedConfigurationFactory(this.properties)
.createConfiguration();
}
@Bean(initMethod = "start", destroyMethod = "stop")
@ConditionalOnMissingBean
public EmbeddedJMS hornetQServer(
org.hornetq.core.config.Configuration configuration,
JMSConfiguration jmsConfiguration) {
EmbeddedJMS server = new EmbeddedJMS();
customize(configuration);
server.setConfiguration(configuration);
server.setJmsConfiguration(jmsConfiguration);
server.setRegistry(new HornetQNoOpBindingRegistry());
return server;
}
private void customize(org.hornetq.core.config.Configuration configuration) {
if (this.configurationCustomizers != null) {
AnnotationAwareOrderComparator.sort(this.configurationCustomizers);
for (HornetQConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
}
@Bean
@ConditionalOnMissingBean
public JMSConfiguration hornetQJmsConfiguration() {
JMSConfiguration configuration = new JMSConfigurationImpl();
addAll(configuration.getQueueConfigurations(), this.queuesConfiguration);
addAll(configuration.getTopicConfigurations(), this.topicsConfiguration);
addQueues(configuration, this.properties.getEmbedded().getQueues());
addTopics(configuration, this.properties.getEmbedded().getTopics());
return configuration;
}
private <T> void addAll(List<T> list, Collection<? extends T> items) {
if (items != null) {
list.addAll(items);
}
}
private void addQueues(JMSConfiguration configuration, String[] queues) {
boolean persistent = this.properties.getEmbedded().isPersistent();
for (String queue : queues) {
configuration.getQueueConfigurations().add(
new JMSQueueConfigurationImpl(queue, null, persistent, "/queue/"
+ queue));
}
}
private void addTopics(JMSConfiguration configuration, String[] topics) {
for (String topic : topics) {
configuration.getTopicConfigurations().add(
new TopicConfigurationImpl(topic, "/topic/" + topic));
}
}
}
}

@ -0,0 +1,56 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jms.hornetq;
import javax.jms.ConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hornetq.jms.client.HornetQConnectionFactory;
import org.hornetq.jms.server.embedded.EmbeddedJMS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration for HornetQ {@link ConnectionFactory}.
*
* @author Phillip Webb
* @since 1.2.0
*/
@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
class HornetQConnectionFactoryConfiguration {
private static Log logger = LogFactory
.getLog(HornetQEmbeddedServerConfiguration.class);
// Ensure JMS is setup before XA
@Autowired(required = false)
private EmbeddedJMS embeddedJMS;
@Bean
public ConnectionFactory jmsConnectionFactory(HornetQProperties properties) {
if (this.embeddedJMS != null && logger.isDebugEnabled()) {
logger.debug("Using embdedded HornetQ broker");
}
return new HornetQConnectionFactoryFactory(properties)
.createConnectionFactory(HornetQConnectionFactory.class);
}
}

@ -0,0 +1,117 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jms.hornetq;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.core.client.HornetQClient;
import org.hornetq.api.core.client.ServerLocator;
import org.hornetq.core.remoting.impl.invm.InVMConnectorFactory;
import org.hornetq.core.remoting.impl.netty.NettyConnectorFactory;
import org.hornetq.core.remoting.impl.netty.TransportConstants;
import org.hornetq.jms.client.HornetQConnectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Factory to create a {@link HornetQConnectionFactory} instance from properties defined
* in {@link HornetQProperties}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
*/
class HornetQConnectionFactoryFactory {
static final String EMBEDDED_JMS_CLASS = "org.hornetq.jms.server.embedded.EmbeddedJMS";
private final HornetQProperties properties;
public HornetQConnectionFactoryFactory(HornetQProperties properties) {
Assert.notNull(properties, "Properties must not be null");
this.properties = properties;
}
public <T extends HornetQConnectionFactory> T createConnectionFactory(
Class<T> factoryClass) {
try {
return doCreateConnectionFactory(factoryClass);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create "
+ "HornetQConnectionFactory", ex);
}
}
private <T extends HornetQConnectionFactory> T doCreateConnectionFactory(
Class<T> factoryClass) throws Exception {
HornetQMode mode = this.properties.getMode();
if (mode == null) {
mode = deduceMode();
}
if (mode == HornetQMode.EMBEDDED) {
return createEmbeddedConnectionFactory(factoryClass);
}
return createNativeConnectionFactory(factoryClass);
}
/**
* Deduce the {@link HornetQMode} to use if none has been set.
*/
private HornetQMode deduceMode() {
if (this.properties.getEmbedded().isEnabled()
&& ClassUtils.isPresent(EMBEDDED_JMS_CLASS, null)) {
return HornetQMode.EMBEDDED;
}
return HornetQMode.NATIVE;
}
private <T extends HornetQConnectionFactory> T createEmbeddedConnectionFactory(
Class<T> factoryClass) throws Exception {
try {
TransportConfiguration transportConfiguration = new TransportConfiguration(
InVMConnectorFactory.class.getName(), this.properties.getEmbedded()
.generateTransportParameters());
ServerLocator serviceLocator = HornetQClient
.createServerLocatorWithoutHA(transportConfiguration);
return factoryClass.getConstructor(ServerLocator.class).newInstance(
serviceLocator);
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Unable to create InVM "
+ "HornetQ connection, ensure that hornet-jms-server.jar "
+ "is in the classpath", ex);
}
}
private <T extends HornetQConnectionFactory> T createNativeConnectionFactory(
Class<T> factoryClass) throws Exception {
Map<String, Object> params = new HashMap<String, Object>();
params.put(TransportConstants.HOST_PROP_NAME, this.properties.getHost());
params.put(TransportConstants.PORT_PROP_NAME, this.properties.getPort());
TransportConfiguration transportConfiguration = new TransportConfiguration(
NettyConnectorFactory.class.getName(), params);
Constructor<T> constructor = factoryClass.getConstructor(boolean.class,
TransportConfiguration[].class);
return constructor.newInstance(false,
new TransportConfiguration[] { transportConfiguration });
}
}

@ -0,0 +1,122 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jms.hornetq;
import java.util.Collection;
import java.util.List;
import org.hornetq.jms.server.config.JMSConfiguration;
import org.hornetq.jms.server.config.JMSQueueConfiguration;
import org.hornetq.jms.server.config.TopicConfiguration;
import org.hornetq.jms.server.config.impl.JMSConfigurationImpl;
import org.hornetq.jms.server.config.impl.JMSQueueConfigurationImpl;
import org.hornetq.jms.server.config.impl.TopicConfigurationImpl;
import org.hornetq.jms.server.embedded.EmbeddedJMS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
/**
* Configuration used to create the embedded HornetQ server.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
*/
@Configuration
@ConditionalOnClass(name = HornetQConnectionFactoryFactory.EMBEDDED_JMS_CLASS)
@ConditionalOnProperty(prefix = "spring.hornetq.embedded", name = "enabled", havingValue = "true", matchIfMissing = true)
class HornetQEmbeddedServerConfiguration {
@Autowired
private HornetQProperties properties;
@Autowired(required = false)
private List<HornetQConfigurationCustomizer> configurationCustomizers;
@Autowired(required = false)
private List<JMSQueueConfiguration> queuesConfiguration;
@Autowired(required = false)
private List<TopicConfiguration> topicsConfiguration;
@Bean
@ConditionalOnMissingBean
public org.hornetq.core.config.Configuration hornetQConfiguration() {
return new HornetQEmbeddedConfigurationFactory(this.properties)
.createConfiguration();
}
@Bean(initMethod = "start", destroyMethod = "stop")
@ConditionalOnMissingBean
public EmbeddedJMS hornetQServer(org.hornetq.core.config.Configuration configuration,
JMSConfiguration jmsConfiguration) {
EmbeddedJMS server = new EmbeddedJMS();
customize(configuration);
server.setConfiguration(configuration);
server.setJmsConfiguration(jmsConfiguration);
server.setRegistry(new HornetQNoOpBindingRegistry());
return server;
}
private void customize(org.hornetq.core.config.Configuration configuration) {
if (this.configurationCustomizers != null) {
AnnotationAwareOrderComparator.sort(this.configurationCustomizers);
for (HornetQConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
}
@Bean
@ConditionalOnMissingBean
public JMSConfiguration hornetQJmsConfiguration() {
JMSConfiguration configuration = new JMSConfigurationImpl();
addAll(configuration.getQueueConfigurations(), this.queuesConfiguration);
addAll(configuration.getTopicConfigurations(), this.topicsConfiguration);
addQueues(configuration, this.properties.getEmbedded().getQueues());
addTopics(configuration, this.properties.getEmbedded().getTopics());
return configuration;
}
private <T> void addAll(List<T> list, Collection<? extends T> items) {
if (items != null) {
list.addAll(items);
}
}
private void addQueues(JMSConfiguration configuration, String[] queues) {
boolean persistent = this.properties.getEmbedded().isPersistent();
for (String queue : queues) {
configuration.getQueueConfigurations().add(
new JMSQueueConfigurationImpl(queue, null, persistent, "/queue/"
+ queue));
}
}
private void addTopics(JMSConfiguration configuration, String[] topics) {
for (String topic : topics) {
configuration.getTopicConfigurations().add(
new TopicConfigurationImpl(topic, "/topic/" + topic));
}
}
}

@ -0,0 +1,63 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jms.hornetq;
import javax.jms.ConnectionFactory;
import javax.transaction.TransactionManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hornetq.jms.client.HornetQXAConnectionFactory;
import org.hornetq.jms.server.embedded.EmbeddedJMS;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
* Configuration for HornetQ XA {@link ConnectionFactory}.
*
* @author Phillip Webb
* @since 1.2.0
*/
@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
@ConditionalOnClass(TransactionManager.class)
@ConditionalOnBean(XAConnectionFactoryWrapper.class)
class HornetQXAConnectionFactoryConfiguration {
private static Log logger = LogFactory
.getLog(HornetQEmbeddedServerConfiguration.class);
// Ensure JMS is setup before XA
@Autowired(required = false)
private EmbeddedJMS embeddedJMS;
@Bean
public ConnectionFactory jmsConnectionFactory(HornetQProperties properties,
XAConnectionFactoryWrapper wrapper) throws Exception {
if (this.embeddedJMS != null && logger.isDebugEnabled()) {
logger.debug("Using embdedded HornetQ broker with XA");
}
return wrapper.wrapConnectionFactory(new HornetQConnectionFactoryFactory(
properties).createConnectionFactory(HornetQXAConnectionFactory.class));
}
}

@ -24,7 +24,6 @@ import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@ -49,6 +48,10 @@ import static org.junit.Assert.assertTrue;
*/
public class JmsAutoConfigurationTests {
private static final String ACTIVEMQ_EMBEDDED_URL = "vm://localhost?broker.persistent=false";
private static final String ACTIVEMQ_NETWORK_URL = "tcp://localhost:61616";
private AnnotationConfigApplicationContext context;
@Test
@ -61,7 +64,7 @@ public class JmsAutoConfigurationTests {
.getBean(JmsMessagingTemplate.class);
assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory);
assertEquals(jmsTemplate, messagingTemplate.getJmsTemplate());
assertEquals(ActiveMQProperties.DEFAULT_EMBEDDED_BROKER_URL,
assertEquals(ACTIVEMQ_EMBEDDED_URL,
((ActiveMQConnectionFactory) jmsTemplate.getConnectionFactory())
.getBrokerURL());
assertTrue("listener container factory should be created by default",
@ -158,7 +161,7 @@ public class JmsAutoConfigurationTests {
assertNotNull(jmsTemplate);
assertNotNull(connectionFactory);
assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory);
assertEquals(ActiveMQProperties.DEFAULT_NETWORK_BROKER_URL,
assertEquals(ACTIVEMQ_NETWORK_URL,
((ActiveMQConnectionFactory) jmsTemplate.getConnectionFactory())
.getBrokerURL());
}
@ -188,8 +191,7 @@ public class JmsAutoConfigurationTests {
assertEquals(jmsTemplate.getConnectionFactory(), pool);
ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool
.getConnectionFactory();
assertEquals(ActiveMQProperties.DEFAULT_EMBEDDED_BROKER_URL,
factory.getBrokerURL());
assertEquals(ACTIVEMQ_EMBEDDED_URL, factory.getBrokerURL());
}
@Test
@ -204,8 +206,7 @@ public class JmsAutoConfigurationTests {
assertEquals(jmsTemplate.getConnectionFactory(), pool);
ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool
.getConnectionFactory();
assertEquals(ActiveMQProperties.DEFAULT_NETWORK_BROKER_URL,
factory.getBrokerURL());
assertEquals(ACTIVEMQ_NETWORK_URL, factory.getBrokerURL());
}
@Test
@ -257,6 +258,7 @@ public class JmsAutoConfigurationTests {
@Configuration
protected static class TestConfiguration2 {
@Bean
ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory() {
@ -265,10 +267,12 @@ public class JmsAutoConfigurationTests {
}
};
}
}
@Configuration
protected static class TestConfiguration3 {
@Bean
JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
@ -280,6 +284,7 @@ public class JmsAutoConfigurationTests {
@Configuration
protected static class TestConfiguration4 implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
@ -295,6 +300,7 @@ public class JmsAutoConfigurationTests {
throws BeansException {
return bean;
}
}
@Configuration

@ -17,67 +17,50 @@
package org.springframework.boot.autoconfigure.jms.activemq;
import org.junit.Test;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.core.env.StandardEnvironment;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link ActiveMQProperties}.
* Tests for {@link ActiveMQProperties} and ActiveMQConnectionFactoryFactory.
*
* @author Stephane Nicoll
*/
public class ActiveMQPropertiesTests {
private final ActiveMQProperties properties = new ActiveMQProperties();
private final StandardEnvironment environment = new StandardEnvironment();
private static final String DEFAULT_EMBEDDED_BROKER_URL = "vm://localhost?broker.persistent=false";
@Test
public void determineBrokerUrlDefault() {
assertEquals(ActiveMQProperties.DEFAULT_EMBEDDED_BROKER_URL,
ActiveMQProperties.determineBrokerUrl(this.environment));
}
private static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616";
@Test
public void determineBrokerUrlVmBrokerUrl() {
EnvironmentTestUtils.addEnvironment(this.environment,
"spring.activemq.brokerUrl:vm://localhost?persistent=true");
assertEquals("vm://localhost?persistent=true",
ActiveMQProperties.determineBrokerUrl(this.environment));
}
@Test
public void determineBrokerUrlInMemoryFlag() {
EnvironmentTestUtils.addEnvironment(this.environment,
"spring.activemq.inMemory:false");
assertEquals(ActiveMQProperties.DEFAULT_NETWORK_BROKER_URL,
ActiveMQProperties.determineBrokerUrl(this.environment));
}
private final ActiveMQProperties properties = new ActiveMQProperties();
@Test
public void getBrokerUrlIsInMemoryByDefault() {
assertEquals(ActiveMQProperties.DEFAULT_EMBEDDED_BROKER_URL,
this.properties.determineBrokerUrl());
assertEquals(DEFAULT_EMBEDDED_BROKER_URL, new ActiveMQConnectionFactoryFactory(
this.properties).determineBrokerUrl());
}
@Test
public void getBrokerUrlUseExplicitBrokerUrl() {
this.properties.setBrokerUrl("vm://foo-bar");
assertEquals("vm://foo-bar", this.properties.determineBrokerUrl());
assertEquals("vm://foo-bar",
new ActiveMQConnectionFactoryFactory(this.properties)
.determineBrokerUrl());
}
@Test
public void getBrokerUrlWithInMemorySetToFalse() {
this.properties.setInMemory(false);
assertEquals(ActiveMQProperties.DEFAULT_NETWORK_BROKER_URL,
this.properties.determineBrokerUrl());
assertEquals(DEFAULT_NETWORK_BROKER_URL, new ActiveMQConnectionFactoryFactory(
this.properties).determineBrokerUrl());
}
@Test
public void getExplicitBrokerUrlAlwaysWins() {
this.properties.setBrokerUrl("vm://foo-bar");
this.properties.setInMemory(false);
assertEquals("vm://foo-bar", this.properties.determineBrokerUrl());
assertEquals("vm://foo-bar",
new ActiveMQConnectionFactoryFactory(this.properties)
.determineBrokerUrl());
}
}

@ -43,11 +43,13 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.SessionCallback;
@ -183,10 +185,8 @@ public class HornetQAutoConfigurationTests {
@Test
public void embeddedServiceWithCustomJmsConfiguration() {
load(CustomJmsConfiguration.class, "spring.hornetq.embedded.queues=Queue1,Queue2"); // Ignored
// with
// custom
// config
// Ignored with custom config
load(CustomJmsConfiguration.class, "spring.hornetq.embedded.queues=Queue1,Queue2");
DestinationChecker checker = new DestinationChecker(this.context);
checker.checkQueue("custom", true); // See CustomJmsConfiguration
@ -317,7 +317,7 @@ public class HornetQAutoConfigurationTests {
String... environment) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(config);
applicationContext.register(HornetQAutoConfiguration.class,
applicationContext.register(HornetQAutoConfigurationWithoutXA.class,
JmsAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.refresh();
@ -417,4 +417,11 @@ public class HornetQAutoConfigurationTests {
}
}
@Configuration
@EnableConfigurationProperties(HornetQProperties.class)
@Import({ HornetQEmbeddedServerConfiguration.class,
HornetQConnectionFactoryConfiguration.class })
protected static class HornetQAutoConfigurationWithoutXA {
}
}

Loading…
Cancel
Save