diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index b5ba8a6ea3..017a68059f 100644 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -111,6 +111,16 @@ hibernate-jpa-2.0-api true + + org.hornetq + hornetq-jms-client + true + + + org.hornetq + hornetq-jms-server + true + org.springframework spring-jdbc diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQAutoConfiguration.java new file mode 100644 index 0000000000..db8596a29f --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQAutoConfiguration.java @@ -0,0 +1,212 @@ +/* + * 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; + +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.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +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; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to integrate with an HornetQ broker. Connect by default to a broker + * available on the local machine with the default settings. If the necessary classes are + * present, the broker can also be embedded in the application itself. + * + * @author Stephane Nicoll + * @since 1.1.0 + */ +@Configuration +@AutoConfigureBefore(JmsAutoConfiguration.class) +@ConditionalOnClass({ ConnectionFactory.class, HornetQJMSClient.class }) +@EnableConfigurationProperties(HornetQProperties.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, connect to + * the embedded server if it has been requested or 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()); + 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 params = new HashMap(); + 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) + @ConditionalOnProperty(prefix = "spring.hornetq.embedded", value = "enabled") + static class EmbeddedServerConfiguration { + + @Autowired + private HornetQProperties properties; + + @Autowired(required = false) + private List configurationCustomizers; + + @Autowired(required = false) + private List queuesConfiguration; + + @Autowired(required = false) + private List 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(); + applyCustomizers(configuration); + server.setConfiguration(configuration); + server.setJmsConfiguration(jmsConfiguration); + server.setRegistry(new HornetQNoOpBindingRegistry()); + return server; + } + + private void applyCustomizers(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()); + addTopis(configuration, this.properties.getEmbedded().getTopics()); + return configuration; + } + + private void addAll(List list, Collection 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 addTopis(JMSConfiguration configuration, String[] topics) { + for (String topic : topics) { + configuration.getTopicConfigurations().add( + new TopicConfigurationImpl(topic, "/topic/" + topic)); + } + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQConfigurationCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQConfigurationCustomizer.java new file mode 100644 index 0000000000..6ef1afd2f3 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQConfigurationCustomizer.java @@ -0,0 +1,39 @@ +/* + * 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; + +import org.hornetq.core.config.Configuration; +import org.hornetq.jms.server.embedded.EmbeddedJMS; + +/** + * Callback interface that can be implemented by beans wishing to customize the HornetQ + * JMS server {@link Configuration} before it is used by an auto-configured + * {@link EmbeddedJMS} instance. + * + * @author Phillip Webb + * @since 1.1.0 + * @see HornetQAutoConfiguration + */ +public interface HornetQConfigurationCustomizer { + + /** + * Customize the configuration. + * @param configuration the configuration to customize + */ + void customize(Configuration configuration); + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQEmbeddedConfigurationFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQEmbeddedConfigurationFactory.java new file mode 100644 index 0000000000..783a258733 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQEmbeddedConfigurationFactory.java @@ -0,0 +1,85 @@ +/* + * 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; + +import java.io.File; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hornetq.api.core.TransportConfiguration; +import org.hornetq.core.config.Configuration; +import org.hornetq.core.config.impl.ConfigurationImpl; +import org.hornetq.core.remoting.impl.invm.InVMAcceptorFactory; +import org.hornetq.core.server.JournalType; +import org.springframework.boot.autoconfigure.jms.HornetQProperties.Embedded; + +/** + * Factory class to create a HornetQ {@link Configuration} from {@link HornetQProperties}. + * + * @author Stephane Nicol + * @author Phillip Webb + * @since 1.1.0 + */ +class HornetQEmbeddedConfigurationFactory { + + private Log logger = LogFactory.getLog(HornetQAutoConfiguration.class); + + private final Embedded properties; + + public HornetQEmbeddedConfigurationFactory(HornetQProperties properties) { + this.properties = properties.getEmbedded(); + } + + public Configuration createConfiguration() { + ConfigurationImpl configuration = new ConfigurationImpl(); + configuration.setSecurityEnabled(false); + configuration.setPersistenceEnabled(this.properties.isPersistent()); + + String dataDir = getDataDir(); + + // HORNETQ-1302 + configuration.setJournalDirectory(dataDir + "/journal"); + + if (this.properties.isPersistent()) { + configuration.setJournalType(JournalType.NIO); + configuration.setLargeMessagesDirectory(dataDir + "/largemessages"); + configuration.setBindingsDirectory(dataDir + "/bindings"); + configuration.setPagingDirectory(dataDir + "/paging"); + } + + TransportConfiguration transportConfiguration = new TransportConfiguration( + InVMAcceptorFactory.class.getName()); + configuration.getAcceptorConfigurations().add(transportConfiguration); + + // HORNETQ-1143 + if (this.properties.isDefaultClusterPassword()) { + this.logger.debug("Using default HornetQ cluster password: " + + this.properties.getClusterPassword()); + } + + configuration.setClusterPassword(this.properties.getClusterPassword()); + return configuration; + } + + private String getDataDir() { + if (this.properties.getDataDirectory() != null) { + return this.properties.getDataDirectory(); + } + String tempDirectory = System.getProperty("java.io.tmpdir"); + return new File(tempDirectory, "hornetq-data").getAbsolutePath(); + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQMode.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQMode.java new file mode 100644 index 0000000000..d35fe3808e --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQMode.java @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * Define the mode in which HornetQ can operate. + * + * @author Stephane Nicoll + * @since 1.1.0 + */ +public enum HornetQMode { + + /** + * Connect to a broker using the native HornetQ protocol (i.e. netty). + */ + NATIVE, + + /** + * Embed (i.e. start) the broker in the application. + */ + EMBEDDED + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQNoOpBindingRegistry.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQNoOpBindingRegistry.java new file mode 100644 index 0000000000..7c848f6f25 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQNoOpBindingRegistry.java @@ -0,0 +1,64 @@ +/* + * 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; + +import org.hornetq.spi.core.naming.BindingRegistry; + +/** + * A no-op implementation of the {@link org.hornetq.spi.core.naming.BindingRegistry}. + * + * @author Stephane Nicoll + * @since 1.1.0 + */ +public class HornetQNoOpBindingRegistry implements BindingRegistry { + + @Override + public Object lookup(String name) { + // This callback is used to check if an entry is present in the context before + // creating a queue on the fly. This is actually never used to try to fetch a + // destination that is unknown. + return null; + } + + @Override + public boolean bind(String name, Object obj) { + // This callback is used bind a Destination created on the fly by the embedded + // broker using the JNDI name that was specified in the configuration. This does + // not look very useful since it's used nowhere. It could be interesting to + // autowire a destination to use it but the wiring is a bit "asynchronous" so + // better not provide that feature at all. + return false; + } + + @Override + public void unbind(String name) { + } + + @Override + public void close() { + } + + @Override + public Object getContext() { + return this; + } + + @Override + public void setContext(Object ctx) { + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQProperties.java new file mode 100644 index 0000000000..1cd229a04b --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/HornetQProperties.java @@ -0,0 +1,142 @@ +/* + * 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; + +import java.util.UUID; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for HornetQ + * + * @author Stephane Nicoll + * @since 1.1.0 + */ +@ConfigurationProperties(prefix = "spring.hornetq") +public class HornetQProperties { + + private HornetQMode mode; + + private String host = "localhost"; + + private int port = 5445; + + private final Embedded embedded = new Embedded(); + + public HornetQMode getMode() { + return this.mode; + } + + public void setMode(HornetQMode mode) { + this.mode = mode; + } + + public String getHost() { + return this.host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return this.port; + } + + public void setPort(int port) { + this.port = port; + } + + public Embedded getEmbedded() { + return this.embedded; + } + + /** + * Configuration for an embedded HornetQ server. + */ + public static class Embedded { + + private boolean enabled; + + private boolean persistent; + + private String dataDirectory; + + private String[] queues = new String[0]; + + private String[] topics = new String[0]; + + private String clusterPassword = UUID.randomUUID().toString(); + + private boolean defaultClusterPassword = true; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isPersistent() { + return this.persistent; + } + + public void setPersistent(boolean persistent) { + this.persistent = persistent; + } + + public String getDataDirectory() { + return this.dataDirectory; + } + + public void setDataDirectory(String dataDirectory) { + this.dataDirectory = dataDirectory; + } + + public String[] getQueues() { + return this.queues; + } + + public void setQueues(String[] queues) { + this.queues = queues; + } + + public String[] getTopics() { + return this.topics; + } + + public void setTopics(String[] topics) { + this.topics = topics; + } + + public String getClusterPassword() { + return this.clusterPassword; + } + + public void setClusterPassword(String clusterPassword) { + this.clusterPassword = clusterPassword; + this.defaultClusterPassword = false; + } + + public boolean isDefaultClusterPassword() { + return this.defaultClusterPassword; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java index 568335badd..f225c3ae43 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -37,6 +38,7 @@ import org.springframework.jms.core.JmsTemplate; @ConditionalOnClass(JmsTemplate.class) @ConditionalOnBean(ConnectionFactory.class) @EnableConfigurationProperties(JmsProperties.class) +@AutoConfigureAfter({ HornetQAutoConfiguration.class, ActiveMQAutoConfiguration.class }) public class JmsAutoConfiguration { @Autowired diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 8fd434cc77..1696504f29 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -19,6 +19,7 @@ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.ActiveMQAutoConfiguration,\ +org.springframework.boot.autoconfigure.jms.HornetQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/ActiveMQPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/ActiveMQPropertiesTests.java index dd61c4f042..ad0ab44cab 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/ActiveMQPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/ActiveMQPropertiesTests.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertEquals; public class ActiveMQPropertiesTests { private final ActiveMQProperties properties = new ActiveMQProperties(); + private final StandardEnvironment environment = new StandardEnvironment(); @Test diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/HornetQAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/HornetQAutoConfigurationTests.java new file mode 100644 index 0000000000..5321dc9a42 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/HornetQAutoConfigurationTests.java @@ -0,0 +1,341 @@ +/* + * 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; + +import java.io.File; +import java.io.IOException; +import java.util.UUID; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.hornetq.api.core.TransportConfiguration; +import org.hornetq.core.remoting.impl.invm.InVMConnectorFactory; +import org.hornetq.core.remoting.impl.netty.NettyConnectorFactory; +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.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.springframework.beans.factory.annotation.Autowired; +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.jms.core.JmsTemplate; +import org.springframework.jms.core.MessageCreator; +import org.springframework.jms.core.SessionCallback; +import org.springframework.jms.support.destination.DestinationResolver; +import org.springframework.jms.support.destination.DynamicDestinationResolver; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * Tests for {@link HornetQAutoConfiguration}. + * + * @author Stephane Nicoll + */ +public class HornetQAutoConfigurationTests { + + @Rule + public final TemporaryFolder folder = new TemporaryFolder(); + + private AnnotationConfigApplicationContext context; + + @After + public void tearDown() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void nativeConnectionFactory() { + load(EmptyConfiguration.class, "spring.hornetq.mode:native"); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + HornetQConnectionFactory connectionFactory = this.context + .getBean(HornetQConnectionFactory.class); + assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory); + assertNettyConnectionFactory(connectionFactory, "localhost", 5445); + } + + @Test + public void nativeConnectionFactoryCustomHost() { + load(EmptyConfiguration.class, "spring.hornetq.host:192.168.1.144", + "spring.hornetq.port:9876"); + HornetQConnectionFactory connectionFactory = this.context + .getBean(HornetQConnectionFactory.class); + assertNettyConnectionFactory(connectionFactory, "192.168.1.144", 9876); + } + + @Test + public void embeddedConnectionFactory() { + load(EmptyConfiguration.class, "spring.hornetq.mode:embedded", + "spring.hornetq.embedded.enabled:true"); + + HornetQProperties properties = this.context.getBean(HornetQProperties.class); + assertEquals(HornetQMode.EMBEDDED, properties.getMode()); + + assertEquals(1, this.context.getBeansOfType(EmbeddedJMS.class).size()); + org.hornetq.core.config.Configuration configuration = this.context + .getBean(org.hornetq.core.config.Configuration.class); + assertFalse("Persistence disabled by default", + configuration.isPersistenceEnabled()); + assertFalse("Security disabled by default", configuration.isSecurityEnabled()); + + HornetQConnectionFactory connectionFactory = this.context + .getBean(HornetQConnectionFactory.class); + assertInVmConnectionFactory(connectionFactory); + } + + @Test + public void nativeConnectionFactoryByDefault() { + // No mode is specified + load(EmptyConfiguration.class); + HornetQConnectionFactory connectionFactory = this.context + .getBean(HornetQConnectionFactory.class); + assertNettyConnectionFactory(connectionFactory, "localhost", 5445); + } + + @Test + public void embeddedConnectionFactoryIfEmbeddedServiceEnabled() { + // No mode enabled, embedded server required + load(EmptyConfiguration.class, "spring.hornetq.embedded.enabled:true"); + HornetQConnectionFactory connectionFactory = this.context + .getBean(HornetQConnectionFactory.class); + assertInVmConnectionFactory(connectionFactory); + } + + @Test + public void embeddedServerWithDestinations() { + load(EmptyConfiguration.class, "spring.hornetq.embedded.enabled:true", + "spring.hornetq.embedded.queues=Queue1,Queue2", + "spring.hornetq.embedded.topics=Topic1"); + + DestinationChecker checker = new DestinationChecker(this.context); + checker.checkQueue("Queue1", true); + checker.checkQueue("Queue2", true); + checker.checkQueue("QueueDoesNotExist", false); + + checker.checkTopic("Topic1", true); + checker.checkTopic("TopicDoesNotExist", false); + } + + @Test + public void embeddedServerWithDestinationConfig() { + load(DestinationConfiguration.class, "spring.hornetq.embedded.enabled:true"); + + DestinationChecker checker = new DestinationChecker(this.context); + checker.checkQueue("sampleQueue", true); + checker.checkTopic("sampleTopic", true); + } + + @Test + public void embeddedServiceWithCustomJmsConfiguration() { + load(CustomJmsConfiguration.class, "spring.hornetq.embedded.enabled:true", + "spring.hornetq.embedded.queues=Queue1,Queue2"); // Ignored with custom + // config + DestinationChecker checker = new DestinationChecker(this.context); + checker.checkQueue("custom", true); // See CustomJmsConfiguration + + checker.checkQueue("Queue1", false); + checker.checkQueue("Queue2", false); + } + + @Test + public void embeddedServiceWithCustomHornetQConfiguration() { + load(CustomHornetQConfiguration.class, "spring.hornetq.embedded.enabled:true"); + org.hornetq.core.config.Configuration configuration = this.context + .getBean(org.hornetq.core.config.Configuration.class); + assertEquals("customFooBar", configuration.getName()); + } + + @Test + public void embeddedWithPersistentMode() throws IOException, JMSException { + File dataFolder = this.folder.newFolder(); + + // Start the server and post a message to some queue + load(EmptyConfiguration.class, "spring.hornetq.embedded.enabled:true", + "spring.hornetq.embedded.queues=TestQueue", + "spring.hornetq.embedded.persistent:true", + "spring.hornetq.embedded.dataDirectory:" + dataFolder.getAbsolutePath()); + + final String msgId = UUID.randomUUID().toString(); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + jmsTemplate.send("TestQueue", new MessageCreator() { + @Override + public Message createMessage(Session session) throws JMSException { + return session.createTextMessage(msgId); + } + }); + this.context.close(); // Shutdown the broker + + // Start the server again and check if our message is still here + load(EmptyConfiguration.class, "spring.hornetq.embedded.enabled:true", + "spring.hornetq.embedded.queues=TestQueue", + "spring.hornetq.embedded.persistent:true", + "spring.hornetq.embedded.dataDirectory:" + dataFolder.getAbsolutePath()); + + JmsTemplate jmsTemplate2 = this.context.getBean(JmsTemplate.class); + jmsTemplate2.setReceiveTimeout(1000L); + Message message = jmsTemplate2.receive("TestQueue"); + assertNotNull("No message on persistent queue", message); + assertEquals("Invalid message received on queue", msgId, + ((TextMessage) message).getText()); + } + + private TransportConfiguration assertInVmConnectionFactory( + HornetQConnectionFactory connectionFactory) { + TransportConfiguration transportConfig = getSingleTransportConfiguration(connectionFactory); + assertEquals(InVMConnectorFactory.class.getName(), + transportConfig.getFactoryClassName()); + return transportConfig; + } + + private TransportConfiguration assertNettyConnectionFactory( + HornetQConnectionFactory connectionFactory, String host, int port) { + TransportConfiguration transportConfig = getSingleTransportConfiguration(connectionFactory); + assertEquals(NettyConnectorFactory.class.getName(), + transportConfig.getFactoryClassName()); + assertEquals(host, transportConfig.getParams().get("host")); + assertEquals(port, transportConfig.getParams().get("port")); + return transportConfig; + } + + private TransportConfiguration getSingleTransportConfiguration( + HornetQConnectionFactory connectionFactory) { + TransportConfiguration[] transportConfigurations = connectionFactory + .getServerLocator().getStaticTransportConfigurations(); + assertEquals(1, transportConfigurations.length); + return transportConfigurations[0]; + } + + private void load(Class config, String... environment) { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(config); + this.context.register(HornetQAutoConfiguration.class, JmsAutoConfiguration.class); + EnvironmentTestUtils.addEnvironment(this.context, environment); + this.context.refresh(); + } + + private static class DestinationChecker { + + private final JmsTemplate jmsTemplate; + + private final DestinationResolver destinationResolver; + + private DestinationChecker(ApplicationContext applicationContext) { + this.jmsTemplate = applicationContext.getBean(JmsTemplate.class); + this.destinationResolver = new DynamicDestinationResolver(); + } + + public void checkQueue(String name, boolean shouldExist) { + checkDestination(name, false, shouldExist); + } + + public void checkTopic(String name, boolean shouldExist) { + checkDestination(name, true, shouldExist); + } + + public void checkDestination(final String name, final boolean pubSub, + final boolean shouldExist) { + this.jmsTemplate.execute(new SessionCallback() { + @Override + public Void doInJms(Session session) throws JMSException { + try { + Destination destination = DestinationChecker.this.destinationResolver + .resolveDestinationName(session, name, pubSub); + if (!shouldExist) { + throw new IllegalStateException("Destination '" + name + + "' was not expected but got " + destination); + } + } + catch (JMSException e) { + if (shouldExist) { + throw new IllegalStateException("Destination '" + name + + "' was expected but got " + e.getMessage()); + } + } + return null; + } + }); + } + } + + @Configuration + protected static class EmptyConfiguration { + } + + @Configuration + protected static class DestinationConfiguration { + + @Bean + JMSQueueConfiguration sampleQueueConfiguration() { + return new JMSQueueConfigurationImpl("sampleQueue", "foo=bar", false, + "/queue/1"); + } + + @Bean + TopicConfiguration sampleTopicConfiguration() { + return new TopicConfigurationImpl("sampleTopic", "/topic/1"); + } + } + + @Configuration + protected static class CustomJmsConfiguration { + + @Bean + public JMSConfiguration myJmsConfiguration() { + JMSConfiguration config = new JMSConfigurationImpl(); + config.getQueueConfigurations().add( + new JMSQueueConfigurationImpl("custom", null, false)); + return config; + } + } + + @Configuration + protected static class CustomHornetQConfiguration { + + @Autowired + private HornetQProperties properties; + + @Bean + public HornetQConfigurationCustomizer myHornetQCustomize() { + return new HornetQConfigurationCustomizer() { + @Override + public void customize(org.hornetq.core.config.Configuration configuration) { + configuration.setClusterPassword("Foobar"); + configuration.setName("customFooBar"); + } + }; + } + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/HornetQEmbeddedConfigurationFactoryTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/HornetQEmbeddedConfigurationFactoryTests.java new file mode 100644 index 0000000000..288c4b24bd --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/HornetQEmbeddedConfigurationFactoryTests.java @@ -0,0 +1,74 @@ +/* + * 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; + +import org.hornetq.core.config.Configuration; +import org.hornetq.core.server.JournalType; +import org.junit.Test; + +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link HornetQEmbeddedConfigurationFactory}. + * + * @author Stephane Nicol + * @author Phillip Webb + */ +public class HornetQEmbeddedConfigurationFactoryTests { + + @Test + public void defaultDataDir() { + HornetQProperties properties = new HornetQProperties(); + properties.getEmbedded().setPersistent(true); + Configuration configuration = new HornetQEmbeddedConfigurationFactory(properties) + .createConfiguration(); + assertThat(configuration.getJournalDirectory(), + startsWith(System.getProperty("java.io.tmpdir"))); + assertThat(configuration.getJournalDirectory(), endsWith("/journal")); + } + + @Test + public void persistenceSetup() { + HornetQProperties properties = new HornetQProperties(); + properties.getEmbedded().setPersistent(true); + Configuration configuration = new HornetQEmbeddedConfigurationFactory(properties) + .createConfiguration(); + assertThat(configuration.isPersistenceEnabled(), equalTo(true)); + assertThat(configuration.getJournalType(), equalTo(JournalType.NIO)); + } + + @Test + public void generatedClusterPassoword() throws Exception { + HornetQProperties properties = new HornetQProperties(); + Configuration configuration = new HornetQEmbeddedConfigurationFactory(properties) + .createConfiguration(); + assertThat(configuration.getClusterPassword().length(), equalTo(36)); + } + + @Test + public void specificClusterPassoword() throws Exception { + HornetQProperties properties = new HornetQProperties(); + properties.getEmbedded().setClusterPassword("password"); + Configuration configuration = new HornetQEmbeddedConfigurationFactory(properties) + .createConfiguration(); + assertThat(configuration.getClusterPassword(), equalTo("password")); + } + +} diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index edf00f2267..5058e8cffe 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -68,6 +68,7 @@ 1.3.8 4.3.3 4.0.1 + 2.4.1.Final 2.3.2 2.3.3 3.18.1-GA @@ -212,6 +213,11 @@ spring-boot-starter-groovy-templates 1.1.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-starter-hornetq + 1.1.0.BUILD-SNAPSHOT + org.springframework.boot spring-boot-starter-integration @@ -714,6 +720,16 @@ hibernate-jpa-2.0-api ${hibernate-jpa-api.version} + + org.hornetq + hornetq-jms-server + ${hornetq.version} + + + org.hornetq + hornetq-jms-client + ${hornetq.version} + org.hsqldb hsqldb diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 2d67717bb1..3c44b9f7f8 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -242,6 +242,17 @@ content into your application; rather pick only the properties that you need. spring.activemq.in-memory=true # broker kind to create if no broker-url is specified spring.activemq.pooled=false + # HornetQ ({sc-spring-boot-autoconfigure}/jms/HornetQProperties.{sc-ext}[HornetQProperties]) + spring.hornetq.mode= # connection mode (native, embedded) + spring.hornetq.host=localhost # hornetQ host (native mode) + spring.hornetq.port=5445 # hornetQ port (native mode) + spring.hornetq.embedded.enabled=true # if the embedded server is enabled (needs hornetq-jms-server.jar) + spring.hornetq.embedded.persistent=false # message persistence + spring.hornetq.embedded.data-directory= # location of data content (when persistence is enabled) + spring.hornetq.embedded.queues= # comma separate queues to create on startup + spring.hornetq.embedded.topics= # comma separate topics to create on startup + spring.hornetq.embedded.cluster-password = # customer password (randomly generated by default) + # JMS ({sc-spring-boot-autoconfigure}/jms/JmsTemplateProperties.{sc-ext}[JmsTemplateProperties]) spring.jms.pub-sub-domain= # false for queue (default), true for topic diff --git a/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc b/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc index 6a85e9dec6..6fb5bffbe5 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc @@ -53,6 +53,12 @@ The following auto-configuration classes are from the `spring-boot-autoconfigure |{sc-spring-boot-autoconfigure}/web/HttpMessageConvertersAutoConfiguration.{sc-ext}[HttpMessageConvertersAutoConfiguration] |{dc-spring-boot-autoconfigure}/web/HttpMessageConvertersAutoConfiguration.{dc-ext}[javadoc] +|{sc-spring-boot-autoconfigure}/jms/ActiveMQAutoConfiguration.{sc-ext}[ActiveMQAutoConfiguration] +|{dc-spring-boot-autoconfigure}/jms/ActiveMQAutoConfiguration.{dc-ext}[javadoc] + +|{sc-spring-boot-autoconfigure}/jms/HornetQAutoConfiguration.{sc-ext}[HornetQAutoConfiguration] +|{dc-spring-boot-autoconfigure}/jms/HornetQAutoConfiguration.{dc-ext}[javadoc] + |{sc-spring-boot-autoconfigure}/jms/JmsTemplateAutoConfiguration.{sc-ext}[JmsTemplateAutoConfiguration] |{dc-spring-boot-autoconfigure}/jms/JmsTemplateAutoConfiguration.{dc-ext}[javadoc] diff --git a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc index 6015ea1b15..b7a67803f0 100644 --- a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc +++ b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc @@ -245,6 +245,9 @@ and Hibernate. |`spring-boot-starter-groovy-templates` |Support for the Groovy templating engine +|`spring-boot-starter-hornetq` +|Support for ``Java Message Service API'' via HornetQ. + |`spring-boot-starter-integration` |Support for common `spring-integration` modules. diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 48da9418c2..881c2e013d 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -34,6 +34,7 @@ spring-boot-sample-data-rest spring-boot-sample-data-solr spring-boot-sample-flyway + spring-boot-sample-hornetq spring-boot-sample-integration spring-boot-sample-jetty spring-boot-sample-liquibase diff --git a/spring-boot-samples/spring-boot-sample-hornetq/pom.xml b/spring-boot-samples/spring-boot-sample-hornetq/pom.xml new file mode 100644 index 0000000000..fbe5f44e54 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hornetq/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.1.0.BUILD-SNAPSHOT + + spring-boot-sample-hornetq + Spring Boot HornetQ Sample + Spring Boot HornetQ Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter-hornetq + + + org.hornetq + hornetq-jms-server + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-hornetq/src/main/java/sample/hornetq/SampleHornetQApplication.java b/spring-boot-samples/spring-boot-sample-hornetq/src/main/java/sample/hornetq/SampleHornetQApplication.java new file mode 100644 index 0000000000..b73a8626ca --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hornetq/src/main/java/sample/hornetq/SampleHornetQApplication.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2013 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 sample.hornetq; + +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.jms.listener.DefaultMessageListenerContainer; +import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class SampleHornetQApplication { + + @Autowired + private ConnectionFactory connectionFactory; + + @Bean + public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor() { + return new ScheduledAnnotationBeanPostProcessor(); + } + + @Bean + public DefaultMessageListenerContainer messageListener() { + DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(); + container.setConnectionFactory(this.connectionFactory); + container.setDestinationName("testQueue"); + container.setMessageListener(new MessageListener() { + @Override + public void onMessage(Message message) { + try { + System.out.println(message.getBody(Object.class)); + } + catch (JMSException ex) { + ex.printStackTrace(); + } + } + }); + return container; + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleHornetQApplication.class, args); + } +} diff --git a/spring-boot-samples/spring-boot-sample-hornetq/src/main/java/sample/hornetq/Sender.java b/spring-boot-samples/spring-boot-sample-hornetq/src/main/java/sample/hornetq/Sender.java new file mode 100644 index 0000000000..5c34c4d4c6 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hornetq/src/main/java/sample/hornetq/Sender.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2013 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 sample.hornetq; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class Sender { + + @Autowired + private JmsTemplate jmsTemplate; + + @Scheduled(fixedDelay = 1000L) + public void send() { + this.jmsTemplate.convertAndSend("testQueue", "Hello"); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-hornetq/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-hornetq/src/main/resources/application.properties new file mode 100644 index 0000000000..f7c09bcefd --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hornetq/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.hornetq.mode=embedded +spring.hornetq.embedded.enabled=true +spring.hornetq.embedded.queues=testQueue,anotherQueue diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index bb7a588aae..b9cee097aa 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -31,6 +31,7 @@ spring-boot-starter-data-solr spring-boot-starter-freemarker spring-boot-starter-groovy-templates + spring-boot-starter-hornetq spring-boot-starter-integration spring-boot-starter-jdbc spring-boot-starter-jetty diff --git a/spring-boot-starters/spring-boot-starter-hornetq/pom.xml b/spring-boot-starters/spring-boot-starter-hornetq/pom.xml new file mode 100644 index 0000000000..25e7f14d52 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-hornetq/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starters + 1.1.0.BUILD-SNAPSHOT + + spring-boot-starter-hornetq + Spring Boot HornetQ Starter + Spring Boot HornetQ Starter + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter + + + org.springframework + spring-jms + + + org.hornetq + hornetq-jms-client + + +