From 7d5cc3da63010a86ae3ad212643d8111a2f370f4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 14 Dec 2015 16:24:43 +0000 Subject: [PATCH] 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 bedf2edf. Closes gh-4748 --- ...ctiveMQConnectionFactoryConfiguration.java | 41 ++++++++----------- ...iveMQXAConnectionFactoryConfiguration.java | 27 ++++++++---- .../ActiveMQAutoConfigurationTests.java | 8 +++- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java index 8d11ef28ea..4127760398 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java @@ -16,18 +16,16 @@ package org.springframework.boot.autoconfigure.jms.activemq; -import java.lang.reflect.Method; - import javax.jms.ConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.pool.PooledConnectionFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; /** * Configuration for ActiveMQ {@link ConnectionFactory}. @@ -43,31 +41,24 @@ import org.springframework.util.ReflectionUtils; class ActiveMQConnectionFactoryConfiguration { @Bean - public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties) { - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory( - properties).createConnectionFactory(ActiveMQConnectionFactory.class); - if (properties.isPooled()) { - PooledConnectionFactory pool = new PooledConnectionFactory(); - Method setConnectionFactory = findConnectionFactorySetter(); - Assert.state(setConnectionFactory != null, - "No supported " + "setConnectionFactory method was found"); - ReflectionUtils.invokeMethod(setConnectionFactory, pool, connectionFactory); - return pool; - } - return connectionFactory; + @ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "false", matchIfMissing = true) + public ActiveMQConnectionFactory jmsConnectionFactory(ActiveMQProperties properties) { + return new ActiveMQConnectionFactoryFactory(properties) + .createConnectionFactory(ActiveMQConnectionFactory.class); } - private Method findConnectionFactorySetter() { - Method setter = findConnectionFactorySetter(ConnectionFactory.class); - if (setter == null) { - setter = findConnectionFactorySetter(Object.class); + @ConditionalOnClass(PooledConnectionFactory.class) + static class PooledConnectionFactoryConfiguration { + + @Bean(destroyMethod = "stop") + @ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "true", matchIfMissing = false) + public PooledConnectionFactory pooledJmsConnectionFactory( + ActiveMQProperties properties) { + return new PooledConnectionFactory( + new ActiveMQConnectionFactoryFactory(properties) + .createConnectionFactory(ActiveMQConnectionFactory.class)); } - return setter; - } - private Method findConnectionFactorySetter(Class param) { - return ReflectionUtils.findMethod(PooledConnectionFactory.class, - "setConnectionFactory", param); } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java index 7d09cea018..7a67555fd1 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQXAConnectionFactoryConfiguration.java @@ -26,6 +26,7 @@ import org.apache.activemq.pool.PooledConnectionFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -53,15 +54,25 @@ class ActiveMQXAConnectionFactoryConfiguration { } @Bean - public ConnectionFactory nonXaJmsConnectionFactory(ActiveMQProperties properties) { - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory( - properties).createConnectionFactory(ActiveMQConnectionFactory.class); - if (properties.isPooled()) { - PooledConnectionFactory pool = new PooledConnectionFactory(); - pool.setConnectionFactory(connectionFactory); - return pool; + @ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "false", matchIfMissing = true) + public ActiveMQConnectionFactory nonXaJmsConnectionFactory( + ActiveMQProperties properties) { + return new ActiveMQConnectionFactoryFactory(properties) + .createConnectionFactory(ActiveMQConnectionFactory.class); + } + + @ConditionalOnClass(PooledConnectionFactory.class) + @ConditionalOnProperty(prefix = "spring.activemq", name = "pooled", havingValue = "true", matchIfMissing = false) + static class PooledConnectionFactoryConfiguration { + + @Bean(destroyMethod = "stop") + public PooledConnectionFactory pooledNonXaJmsConnectionFactory( + ActiveMQProperties properties) { + return new PooledConnectionFactory( + new ActiveMQConnectionFactoryFactory(properties) + .createConnectionFactory(ActiveMQConnectionFactory.class)); } - return connectionFactory; + } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java index d293694812..8a39424b9e 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.jms.activemq; import javax.jms.ConnectionFactory; +import javax.jms.JMSException; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.pool.PooledConnectionFactory; @@ -29,6 +30,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -62,11 +65,14 @@ public class ActiveMQAutoConfigurationTests { } @Test - public void pooledConnectionFactoryConfiguration() { + public void pooledConnectionFactoryConfiguration() throws JMSException { load(EmptyConfiguration.class, "spring.activemq.pooled:true"); ConnectionFactory connectionFactory = this.context .getBean(ConnectionFactory.class); assertThat(connectionFactory, instanceOf(PooledConnectionFactory.class)); + this.context.close(); + assertThat(((PooledConnectionFactory) connectionFactory).createConnection(), + is(nullValue())); } private void load(Class config, String... environment) {