Skip to content
This repository

Add JMS configuration properties #57

Closed
wants to merge 6 commits into from

2 participants

Greg Turnquist Dave Syer
Greg Turnquist
Collaborator

Added code for extra properties:

  • spring.jms.pubSubDomain - defaults to true (topic style)
  • spring.activemq.inMemory - default to true, returning vm://localhost from getBrokerURL. Switching to false flips brokerURL whatever is in spring.activemq.brokerURL.
  • spring.activemq.brokerURL - defaults to tcp://localhost:61616, the default installation URL for ActiveMQ standalone broker. This is overridden when spring.activemq.inMemory is true.
and others added some commits September 20, 2013
Greg Turnquist Add application.properties support for spring.jms and spring.activemq 18a6428
Greg Turnquist Add more tests to verify ActiveMQConnectionFactory pooling bea745c
Greg Turnquist Put static imports at the bottom a12fccc
Dave Syer Fixes #55: stop() connector to unbind socket
The `Tomcat.start()` has to happen to initialize the `ServletContext`
but we can immediately stop the connector and then restart it when
the context is finished refreshing. Seems to make curl fail quickly
if an app is slow to start.
0c466dc
Greg Turnquist
Collaborator

That last commit is because I did another git fetch && git merge upstream/master, and the git rebased my branch against master. It drove to do a git pull --rebase on this branch, and for some reason thought this was important. Hopefully it will merge cleanly.

Greg Turnquist
Collaborator

That is what happens when you commit before running ALL the tests.

Greg Turnquist
Collaborator

The last commit should make this feature integrate nicely, i.e. not collide with the up-and-coming RabbitTemplate autodetection pull request.

Dave Syer
Collaborator

Merged, thanks

Dave Syer dsyer closed this September 26, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 6 unique commits by 2 authors.

Sep 20, 2013
Greg Turnquist Add application.properties support for spring.jms and spring.activemq 18a6428
Greg Turnquist Add more tests to verify ActiveMQConnectionFactory pooling bea745c
Greg Turnquist Put static imports at the bottom a12fccc
Dave Syer Fixes #55: stop() connector to unbind socket
The `Tomcat.start()` has to happen to initialize the `ServletContext`
but we can immediately stop the connector and then restart it when
the context is finished refreshing. Seems to make curl fail quickly
if an app is slow to start.
0c466dc
Greg Turnquist Added activemq-pool to jms.groovy 62cc67d
Sep 25, 2013
Greg Turnquist Replace Groovy detection mechanism with @EnableJmsMessaging annotation b9d6871
This page is out of date. Refresh to see the latest.
5  spring-boot-autoconfigure/pom.xml
@@ -32,6 +32,11 @@
32 32
 			<optional>true</optional>
33 33
 		</dependency>
34 34
 		<dependency>
  35
+			<groupId>org.apache.activemq</groupId>
  36
+			<artifactId>activemq-pool</artifactId>
  37
+			<optional>true</optional>
  38
+		</dependency>
  39
+		<dependency>
35 40
 			<groupId>org.apache.tomcat.embed</groupId>
36 41
 			<artifactId>tomcat-embed-core</artifactId>
37 42
 			<optional>true</optional>
78  ...autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfiguration.java
@@ -19,10 +19,13 @@
19 19
 import javax.jms.ConnectionFactory;
20 20
 
21 21
 import org.apache.activemq.ActiveMQConnectionFactory;
  22
+import org.apache.activemq.pool.PooledConnectionFactory;
22 23
 import org.springframework.beans.factory.annotation.Autowired;
23 24
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
24 25
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25 26
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  27
+import org.springframework.boot.context.properties.ConfigurationProperties;
  28
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
26 29
 import org.springframework.context.annotation.Bean;
27 30
 import org.springframework.context.annotation.Configuration;
28 31
 import org.springframework.jms.core.JmsTemplate;
@@ -38,7 +41,11 @@
38 41
 
39 42
 	@Configuration
40 43
 	@ConditionalOnMissingBean(JmsTemplate.class)
  44
+	@EnableConfigurationProperties(JmsTemplateProperties.class)
41 45
 	protected static class JmsTemplateCreator {
  46
+		
  47
+		@Autowired
  48
+		private JmsTemplateProperties config;
42 49
 
43 50
 		@Autowired
44 51
 		private ConnectionFactory connectionFactory;
@@ -46,22 +53,87 @@
46 53
 		@Bean
47 54
 		public JmsTemplate jmsTemplate() {
48 55
 			JmsTemplate jmsTemplate = new JmsTemplate(this.connectionFactory);
49  
-			jmsTemplate.setPubSubDomain(true);
  56
+			jmsTemplate.setPubSubDomain(this.config.isPubSubDomain());
50 57
 			return jmsTemplate;
51 58
 		}
52 59
 
53 60
 	}
  61
+	
  62
+	@ConfigurationProperties(name = "spring.jms")
  63
+	public static class JmsTemplateProperties {
  64
+		
  65
+		private boolean pubSubDomain = true;
  66
+
  67
+		public boolean isPubSubDomain() {
  68
+			return pubSubDomain;
  69
+		}
  70
+
  71
+		public void setPubSubDomain(boolean pubSubDomain) {
  72
+			this.pubSubDomain = pubSubDomain;
  73
+		}
  74
+		
  75
+	}
54 76
 
55 77
 	@Configuration
56 78
 	@ConditionalOnClass(ActiveMQConnectionFactory.class)
57 79
 	@ConditionalOnMissingBean(ConnectionFactory.class)
  80
+	@EnableConfigurationProperties(ActiveMQConnectionFactoryProperties.class)
58 81
 	protected static class ActiveMQConnectionFactoryCreator {
59  
-
  82
+		
  83
+		@Autowired
  84
+		private ActiveMQConnectionFactoryProperties config;
  85
+		
60 86
 		@Bean
61 87
 		ConnectionFactory connectionFactory() {
62  
-			return new ActiveMQConnectionFactory("vm://localhost");
  88
+			if (this.config.isPooled()) {
  89
+				PooledConnectionFactory pool = new PooledConnectionFactory();
  90
+				pool.setConnectionFactory(new ActiveMQConnectionFactory(this.config.getBrokerURL()));
  91
+				return pool;
  92
+			} else {
  93
+				return new ActiveMQConnectionFactory(this.config.getBrokerURL());
  94
+			}
  95
+		}
  96
+
  97
+	}
  98
+	
  99
+	@ConfigurationProperties(name = "spring.activemq")
  100
+	public static class ActiveMQConnectionFactoryProperties {
  101
+		
  102
+		private String brokerURL = "tcp://localhost:61616";
  103
+		
  104
+		private boolean inMemory = true;
  105
+		
  106
+		private boolean pooled = false;
  107
+		
  108
+		// Will override brokerURL if inMemory is set to true
  109
+		public String getBrokerURL() {
  110
+			if (this.inMemory) {
  111
+				return "vm://localhost";
  112
+			} else {
  113
+				return this.brokerURL;
  114
+			}
  115
+		}
  116
+
  117
+		public void setBrokerURL(String brokerURL) {
  118
+			this.brokerURL = brokerURL;
  119
+		}
  120
+
  121
+		public boolean isInMemory() {
  122
+			return inMemory;
63 123
 		}
64 124
 
  125
+		public void setInMemory(boolean inMemory) {
  126
+			this.inMemory = inMemory;
  127
+		}
  128
+
  129
+		public boolean isPooled() {
  130
+			return pooled;
  131
+		}
  132
+
  133
+		public void setPooled(boolean pooled) {
  134
+			this.pooled = pooled;
  135
+		}
  136
+		
65 137
 	}
66 138
 
67 139
 }
107  ...onfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsTemplateAutoConfigurationTests.java
@@ -19,9 +19,11 @@
19 19
 import javax.jms.ConnectionFactory;
20 20
 
21 21
 import org.apache.activemq.ActiveMQConnectionFactory;
  22
+import org.apache.activemq.pool.PooledConnectionFactory;
22 23
 import org.junit.Test;
23 24
 import org.springframework.beans.BeansException;
24 25
 import org.springframework.beans.factory.config.BeanPostProcessor;
  26
+import org.springframework.boot.TestUtils;
25 27
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
26 28
 import org.springframework.context.annotation.Bean;
27 29
 import org.springframework.context.annotation.Configuration;
@@ -53,6 +55,7 @@ public void testDefaultJmsTemplate() {
53 55
 		assertNotNull(jmsTemplate);
54 56
 		assertNotNull(connectionFactory);
55 57
 		assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory);
  58
+		assertEquals(((ActiveMQConnectionFactory)jmsTemplate.getConnectionFactory()).getBrokerURL(), "vm://localhost");
56 59
 	}
57 60
 
58 61
 	@Test
@@ -106,6 +109,110 @@ public void testJmsTemplatePostProcessedSoThatPubSubIsFalse() {
106 109
 		JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class);
107 110
 		assertFalse(jmsTemplate.isPubSubDomain());
108 111
 	}
  112
+	
  113
+	@Test
  114
+	public void testJmsTemplateOverridden() {
  115
+		this.context = new AnnotationConfigApplicationContext();
  116
+		this.context
  117
+				.register(TestConfiguration.class, JmsTemplateAutoConfiguration.class);
  118
+		TestUtils.addEnviroment(this.context, "spring.jms.pubSubDomain:false");
  119
+		this.context.refresh();
  120
+		JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class);
  121
+		ActiveMQConnectionFactory connectionFactory = this.context
  122
+				.getBean(ActiveMQConnectionFactory.class);
  123
+		assertNotNull(jmsTemplate);
  124
+		assertFalse(jmsTemplate.isPubSubDomain());
  125
+		assertNotNull(connectionFactory);
  126
+		assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory);
  127
+	}
  128
+
  129
+	@Test
  130
+	public void testActiveMQOverriddenStandalone() {
  131
+		this.context = new AnnotationConfigApplicationContext();
  132
+		this.context
  133
+				.register(TestConfiguration.class, JmsTemplateAutoConfiguration.class);
  134
+		TestUtils.addEnviroment(this.context, "spring.activemq.inMemory:false");
  135
+		this.context.refresh();
  136
+		JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class);
  137
+		ActiveMQConnectionFactory connectionFactory = this.context
  138
+				.getBean(ActiveMQConnectionFactory.class);
  139
+		assertNotNull(jmsTemplate);
  140
+		assertNotNull(connectionFactory);
  141
+		assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory);
  142
+		assertEquals(((ActiveMQConnectionFactory)jmsTemplate.getConnectionFactory()).getBrokerURL(), 
  143
+				"tcp://localhost:61616");
  144
+	}
  145
+
  146
+	@Test
  147
+	public void testActiveMQOverriddenRemoteHost() {
  148
+		this.context = new AnnotationConfigApplicationContext();
  149
+		this.context
  150
+				.register(TestConfiguration.class, JmsTemplateAutoConfiguration.class);
  151
+		TestUtils.addEnviroment(this.context, "spring.activemq.inMemory:false",
  152
+				"spring.activemq.brokerURL:tcp://remote-host:10000");
  153
+		this.context.refresh();
  154
+		JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class);
  155
+		ActiveMQConnectionFactory connectionFactory = this.context
  156
+				.getBean(ActiveMQConnectionFactory.class);
  157
+		assertNotNull(jmsTemplate);
  158
+		assertNotNull(connectionFactory);
  159
+		assertEquals(jmsTemplate.getConnectionFactory(), connectionFactory);
  160
+		assertEquals(((ActiveMQConnectionFactory)jmsTemplate.getConnectionFactory()).getBrokerURL(), 
  161
+				"tcp://remote-host:10000");
  162
+	}
  163
+
  164
+	@Test
  165
+	public void testActiveMQOverriddenPool() {
  166
+		this.context = new AnnotationConfigApplicationContext();
  167
+		this.context
  168
+				.register(TestConfiguration.class, JmsTemplateAutoConfiguration.class);
  169
+		TestUtils.addEnviroment(this.context, "spring.activemq.pooled:true");
  170
+		this.context.refresh();
  171
+		JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class);
  172
+		PooledConnectionFactory pool = this.context
  173
+				.getBean(PooledConnectionFactory.class);
  174
+		assertNotNull(jmsTemplate);
  175
+		assertNotNull(pool);
  176
+		assertEquals(jmsTemplate.getConnectionFactory(), pool);
  177
+		ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool.getConnectionFactory();
  178
+		assertEquals("vm://localhost", factory.getBrokerURL());
  179
+	}
  180
+
  181
+	@Test
  182
+	public void testActiveMQOverriddenPoolAndStandalone() {
  183
+		this.context = new AnnotationConfigApplicationContext();
  184
+		this.context
  185
+				.register(TestConfiguration.class, JmsTemplateAutoConfiguration.class);
  186
+		TestUtils.addEnviroment(this.context, "spring.activemq.pooled:true",
  187
+				"spring.activemq.inMemory:false");
  188
+		this.context.refresh();
  189
+		JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class);
  190
+		PooledConnectionFactory pool = this.context
  191
+				.getBean(PooledConnectionFactory.class);
  192
+		assertNotNull(jmsTemplate);
  193
+		assertNotNull(pool);
  194
+		assertEquals(jmsTemplate.getConnectionFactory(), pool);
  195
+		ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool.getConnectionFactory();
  196
+		assertEquals("tcp://localhost:61616", factory.getBrokerURL());
  197
+	}
  198
+
  199
+	@Test
  200
+	public void testActiveMQOverriddenPoolAndRemoteServer() {
  201
+		this.context = new AnnotationConfigApplicationContext();
  202
+		this.context
  203
+				.register(TestConfiguration.class, JmsTemplateAutoConfiguration.class);
  204
+		TestUtils.addEnviroment(this.context, "spring.activemq.pooled:true",
  205
+				"spring.activemq.inMemory:false", "spring.activemq.brokerURL:tcp://remote-host:10000");
  206
+		this.context.refresh();
  207
+		JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class);
  208
+		PooledConnectionFactory pool = this.context
  209
+				.getBean(PooledConnectionFactory.class);
  210
+		assertNotNull(jmsTemplate);
  211
+		assertNotNull(pool);
  212
+		assertEquals(jmsTemplate.getConnectionFactory(), pool);
  213
+		ActiveMQConnectionFactory factory = (ActiveMQConnectionFactory) pool.getConnectionFactory();
  214
+		assertEquals("tcp://remote-host:10000", factory.getBrokerURL());
  215
+	}
109 216
 
110 217
 	@Configuration
111 218
 	protected static class TestConfiguration {
5  spring-boot-cli/samples/jms.groovy
... ...
@@ -1,11 +1,13 @@
1 1
 package org.test
2 2
 
3 3
 @Grab("org.apache.activemq:activemq-all:5.4.0")
  4
+@Grab("org.apache.activemq:activemq-pool:5.4.0")
4 5
 
5 6
 import java.util.concurrent.CountDownLatch
6 7
 
7  
-@Configuration
8 8
 @Log
  9
+@Configuration
  10
+@EnableJmsMessaging
9 11
 class JmsExample implements CommandLineRunner {
10 12
 
11 13
 	private CountDownLatch latch = new CountDownLatch(1)
@@ -30,7 +32,6 @@ class JmsExample implements CommandLineRunner {
30 32
 			session.createObjectMessage("Greetings from Spring Boot via ActiveMQ")
31 33
 		} as MessageCreator
32 34
 		log.info "Sending JMS message..."
33  
-		jmsTemplate.pubSubDomain = true
34 35
 		jmsTemplate.send("spring-boot", messageCreator)
35 36
 		latch.await()
36 37
 	}
17  ...-cli/src/main/java/org/springframework/boot/cli/compiler/autoconfigure/JmsCompilerAutoConfiguration.java
@@ -23,6 +23,8 @@
23 23
 import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
24 24
 import org.springframework.boot.cli.compiler.DependencyCustomizer;
25 25
 
  26
+import java.lang.annotation.*;
  27
+
26 28
 /**
27 29
  * {@link CompilerAutoConfiguration} for Spring JMS.
28 30
  * 
@@ -32,8 +34,9 @@
32 34
 
33 35
 	@Override
34 36
 	public boolean matches(ClassNode classNode) {
35  
-		return AstUtils.hasAtLeastOneFieldOrMethod(classNode, "JmsTemplate",
36  
-				"DefaultMessageListenerContainer", "SimpleMessageListenerContainer");
  37
+        // Slightly weird detection algorithm because there is no @Enable annotation for
  38
+        // Spring JMS
  39
+        return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJmsMessaging");
37 40
 	}
38 41
 
39 42
 	@Override
@@ -49,7 +52,15 @@ public void applyDependencies(DependencyCustomizer dependencies)
49 52
 	public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
50 53
 		imports.addStarImports("javax.jms", "org.springframework.jms.core",
51 54
 				"org.springframework.jms.listener",
52  
-				"org.springframework.jms.listener.adapter");
  55
+				"org.springframework.jms.listener.adapter")
  56
+                .addImports(EnableJmsMessaging.class.getCanonicalName());
53 57
 	}
54 58
 
  59
+    @Target(ElementType.TYPE)
  60
+    @Documented
  61
+    @Retention(RetentionPolicy.RUNTIME)
  62
+    public static @interface EnableJmsMessaging {
  63
+
  64
+    }
  65
+
55 66
 }
5  spring-boot-dependencies/pom.xml
@@ -112,6 +112,11 @@
112 112
 				<version>${activemq.version}</version>
113 113
 			</dependency>
114 114
 			<dependency>
  115
+				<groupId>org.apache.activemq</groupId>
  116
+				<artifactId>activemq-pool</artifactId>
  117
+				<version>${activemq.version}</version>
  118
+			</dependency>
  119
+			<dependency>
115 120
 				<groupId>org.apache.tomcat.embed</groupId>
116 121
 				<artifactId>tomcat-embed-core</artifactId>
117 122
 				<version>${tomcat.version}</version>
2  ...spring-boot-sample-tomcat/src/main/java/org/springframework/boot/sample/tomcat/web/SampleController.java
@@ -27,7 +27,7 @@
27 27
 
28 28
 	@Autowired
29 29
 	private HelloWorldService helloWorldService;
30  
-
  30
+	
31 31
 	@RequestMapping("/")
32 32
 	@ResponseBody
33 33
 	public String helloWorld() {
12  ...-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainer.java
@@ -55,6 +55,16 @@ public TomcatEmbeddedServletContainer(Tomcat tomcat) {
55 55
 	private synchronized void initialize() throws EmbeddedServletContainerException {
56 56
 		try {
57 57
 			this.tomcat.start();
  58
+			try {
  59
+				// Allow the server to start so the ServletContext is available, but stop
  60
+				// the connector to prevent requests from being handled before the Spring
  61
+				// context is ready:
  62
+				Connector connector = this.tomcat.getConnector();
  63
+				connector.getProtocolHandler().stop();
  64
+			}
  65
+			catch (Exception e) {
  66
+				this.logger.error("Cannot pause connector: ", e);
  67
+			}
58 68
 			// Unlike Jetty, all Tomcat threads are daemon threads. We create a
59 69
 			// blocking non-daemon to stop immediate shutdown
60 70
 			Thread awaitThread = new Thread("container-" + (containerCounter++)) {
@@ -77,7 +87,7 @@ public void start() throws EmbeddedServletContainerException {
77 87
 		Connector connector = this.tomcat.getConnector();
78 88
 		if (connector != null) {
79 89
 			try {
80  
-				connector.getProtocolHandler().resume();
  90
+				connector.getProtocolHandler().start();
81 91
 			}
82 92
 			catch (Exception e) {
83 93
 				this.logger.error("Cannot start connector: ", e);
20  ...rc/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java
@@ -131,15 +131,6 @@ public EmbeddedServletContainer getEmbeddedServletContainer(
131 131
 		customizeConnector(connector);
132 132
 		tomcat.getService().addConnector(connector);
133 133
 		tomcat.setConnector(connector);
134  
-		try {
135  
-			// Allow the server to start so the ServletContext is available, but stop the
136  
-			// connector to prevent requests from being handled before the Spring context
137  
-			// is ready:
138  
-			connector.getProtocolHandler().pause();
139  
-		}
140  
-		catch (Exception e) {
141  
-			this.logger.error("Cannot pause connector: ", e);
142  
-		}
143 134
 		tomcat.getHost().setAutoDeploy(false);
144 135
 		tomcat.getEngine().setBackgroundProcessorDelay(-1);
145 136
 
@@ -203,10 +194,15 @@ private void addJspServlet(Context context) {
203 194
 	// Needs to be protected so it can be used by subclasses
204 195
 	protected void customizeConnector(Connector connector) {
205 196
 		connector.setPort(getPort());
206  
-		if (connector.getProtocolHandler() instanceof AbstractProtocol
207  
-				&& getAddress() != null) {
208  
-			((AbstractProtocol) connector.getProtocolHandler()).setAddress(getAddress());
  197
+		if (connector.getProtocolHandler() instanceof AbstractProtocol) {
  198
+			if (getAddress() != null) {
  199
+				((AbstractProtocol) connector.getProtocolHandler())
  200
+						.setAddress(getAddress());
  201
+			}
209 202
 		}
  203
+		// If ApplicationContext is slow to start we want Tomcat not to bind to the socket
  204
+		// prematurely...
  205
+		connector.setProperty("bindOnInit", "false");
210 206
 	}
211 207
 
212 208
 	/**
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.