From fa6e6fde6c8d71710b0e84819485d22ea18d9daa Mon Sep 17 00:00:00 2001 From: Greg Turnquist Date: Fri, 20 Sep 2013 09:23:51 -0500 Subject: [PATCH] Add JMS autoconfig support * application.properties support for spring.jms and spring.activemq * more tests to verify ActiveMQConnectionFactory pooling * Groovy support and simple sample with activemq * Groovy detection mechanism is @EnableJmsMessaging annotation --- spring-boot-autoconfigure/pom.xml | 5 + .../jms/JmsTemplateAutoConfiguration.java | 78 ++++++++++++- .../JmsTemplateAutoConfigurationTests.java | 107 ++++++++++++++++++ spring-boot-cli/samples/jms.groovy | 5 +- .../JmsCompilerAutoConfiguration.java | 17 ++- spring-boot-dependencies/pom.xml | 5 + 6 files changed, 209 insertions(+), 8 deletions(-) diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 22b8d1f14a..309b5d597b 100644 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -31,6 +31,11 @@ activemq-core true + + org.apache.activemq + activemq-pool + true + org.apache.tomcat.embed tomcat-embed-core diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfiguration.java index 46a387c5a1..464b7312f5 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfiguration.java @@ -19,10 +19,13 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.pool.PooledConnectionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.core.JmsTemplate; @@ -38,7 +41,11 @@ public class JmsTemplateAutoConfiguration { @Configuration @ConditionalOnMissingBean(JmsTemplate.class) + @EnableConfigurationProperties(JmsTemplateProperties.class) protected static class JmsTemplateCreator { + + @Autowired + private JmsTemplateProperties config; @Autowired private ConnectionFactory connectionFactory; @@ -46,22 +53,87 @@ public class JmsTemplateAutoConfiguration { @Bean public JmsTemplate jmsTemplate() { JmsTemplate jmsTemplate = new JmsTemplate(this.connectionFactory); - jmsTemplate.setPubSubDomain(true); + jmsTemplate.setPubSubDomain(this.config.isPubSubDomain()); return jmsTemplate; } } + + @ConfigurationProperties(name = "spring.jms") + public static class JmsTemplateProperties { + + private boolean pubSubDomain = true; + + public boolean isPubSubDomain() { + return pubSubDomain; + } + + public void setPubSubDomain(boolean pubSubDomain) { + this.pubSubDomain = pubSubDomain; + } + + } @Configuration @ConditionalOnClass(ActiveMQConnectionFactory.class) @ConditionalOnMissingBean(ConnectionFactory.class) + @EnableConfigurationProperties(ActiveMQConnectionFactoryProperties.class) protected static class ActiveMQConnectionFactoryCreator { - + + @Autowired + private ActiveMQConnectionFactoryProperties config; + @Bean ConnectionFactory connectionFactory() { - return new ActiveMQConnectionFactory("vm://localhost"); + if (this.config.isPooled()) { + PooledConnectionFactory pool = new PooledConnectionFactory(); + pool.setConnectionFactory(new ActiveMQConnectionFactory(this.config.getBrokerURL())); + return pool; + } else { + return new ActiveMQConnectionFactory(this.config.getBrokerURL()); + } + } + + } + + @ConfigurationProperties(name = "spring.activemq") + public static class ActiveMQConnectionFactoryProperties { + + private String brokerURL = "tcp://localhost:61616"; + + private boolean inMemory = true; + + private boolean pooled = false; + + // Will override brokerURL if inMemory is set to true + public String getBrokerURL() { + if (this.inMemory) { + return "vm://localhost"; + } else { + return this.brokerURL; + } + } + + public void setBrokerURL(String brokerURL) { + this.brokerURL = brokerURL; + } + + public boolean isInMemory() { + return inMemory; } + public void setInMemory(boolean inMemory) { + this.inMemory = inMemory; + } + + public boolean isPooled() { + return pooled; + } + + public void setPooled(boolean pooled) { + this.pooled = pooled; + } + } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfigurationTests.java index c8c44df97b..d477ffcba8 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfigurationTests.java @@ -19,9 +19,11 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.pool.PooledConnectionFactory; import org.junit.Test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.TestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -53,6 +55,7 @@ public class JmsTemplateAutoConfigurationTests { assertNotNull(jmsTemplate); assertNotNull(connectionFactory); assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory); + assertEquals(((ActiveMQConnectionFactory)jmsTemplate.getConnectionFactory()).getBrokerURL(), "vm://localhost"); } @Test @@ -106,6 +109,110 @@ public class JmsTemplateAutoConfigurationTests { JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); assertFalse(jmsTemplate.isPubSubDomain()); } + + @Test + public void testJmsTemplateOverridden() { + this.context = new AnnotationConfigApplicationContext(); + this.context + .register(TestConfiguration.class, JmsTemplateAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "spring.jms.pubSubDomain:false"); + this.context.refresh(); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + ActiveMQConnectionFactory connectionFactory = this.context + .getBean(ActiveMQConnectionFactory.class); + assertNotNull(jmsTemplate); + assertFalse(jmsTemplate.isPubSubDomain()); + assertNotNull(connectionFactory); + assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory); + } + + @Test + public void testActiveMQOverriddenStandalone() { + this.context = new AnnotationConfigApplicationContext(); + this.context + .register(TestConfiguration.class, JmsTemplateAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "spring.activemq.inMemory:false"); + this.context.refresh(); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + ActiveMQConnectionFactory connectionFactory = this.context + .getBean(ActiveMQConnectionFactory.class); + assertNotNull(jmsTemplate); + assertNotNull(connectionFactory); + assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory); + assertEquals(((ActiveMQConnectionFactory)jmsTemplate.getConnectionFactory()).getBrokerURL(), + "tcp://localhost:61616"); + } + + @Test + public void testActiveMQOverriddenRemoteHost() { + this.context = new AnnotationConfigApplicationContext(); + this.context + .register(TestConfiguration.class, JmsTemplateAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "spring.activemq.inMemory:false", + "spring.activemq.brokerURL:tcp://remote-host:10000"); + this.context.refresh(); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + ActiveMQConnectionFactory connectionFactory = this.context + .getBean(ActiveMQConnectionFactory.class); + assertNotNull(jmsTemplate); + assertNotNull(connectionFactory); + assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory); + assertEquals(((ActiveMQConnectionFactory)jmsTemplate.getConnectionFactory()).getBrokerURL(), + "tcp://remote-host:10000"); + } + + @Test + public void testActiveMQOverriddenPool() { + this.context = new AnnotationConfigApplicationContext(); + this.context + .register(TestConfiguration.class, JmsTemplateAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "spring.activemq.pooled:true"); + this.context.refresh(); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + PooledConnectionFactory pool = this.context + .getBean(PooledConnectionFactory.class); + assertNotNull(jmsTemplate); + assertNotNull(pool); + assertEquals(jmsTemplate.getConnectionFactory(), pool); + ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool.getConnectionFactory(); + assertEquals("vm://localhost", factory.getBrokerURL()); + } + + @Test + public void testActiveMQOverriddenPoolAndStandalone() { + this.context = new AnnotationConfigApplicationContext(); + this.context + .register(TestConfiguration.class, JmsTemplateAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "spring.activemq.pooled:true", + "spring.activemq.inMemory:false"); + this.context.refresh(); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + PooledConnectionFactory pool = this.context + .getBean(PooledConnectionFactory.class); + assertNotNull(jmsTemplate); + assertNotNull(pool); + assertEquals(jmsTemplate.getConnectionFactory(), pool); + ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool.getConnectionFactory(); + assertEquals("tcp://localhost:61616", factory.getBrokerURL()); + } + + @Test + public void testActiveMQOverriddenPoolAndRemoteServer() { + this.context = new AnnotationConfigApplicationContext(); + this.context + .register(TestConfiguration.class, JmsTemplateAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "spring.activemq.pooled:true", + "spring.activemq.inMemory:false", "spring.activemq.brokerURL:tcp://remote-host:10000"); + this.context.refresh(); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + PooledConnectionFactory pool = this.context + .getBean(PooledConnectionFactory.class); + assertNotNull(jmsTemplate); + assertNotNull(pool); + assertEquals(jmsTemplate.getConnectionFactory(), pool); + ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool.getConnectionFactory(); + assertEquals("tcp://remote-host:10000", factory.getBrokerURL()); + } @Configuration protected static class TestConfiguration { diff --git a/spring-boot-cli/samples/jms.groovy b/spring-boot-cli/samples/jms.groovy index 7e189b0498..df2a62d475 100644 --- a/spring-boot-cli/samples/jms.groovy +++ b/spring-boot-cli/samples/jms.groovy @@ -1,11 +1,13 @@ package org.test @Grab("org.apache.activemq:activemq-all:5.4.0") +@Grab("org.apache.activemq:activemq-pool:5.4.0") import java.util.concurrent.CountDownLatch -@Configuration @Log +@Configuration +@EnableJmsMessaging class JmsExample implements CommandLineRunner { private CountDownLatch latch = new CountDownLatch(1) @@ -30,7 +32,6 @@ class JmsExample implements CommandLineRunner { session.createObjectMessage("Greetings from Spring Boot via ActiveMQ") } as MessageCreator log.info "Sending JMS message..." - jmsTemplate.pubSubDomain = true jmsTemplate.send("spring-boot", messageCreator) latch.await() } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java index 5c6d47454a..c2182fad48 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java @@ -23,6 +23,8 @@ import org.springframework.boot.cli.compiler.AstUtils; import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; import org.springframework.boot.cli.compiler.DependencyCustomizer; +import java.lang.annotation.*; + /** * {@link CompilerAutoConfiguration} for Spring JMS. * @@ -32,8 +34,9 @@ public class JmsCompilerAutoConfiguration extends CompilerAutoConfiguration { @Override public boolean matches(ClassNode classNode) { - return AstUtils.hasAtLeastOneFieldOrMethod(classNode, "JmsTemplate", - "DefaultMessageListenerContainer", "SimpleMessageListenerContainer"); + // Slightly weird detection algorithm because there is no @Enable annotation for + // Spring JMS + return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJmsMessaging"); } @Override @@ -49,7 +52,15 @@ public class JmsCompilerAutoConfiguration extends CompilerAutoConfiguration { public void applyImports(ImportCustomizer imports) throws CompilationFailedException { imports.addStarImports("javax.jms", "org.springframework.jms.core", "org.springframework.jms.listener", - "org.springframework.jms.listener.adapter"); + "org.springframework.jms.listener.adapter") + .addImports(EnableJmsMessaging.class.getCanonicalName()); } + @Target(ElementType.TYPE) + @Documented + @Retention(RetentionPolicy.RUNTIME) + public static @interface EnableJmsMessaging { + + } + } diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 055617e518..53f98617bd 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -111,6 +111,11 @@ activemq-core ${activemq.version} + + org.apache.activemq + activemq-pool + ${activemq.version} + org.apache.tomcat.embed tomcat-embed-core