Skip to content

Commit 7d5cc3d

Browse files
committed
Stop ActiveMQ pooled connection factory when context is closed
Previously, ActiveMQ's pooled connection factory was not closed as part of the application context being closed. This would leave non-daemon threads running which could cause shutdown to hang unless the JVM itself was shutting down (in which case a shutdown hook would stop the pool). This commit configures each pooled connection factory bean with a custom destroy method so that the pool is stopped as part of the application context being closed. To allow the destroy method to only be declared when the connection factory is pooled, the bean method has been split into two; one for pooled and one for non-pooled. This is a partial backport of the changes made in bedf2ed. Closes gh-4748
1 parent 474ffa7 commit 7d5cc3d

File tree

3 files changed

+42
-34
lines changed

3 files changed

+42
-34
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,16 @@
1616

1717
package org.springframework.boot.autoconfigure.jms.activemq;
1818

19-
import java.lang.reflect.Method;
20-
2119
import javax.jms.ConnectionFactory;
2220

2321
import org.apache.activemq.ActiveMQConnectionFactory;
2422
import org.apache.activemq.pool.PooledConnectionFactory;
2523

24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2625
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
26+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2727
import org.springframework.context.annotation.Bean;
2828
import org.springframework.context.annotation.Configuration;
29-
import org.springframework.util.Assert;
30-
import org.springframework.util.ReflectionUtils;
3129

3230
/**
3331
* Configuration for ActiveMQ {@link ConnectionFactory}.
@@ -43,31 +41,24 @@
4341
class ActiveMQConnectionFactoryConfiguration {
4442

4543
@Bean
46-
public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties) {
47-
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(
48-
properties).createConnectionFactory(ActiveMQConnectionFactory.class);
49-
if (properties.isPooled()) {
50-
PooledConnectionFactory pool = new PooledConnectionFactory();
51-
Method setConnectionFactory = findConnectionFactorySetter();
52-
Assert.state(setConnectionFactory != null,
53-
"No supported " + "setConnectionFactory method was found");
54-
ReflectionUtils.invokeMethod(setConnectionFactory, pool, connectionFactory);
55-
return pool;
56-
}
57-
return connectionFactory;
44+
@ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "false", matchIfMissing = true)
45+
public ActiveMQConnectionFactory jmsConnectionFactory(ActiveMQProperties properties) {
46+
return new ActiveMQConnectionFactoryFactory(properties)
47+
.createConnectionFactory(ActiveMQConnectionFactory.class);
5848
}
5949

60-
private Method findConnectionFactorySetter() {
61-
Method setter = findConnectionFactorySetter(ConnectionFactory.class);
62-
if (setter == null) {
63-
setter = findConnectionFactorySetter(Object.class);
50+
@ConditionalOnClass(PooledConnectionFactory.class)
51+
static class PooledConnectionFactoryConfiguration {
52+
53+
@Bean(destroyMethod = "stop")
54+
@ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "true", matchIfMissing = false)
55+
public PooledConnectionFactory pooledJmsConnectionFactory(
56+
ActiveMQProperties properties) {
57+
return new PooledConnectionFactory(
58+
new ActiveMQConnectionFactoryFactory(properties)
59+
.createConnectionFactory(ActiveMQConnectionFactory.class));
6460
}
65-
return setter;
66-
}
6761

68-
private Method findConnectionFactorySetter(Class<?> param) {
69-
return ReflectionUtils.findMethod(PooledConnectionFactory.class,
70-
"setConnectionFactory", param);
7162
}
7263

7364
}

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2727
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2828
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2930
import org.springframework.boot.jta.XAConnectionFactoryWrapper;
3031
import org.springframework.context.annotation.Bean;
3132
import org.springframework.context.annotation.Configuration;
@@ -53,15 +54,25 @@ public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties,
5354
}
5455

5556
@Bean
56-
public ConnectionFactory nonXaJmsConnectionFactory(ActiveMQProperties properties) {
57-
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(
58-
properties).createConnectionFactory(ActiveMQConnectionFactory.class);
59-
if (properties.isPooled()) {
60-
PooledConnectionFactory pool = new PooledConnectionFactory();
61-
pool.setConnectionFactory(connectionFactory);
62-
return pool;
57+
@ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "false", matchIfMissing = true)
58+
public ActiveMQConnectionFactory nonXaJmsConnectionFactory(
59+
ActiveMQProperties properties) {
60+
return new ActiveMQConnectionFactoryFactory(properties)
61+
.createConnectionFactory(ActiveMQConnectionFactory.class);
62+
}
63+
64+
@ConditionalOnClass(PooledConnectionFactory.class)
65+
@ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "true", matchIfMissing = false)
66+
static class PooledConnectionFactoryConfiguration {
67+
68+
@Bean(destroyMethod = "stop")
69+
public PooledConnectionFactory pooledNonXaJmsConnectionFactory(
70+
ActiveMQProperties properties) {
71+
return new PooledConnectionFactory(
72+
new ActiveMQConnectionFactoryFactory(properties)
73+
.createConnectionFactory(ActiveMQConnectionFactory.class));
6374
}
64-
return connectionFactory;
75+
6576
}
6677

6778
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.autoconfigure.jms.activemq;
1818

1919
import javax.jms.ConnectionFactory;
20+
import javax.jms.JMSException;
2021

2122
import org.apache.activemq.ActiveMQConnectionFactory;
2223
import org.apache.activemq.pool.PooledConnectionFactory;
@@ -29,6 +30,8 @@
2930
import org.springframework.context.annotation.Configuration;
3031

3132
import static org.hamcrest.Matchers.instanceOf;
33+
import static org.hamcrest.Matchers.is;
34+
import static org.hamcrest.Matchers.nullValue;
3235
import static org.junit.Assert.assertEquals;
3336
import static org.junit.Assert.assertThat;
3437
import static org.junit.Assert.assertTrue;
@@ -62,11 +65,14 @@ public void configurationBacksOffWhenCustomConnectionFactoryExists() {
6265
}
6366

6467
@Test
65-
public void pooledConnectionFactoryConfiguration() {
68+
public void pooledConnectionFactoryConfiguration() throws JMSException {
6669
load(EmptyConfiguration.class, "spring.activemq.pooled:true");
6770
ConnectionFactory connectionFactory = this.context
6871
.getBean(ConnectionFactory.class);
6972
assertThat(connectionFactory, instanceOf(PooledConnectionFactory.class));
73+
this.context.close();
74+
assertThat(((PooledConnectionFactory) connectionFactory).createConnection(),
75+
is(nullValue()));
7076
}
7177

7278
private void load(Class<?> config, String... environment) {

0 commit comments

Comments
 (0)