diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java index be584dc5750..eff79905d53 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -41,6 +41,8 @@ public class JmsProperties { */ private String jndiName; + private final Cache cache = new Cache(); + private final Listener listener = new Listener(); private final Template template = new Template(); @@ -61,6 +63,10 @@ public class JmsProperties { this.jndiName = jndiName; } + public Cache getCache() { + return this.cache; + } + public Listener getListener() { return this.listener; } @@ -69,6 +75,62 @@ public class JmsProperties { return this.template; } + public static class Cache { + + /** + * Whether to cache sessions. + */ + private boolean enabled = true; + + /** + * Whether to cache message consumers. + */ + private boolean consumers = false; + + /** + * Whether to cache message producers. + */ + private boolean producers = true; + + /** + * Size of the session cache (per JMS Session type). + */ + private int sessionCacheSize = 1; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isConsumers() { + return this.consumers; + } + + public void setConsumers(boolean consumers) { + this.consumers = consumers; + } + + public boolean isProducers() { + return this.producers; + } + + public void setProducers(boolean producers) { + this.producers = producers; + } + + public int getSessionCacheSize() { + return this.sessionCacheSize; + } + + public void setSessionCacheSize(int sessionCacheSize) { + this.sessionCacheSize = sessionCacheSize; + } + + } + public static class Listener { /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfiguration.java index b5462943cb0..ed1140c83c7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; +import org.springframework.boot.autoconfigure.jms.JmsProperties; import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -45,7 +46,7 @@ import org.springframework.context.annotation.Import; @AutoConfigureAfter({ JndiConnectionFactoryAutoConfiguration.class }) @ConditionalOnClass({ ConnectionFactory.class, ActiveMQConnectionFactory.class }) @ConditionalOnMissingBean(ConnectionFactory.class) -@EnableConfigurationProperties(ActiveMQProperties.class) +@EnableConfigurationProperties({ ActiveMQProperties.class, JmsProperties.class }) @Import({ ActiveMQXAConnectionFactoryConfiguration.class, ActiveMQConnectionFactoryConfiguration.class }) public class ActiveMQAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java index e4a8a5a0467..df32566cf48 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQConnectionFactoryConfiguration.java @@ -28,8 +28,10 @@ import org.springframework.beans.factory.ObjectProvider; 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.autoconfigure.jms.JmsProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.jms.connection.CachingConnectionFactory; /** * Configuration for ActiveMQ {@link ConnectionFactory}. @@ -45,13 +47,49 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnMissingBean(ConnectionFactory.class) class ActiveMQConnectionFactoryConfiguration { - @Bean + @Configuration @ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "false", matchIfMissing = true) - public ActiveMQConnectionFactory jmsConnectionFactory(ActiveMQProperties properties, - ObjectProvider> factoryCustomizers) { - return new ActiveMQConnectionFactoryFactory(properties, - factoryCustomizers.getIfAvailable()) - .createConnectionFactory(ActiveMQConnectionFactory.class); + static class SimpleConnectionFactoryConfiguration { + + private final JmsProperties jmsProperties; + + private final ActiveMQProperties properties; + + private final List connectionFactoryCustomizers; + + SimpleConnectionFactoryConfiguration(JmsProperties jmsProperties, + ActiveMQProperties properties, + ObjectProvider> connectionFactoryCustomizers) { + this.jmsProperties = jmsProperties; + this.properties = properties; + this.connectionFactoryCustomizers = connectionFactoryCustomizers + .getIfAvailable(); + } + + @Bean + @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "true", matchIfMissing = true) + public CachingConnectionFactory cachingJmsConnectionFactory() { + JmsProperties.Cache cacheProperties = this.jmsProperties.getCache(); + CachingConnectionFactory connectionFactory = new CachingConnectionFactory( + createConnectionFactory()); + connectionFactory.setCacheConsumers(cacheProperties.isConsumers()); + connectionFactory.setCacheProducers(cacheProperties.isProducers()); + connectionFactory.setSessionCacheSize(cacheProperties.getSessionCacheSize()); + return connectionFactory; + } + + @Bean + @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "false") + public ActiveMQConnectionFactory jmsConnectionFactory() { + return createConnectionFactory(); + } + + private ActiveMQConnectionFactory createConnectionFactory() { + return new ActiveMQConnectionFactoryFactory(this.properties, + this.connectionFactoryCustomizers) + .createConnectionFactory(ActiveMQConnectionFactory.class); + } + } @Configuration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfiguration.java index bff0d95c627..3f312f8e555 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; +import org.springframework.boot.autoconfigure.jms.JmsProperties; import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -47,7 +48,7 @@ import org.springframework.context.annotation.Import; @AutoConfigureAfter({ JndiConnectionFactoryAutoConfiguration.class }) @ConditionalOnClass({ ConnectionFactory.class, ActiveMQConnectionFactory.class }) @ConditionalOnMissingBean(ConnectionFactory.class) -@EnableConfigurationProperties(ArtemisProperties.class) +@EnableConfigurationProperties({ ArtemisProperties.class, JmsProperties.class }) @Import({ ArtemisEmbeddedServerConfiguration.class, ArtemisXAConnectionFactoryConfiguration.class, ArtemisConnectionFactoryConfiguration.class }) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisConnectionFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisConnectionFactoryConfiguration.java index 51b9947f37c..97bf6ea0079 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisConnectionFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisConnectionFactoryConfiguration.java @@ -26,9 +26,11 @@ import org.springframework.beans.factory.ListableBeanFactory; 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.autoconfigure.jms.JmsProperties; import org.springframework.boot.autoconfigure.jms.activemq.PooledConnectionFactoryFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.jms.connection.CachingConnectionFactory; /** * Configuration for Artemis {@link ConnectionFactory}. @@ -41,12 +43,46 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnMissingBean(ConnectionFactory.class) class ArtemisConnectionFactoryConfiguration { - @Bean + @Configuration @ConditionalOnProperty(prefix = "spring.artemis.pool", name = "enabled", havingValue = "false", matchIfMissing = true) - public ActiveMQConnectionFactory jmsConnectionFactory(ListableBeanFactory beanFactory, - ArtemisProperties properties) { - return new ArtemisConnectionFactoryFactory(beanFactory, properties) - .createConnectionFactory(ActiveMQConnectionFactory.class); + static class SimpleConnectionFactoryConfiguration { + + private final JmsProperties jmsProperties; + + private final ArtemisProperties properties; + + private final ListableBeanFactory beanFactory; + + SimpleConnectionFactoryConfiguration(JmsProperties jmsProperties, + ArtemisProperties properties, ListableBeanFactory beanFactory) { + this.jmsProperties = jmsProperties; + this.properties = properties; + this.beanFactory = beanFactory; + } + + @Bean + @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "true", matchIfMissing = true) + public CachingConnectionFactory cachingJmsConnectionFactory() { + JmsProperties.Cache cacheProperties = this.jmsProperties.getCache(); + CachingConnectionFactory connectionFactory = new CachingConnectionFactory( + createConnectionFactory()); + connectionFactory.setCacheConsumers(cacheProperties.isConsumers()); + connectionFactory.setCacheProducers(cacheProperties.isProducers()); + connectionFactory.setSessionCacheSize(cacheProperties.getSessionCacheSize()); + return connectionFactory; + } + + @Bean + @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "false") + public ActiveMQConnectionFactory jmsConnectionFactory() { + return createConnectionFactory(); + } + + private ActiveMQConnectionFactory createConnectionFactory() { + return new ArtemisConnectionFactoryFactory(this.beanFactory, this.properties) + .createConnectionFactory(ActiveMQConnectionFactory.class); + } + } @Configuration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java index 849a9e7c649..552afb6b9dc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java @@ -40,6 +40,7 @@ import org.springframework.jms.config.JmsListenerConfigUtils; import org.springframework.jms.config.JmsListenerContainerFactory; import org.springframework.jms.config.JmsListenerEndpoint; import org.springframework.jms.config.SimpleJmsListenerContainerFactory; +import org.springframework.jms.connection.CachingConnectionFactory; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.listener.DefaultMessageListenerContainer; @@ -74,15 +75,17 @@ public class JmsAutoConfigurationTests { } private void testDefaultJmsConfiguration(AssertableApplicationContext loaded) { - ActiveMQConnectionFactory factory = loaded - .getBean(ActiveMQConnectionFactory.class); + assertThat(loaded).hasSingleBean(ConnectionFactory.class); + assertThat(loaded).hasSingleBean(CachingConnectionFactory.class); + CachingConnectionFactory factory = loaded.getBean(CachingConnectionFactory.class); + assertThat(factory.getTargetConnectionFactory()) + .isInstanceOf(ActiveMQConnectionFactory.class); JmsTemplate jmsTemplate = loaded.getBean(JmsTemplate.class); JmsMessagingTemplate messagingTemplate = loaded .getBean(JmsMessagingTemplate.class); assertThat(factory).isEqualTo(jmsTemplate.getConnectionFactory()); assertThat(messagingTemplate.getJmsTemplate()).isEqualTo(jmsTemplate); - assertThat(((ActiveMQConnectionFactory) jmsTemplate.getConnectionFactory()) - .getBrokerURL()).isEqualTo(ACTIVEMQ_EMBEDDED_URL); + assertThat(getBrokerUrl(factory)).isEqualTo(ACTIVEMQ_EMBEDDED_URL); assertThat(loaded.containsBean("jmsListenerContainerFactory")).isTrue(); } @@ -313,9 +316,10 @@ public class JmsAutoConfigurationTests { public void testPubSubDomainOverride() { this.contextRunner.withUserConfiguration(TestConfiguration.class) .withPropertyValues("spring.jms.pubSubDomain:false").run((context) -> { + assertThat(context).hasSingleBean(JmsTemplate.class); + assertThat(context).hasSingleBean(ConnectionFactory.class); JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); + ConnectionFactory factory = context.getBean(ConnectionFactory.class); assertThat(jmsTemplate).isNotNull(); assertThat(jmsTemplate.isPubSubDomain()).isFalse(); assertThat(factory).isNotNull() @@ -327,15 +331,13 @@ public class JmsAutoConfigurationTests { public void testActiveMQOverriddenStandalone() { this.contextRunner.withUserConfiguration(TestConfiguration.class) .withPropertyValues("spring.activemq.inMemory:false").run((context) -> { + assertThat(context).hasSingleBean(JmsTemplate.class); + assertThat(context).hasSingleBean(CachingConnectionFactory.class); JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertThat(jmsTemplate).isNotNull(); - assertThat(factory).isNotNull() - .isEqualTo(jmsTemplate.getConnectionFactory()); - assertThat(((ActiveMQConnectionFactory) jmsTemplate - .getConnectionFactory()).getBrokerURL()) - .isEqualTo(ACTIVEMQ_NETWORK_URL); + ConnectionFactory factory = context.getBean(ConnectionFactory.class); + assertThat(factory).isEqualTo(jmsTemplate.getConnectionFactory()); + assertThat(getBrokerUrl((CachingConnectionFactory) factory)) + .isEqualTo(ACTIVEMQ_NETWORK_URL); }); } @@ -344,18 +346,23 @@ public class JmsAutoConfigurationTests { this.contextRunner.withUserConfiguration(TestConfiguration.class) .withPropertyValues("spring.activemq.brokerUrl:tcp://remote-host:10000") .run((context) -> { + assertThat(context).hasSingleBean(JmsTemplate.class); + assertThat(context).hasSingleBean(CachingConnectionFactory.class); JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertThat(jmsTemplate).isNotNull(); - assertThat(factory).isNotNull(); + ConnectionFactory factory = context.getBean(ConnectionFactory.class); assertThat(factory).isEqualTo(jmsTemplate.getConnectionFactory()); - assertThat(((ActiveMQConnectionFactory) jmsTemplate - .getConnectionFactory()).getBrokerURL()) - .isEqualTo("tcp://remote-host:10000"); + assertThat(getBrokerUrl((CachingConnectionFactory) factory)) + .isEqualTo("tcp://remote-host:10000"); }); } + private String getBrokerUrl(CachingConnectionFactory connectionFactory) { + assertThat(connectionFactory.getTargetConnectionFactory()) + .isInstanceOf(ActiveMQConnectionFactory.class); + return ((ActiveMQConnectionFactory) connectionFactory + .getTargetConnectionFactory()).getBrokerURL(); + } + @Test public void testActiveMQOverriddenPool() { this.contextRunner.withUserConfiguration(TestConfiguration.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java index a1337dc94d1..c65009f4082 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java @@ -27,6 +27,8 @@ import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.jms.connection.CachingConnectionFactory; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -50,10 +52,13 @@ public class ActiveMQAutoConfigurationTests { public void brokerIsEmbeddedByDefault() { this.contextRunner.withUserConfiguration(EmptyConfiguration.class) .run((context) -> { - assertThat(context).getBean(ConnectionFactory.class) + assertThat(context).hasSingleBean(CachingConnectionFactory.class); + CachingConnectionFactory cachingConnectionFactory = context + .getBean(CachingConnectionFactory.class); + assertThat(cachingConnectionFactory.getTargetConnectionFactory()) .isInstanceOf(ActiveMQConnectionFactory.class); - assertThat(context.getBean(ActiveMQConnectionFactory.class) - .getBrokerURL()) + assertThat(((ActiveMQConnectionFactory) cachingConnectionFactory + .getTargetConnectionFactory()).getBrokerURL()) .isEqualTo("vm://localhost?broker.persistent=false"); }); } @@ -68,10 +73,46 @@ public class ActiveMQAutoConfigurationTests { } @Test - public void defaultConnectionFactoryIsApplied() { + public void connectionFactoryIsCachedByDefault() { this.contextRunner.withUserConfiguration(EmptyConfiguration.class) - .withPropertyValues("spring.activemq.pool.enabled=false") .run((context) -> { + assertThat(context).hasSingleBean(ConnectionFactory.class); + assertThat(context).hasSingleBean(CachingConnectionFactory.class); + CachingConnectionFactory connectionFactory = context + .getBean(CachingConnectionFactory.class); + assertThat(connectionFactory.getTargetConnectionFactory()) + .isInstanceOf(ActiveMQConnectionFactory.class); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheConsumers")).isEqualTo(false); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheProducers")).isEqualTo(true); + assertThat(connectionFactory.getSessionCacheSize()).isEqualTo(1); + }); + } + + @Test + public void connectionFactoryCachingCanBeCustomized() { + this.contextRunner.withUserConfiguration(EmptyConfiguration.class) + .withPropertyValues("spring.jms.cache.consumers=true", + "spring.jms.cache.producers=false", + "spring.jms.cache.session-cache-size=10") + .run((context) -> { + assertThat(context).hasSingleBean(ConnectionFactory.class); + assertThat(context).hasSingleBean(CachingConnectionFactory.class); + CachingConnectionFactory connectionFactory = context + .getBean(CachingConnectionFactory.class); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheConsumers")).isEqualTo(true); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheProducers")).isEqualTo(false); + assertThat(connectionFactory.getSessionCacheSize()).isEqualTo(10); + }); + } + + @Test + public void connectionFactoryCachingCanBeDisabled() { + this.contextRunner.withUserConfiguration(EmptyConfiguration.class) + .withPropertyValues("spring.jms.cache.enabled=false").run((context) -> { assertThat(context.getBeansOfType(ActiveMQConnectionFactory.class)) .hasSize(1); ActiveMQConnectionFactory connectionFactory = context @@ -99,7 +140,7 @@ public class ActiveMQAutoConfigurationTests { @Test public void customConnectionFactoryIsApplied() { this.contextRunner.withUserConfiguration(EmptyConfiguration.class) - .withPropertyValues("spring.activemq.pool.enabled=false", + .withPropertyValues("spring.jms.cache.enabled=false", "spring.activemq.brokerUrl=vm://localhost?useJmx=false&broker.persistent=false", "spring.activemq.user=foo", "spring.activemq.password=bar", "spring.activemq.closeTimeout=500", diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfigurationTests.java index 25b015aee44..df581ab612a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisAutoConfigurationTests.java @@ -48,10 +48,12 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.jms.connection.CachingConnectionFactory; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.SessionCallback; import org.springframework.jms.support.destination.DestinationResolver; import org.springframework.jms.support.destination.DynamicDestinationResolver; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -70,17 +72,69 @@ public class ArtemisAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(ArtemisAutoConfiguration.class, JmsAutoConfiguration.class)); + @Test + public void connectionFactoryIsCachedByDefault() { + this.contextRunner.withUserConfiguration(EmptyConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(ConnectionFactory.class); + assertThat(context).hasSingleBean(CachingConnectionFactory.class); + CachingConnectionFactory connectionFactory = context + .getBean(CachingConnectionFactory.class); + assertThat(connectionFactory.getTargetConnectionFactory()) + .isInstanceOf(ActiveMQConnectionFactory.class); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheConsumers")).isEqualTo(false); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheProducers")).isEqualTo(true); + assertThat(connectionFactory.getSessionCacheSize()).isEqualTo(1); + }); + } + + @Test + public void connectionFactoryCachingCanBeCustomized() { + this.contextRunner.withUserConfiguration(EmptyConfiguration.class) + .withPropertyValues("spring.jms.cache.consumers=true", + "spring.jms.cache.producers=false", + "spring.jms.cache.session-cache-size=10") + .run((context) -> { + assertThat(context).hasSingleBean(ConnectionFactory.class); + assertThat(context).hasSingleBean(CachingConnectionFactory.class); + CachingConnectionFactory connectionFactory = context + .getBean(CachingConnectionFactory.class); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheConsumers")).isEqualTo(true); + assertThat(ReflectionTestUtils.getField(connectionFactory, + "cacheProducers")).isEqualTo(false); + assertThat(connectionFactory.getSessionCacheSize()).isEqualTo(10); + }); + } + + @Test + public void connectionFactoryCachingCanBeDisabled() { + this.contextRunner.withUserConfiguration(EmptyConfiguration.class) + .withPropertyValues("spring.jms.cache.enabled=false").run((context) -> { + assertThat(context).hasSingleBean(ConnectionFactory.class); + assertThat(context).doesNotHaveBean(CachingConnectionFactory.class); + assertThat(context.getBean(ConnectionFactory.class)) + .isInstanceOf(ActiveMQConnectionFactory.class); + }); + } + @Test public void nativeConnectionFactory() { this.contextRunner.withUserConfiguration(EmptyConfiguration.class) .withPropertyValues("spring.artemis.mode:native").run((context) -> { JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertThat(factory).isEqualTo(jmsTemplate.getConnectionFactory()); - assertNettyConnectionFactory(factory, "localhost", 61616); - assertThat(factory.getUser()).isNull(); - assertThat(factory.getPassword()).isNull(); + ConnectionFactory connectionFactory = context + .getBean(ConnectionFactory.class); + assertThat(connectionFactory) + .isEqualTo(jmsTemplate.getConnectionFactory()); + ActiveMQConnectionFactory activeMQConnectionFactory = getActiveMQConnectionFactory( + connectionFactory); + assertNettyConnectionFactory(activeMQConnectionFactory, "localhost", + 61616); + assertThat(activeMQConnectionFactory.getUser()).isNull(); + assertThat(activeMQConnectionFactory.getPassword()).isNull(); }); } @@ -90,9 +144,10 @@ public class ArtemisAutoConfigurationTests { .withPropertyValues("spring.artemis.mode:native", "spring.artemis.host:192.168.1.144", "spring.artemis.port:9876") .run((context) -> { - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertNettyConnectionFactory(factory, "192.168.1.144", 9876); + assertNettyConnectionFactory( + getActiveMQConnectionFactory( + context.getBean(ConnectionFactory.class)), + "192.168.1.144", 9876); }); } @@ -103,12 +158,17 @@ public class ArtemisAutoConfigurationTests { "spring.artemis.user:user", "spring.artemis.password:secret") .run((context) -> { JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertThat(factory).isEqualTo(jmsTemplate.getConnectionFactory()); - assertNettyConnectionFactory(factory, "localhost", 61616); - assertThat(factory.getUser()).isEqualTo("user"); - assertThat(factory.getPassword()).isEqualTo("secret"); + ConnectionFactory connectionFactory = context + .getBean(ConnectionFactory.class); + assertThat(connectionFactory) + .isEqualTo(jmsTemplate.getConnectionFactory()); + ActiveMQConnectionFactory activeMQConnectionFactory = getActiveMQConnectionFactory( + connectionFactory); + assertNettyConnectionFactory(activeMQConnectionFactory, "localhost", + 61616); + assertThat(activeMQConnectionFactory.getUser()).isEqualTo("user"); + assertThat(activeMQConnectionFactory.getPassword()) + .isEqualTo("secret"); }); } @@ -125,9 +185,8 @@ public class ArtemisAutoConfigurationTests { org.apache.activemq.artemis.core.config.Configuration.class); assertThat(configuration.isPersistenceEnabled()).isFalse(); assertThat(configuration.isSecurityEnabled()).isFalse(); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertInVmConnectionFactory(factory); + assertInVmConnectionFactory(getActiveMQConnectionFactory( + context.getBean(ConnectionFactory.class))); }); } @@ -142,9 +201,8 @@ public class ArtemisAutoConfigurationTests { org.apache.activemq.artemis.core.config.Configuration.class); assertThat(configuration.isPersistenceEnabled()).isFalse(); assertThat(configuration.isSecurityEnabled()).isFalse(); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertInVmConnectionFactory(factory); + assertInVmConnectionFactory(getActiveMQConnectionFactory( + context.getBean(ConnectionFactory.class))); }); } @@ -155,9 +213,10 @@ public class ArtemisAutoConfigurationTests { .withPropertyValues("spring.artemis.embedded.enabled:false") .run((context) -> { assertThat(context).doesNotHaveBean(EmbeddedJMS.class); - ActiveMQConnectionFactory factory = context - .getBean(ActiveMQConnectionFactory.class); - assertNettyConnectionFactory(factory, "localhost", 61616); + assertNettyConnectionFactory( + getActiveMQConnectionFactory( + context.getBean(ConnectionFactory.class)), + "localhost", 61616); }); } @@ -169,9 +228,8 @@ public class ArtemisAutoConfigurationTests { "spring.artemis.embedded.enabled:false") .run((context) -> { assertThat(context.getBeansOfType(EmbeddedJMS.class)).isEmpty(); - ActiveMQConnectionFactory connectionFactory = context - .getBean(ActiveMQConnectionFactory.class); - assertInVmConnectionFactory(connectionFactory); + assertInVmConnectionFactory(getActiveMQConnectionFactory( + context.getBean(ConnectionFactory.class))); }); } @@ -374,6 +432,13 @@ public class ArtemisAutoConfigurationTests { }); } + private ActiveMQConnectionFactory getActiveMQConnectionFactory( + ConnectionFactory connectionFactory) { + assertThat(connectionFactory).isInstanceOf(CachingConnectionFactory.class); + return (ActiveMQConnectionFactory) ((CachingConnectionFactory) connectionFactory) + .getTargetConnectionFactory(); + } + private TransportConfiguration assertInVmConnectionFactory( ActiveMQConnectionFactory connectionFactory) { TransportConfiguration transportConfig = getSingleTransportConfiguration( diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 8d27a409964..b51a13af0bc 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -995,6 +995,10 @@ content into your application. Rather, pick only the properties that you need. spring.integration.jdbc.schema=classpath:org/springframework/integration/jdbc/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema. # JMS ({sc-spring-boot-autoconfigure}/jms/JmsProperties.{sc-ext}[JmsProperties]) + spring.jms.cache.consumers=false # Whether to cache message consumers. + spring.jms.cache.enabled=true # Whether to cache sessions. + spring.jms.cache.producers=true # Whether to cache message producers. + spring.jms.cache.session-cache-size=1 # Size of the session cache (per JMS Session type). spring.jms.jndi-name= # Connection factory JNDI name. When set, takes precedence to others connection factory auto-configurations. spring.jms.listener.acknowledge-mode= # Acknowledge mode of the container. By default, the listener is transacted with automatic acknowledgment. spring.jms.listener.auto-startup=true # Start the container automatically on startup. diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 57b690451a8..f67b0b7fef6 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -5065,7 +5065,16 @@ ActiveMQ configuration is controlled by external configuration properties in spring.activemq.password=secret ---- -You can also pool JMS resources by adding a dependency to +By default, a `CachingConnectionFactory` wraps the native `ConnectionFactory` with +sensible settings that you can control by external configuration properties in +`+spring.jms.*+`: + +[source,properties,indent=0] +---- + spring.jms.cache.session-cache-size=5 +---- + +If you'd rather use native pooling, you can do so by adding a dependency to `org.apache.activemq:activemq-jms-pool` and configuring the `PooledConnectionFactory` accordingly, as shown in the following example: @@ -5122,7 +5131,16 @@ list to create them with the default options, or you can define bean(s) of type `org.apache.activemq.artemis.jms.server.config.TopicConfiguration`, for advanced queue and topic configurations, respectively. -You can also pool JMS resources by adding a dependency to +By default, a `CachingConnectionFactory` wraps the native `ConnectionFactory` with +sensible settings that you can control by external configuration properties in +`+spring.jms.*+`: + +[source,properties,indent=0] +---- + spring.jms.cache.session-cache-size=5 +---- + +If you'd rather use native pooling, you can do so by adding a dependency to `org.apache.activemq:activemq-jms-pool` and configuring the `PooledConnectionFactory` accordingly, as shown in the following example: