From 0ef54a79b15dc4d0463fba4ee4eeef2c948059af Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 6 Jun 2018 14:44:26 +0200 Subject: [PATCH] Add support for connection pooling with Artemis This commit expands ActiveMQ's connection pooling to artemis. The same pooling features are now shared by the two brokers and a PooledConnectionFactory can be auto-configured when the necessary jar is present. Closes gh-13523 --- ...ctiveMQConnectionFactoryConfiguration.java | 45 +---- .../jms/activemq/ActiveMQProperties.java | 159 +--------------- .../PooledConnectionFactoryFactory.java | 79 ++++++++ .../PooledConnectionFactoryProperties.java | 178 ++++++++++++++++++ ...ArtemisConnectionFactoryConfiguration.java | 26 ++- .../jms/artemis/ArtemisProperties.java | 11 +- .../jms/JmsAutoConfigurationTests.java | 2 +- .../ActiveMQAutoConfigurationTests.java | 2 +- .../ArtemisAutoConfigurationTests.java | 83 ++++++++ .../appendix-application-properties.adoc | 11 ++ .../main/asciidoc/spring-boot-features.adoc | 12 +- 11 files changed, 413 insertions(+), 195 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryFactory.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryProperties.java 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 34ee546807..e4a8a5a046 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 @@ -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. @@ -21,7 +21,8 @@ import java.util.List; import javax.jms.ConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory; -import org.apache.activemq.pool.PooledConnectionFactory; +import org.apache.activemq.jms.pool.PooledConnectionFactory; +import org.apache.commons.pool2.PooledObject; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -54,7 +55,7 @@ class ActiveMQConnectionFactoryConfiguration { } @Configuration - @ConditionalOnClass(PooledConnectionFactory.class) + @ConditionalOnClass({ PooledConnectionFactory.class, PooledObject.class }) static class PooledConnectionFactoryConfiguration { @Bean(destroyMethod = "stop") @@ -62,38 +63,12 @@ class ActiveMQConnectionFactoryConfiguration { public PooledConnectionFactory pooledJmsConnectionFactory( ActiveMQProperties properties, ObjectProvider> factoryCustomizers) { - PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory( - new ActiveMQConnectionFactoryFactory(properties, - factoryCustomizers.getIfAvailable()).createConnectionFactory( - ActiveMQConnectionFactory.class)); - ActiveMQProperties.Pool pool = properties.getPool(); - pooledConnectionFactory.setBlockIfSessionPoolIsFull(pool.isBlockIfFull()); - if (pool.getBlockIfFullTimeout() != null) { - pooledConnectionFactory.setBlockIfSessionPoolIsFullTimeout( - pool.getBlockIfFullTimeout().toMillis()); - } - pooledConnectionFactory - .setCreateConnectionOnStartup(pool.isCreateConnectionOnStartup()); - if (pool.getExpiryTimeout() != null) { - pooledConnectionFactory - .setExpiryTimeout(pool.getExpiryTimeout().toMillis()); - } - if (pool.getIdleTimeout() != null) { - pooledConnectionFactory - .setIdleTimeout((int) pool.getIdleTimeout().toMillis()); - } - pooledConnectionFactory.setMaxConnections(pool.getMaxConnections()); - pooledConnectionFactory.setMaximumActiveSessionPerConnection( - pool.getMaximumActiveSessionPerConnection()); - pooledConnectionFactory - .setReconnectOnException(pool.isReconnectOnException()); - if (pool.getTimeBetweenExpirationCheck() != null) { - pooledConnectionFactory.setTimeBetweenExpirationCheckMillis( - pool.getTimeBetweenExpirationCheck().toMillis()); - } - pooledConnectionFactory - .setUseAnonymousProducers(pool.isUseAnonymousProducers()); - return pooledConnectionFactory; + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory( + properties, factoryCustomizers.getIfAvailable()) + .createConnectionFactory(ActiveMQConnectionFactory.class); + return new PooledConnectionFactoryFactory(properties.getPool()) + .createPooledConnectionFactory(connectionFactory); + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java index a951fe2708..8e9352dd83 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; /** * Configuration properties for ActiveMQ. @@ -70,7 +71,8 @@ public class ActiveMQProperties { */ private Duration sendTimeout = Duration.ofMillis(0); - private final Pool pool = new Pool(); + @NestedConfigurationProperty + private final PooledConnectionFactoryProperties pool = new PooledConnectionFactoryProperties(); private final Packages packages = new Packages(); @@ -130,7 +132,7 @@ public class ActiveMQProperties { this.sendTimeout = sendTimeout; } - public Pool getPool() { + public PooledConnectionFactoryProperties getPool() { return this.pool; } @@ -138,159 +140,6 @@ public class ActiveMQProperties { return this.packages; } - public static class Pool { - - /** - * Whether a PooledConnectionFactory should be created, instead of a regular - * ConnectionFactory. - */ - private boolean enabled; - - /** - * Whether to block when a connection is requested and the pool is full. Set it to - * false to throw a "JMSException" instead. - */ - private boolean blockIfFull = true; - - /** - * Blocking period before throwing an exception if the pool is still full. - */ - private Duration blockIfFullTimeout = Duration.ofMillis(-1); - - /** - * Whether to create a connection on startup. Can be used to warm up the pool on - * startup. - */ - private boolean createConnectionOnStartup = true; - - /** - * Connection expiration timeout. - */ - private Duration expiryTimeout = Duration.ofMillis(0); - - /** - * Connection idle timeout. - */ - private Duration idleTimeout = Duration.ofSeconds(30); - - /** - * Maximum number of pooled connections. - */ - private int maxConnections = 1; - - /** - * Maximum number of active sessions per connection. - */ - private int maximumActiveSessionPerConnection = 500; - - /** - * Reset the connection when a "JMSException" occurs. - */ - private boolean reconnectOnException = true; - - /** - * Time to sleep between runs of the idle connection eviction thread. When - * negative, no idle connection eviction thread runs. - */ - private Duration timeBetweenExpirationCheck = Duration.ofMillis(-1); - - /** - * Whether to use only one anonymous "MessageProducer" instance. Set it to false - * to create one "MessageProducer" every time one is required. - */ - private boolean useAnonymousProducers = true; - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isBlockIfFull() { - return this.blockIfFull; - } - - public void setBlockIfFull(boolean blockIfFull) { - this.blockIfFull = blockIfFull; - } - - public Duration getBlockIfFullTimeout() { - return this.blockIfFullTimeout; - } - - public void setBlockIfFullTimeout(Duration blockIfFullTimeout) { - this.blockIfFullTimeout = blockIfFullTimeout; - } - - public boolean isCreateConnectionOnStartup() { - return this.createConnectionOnStartup; - } - - public void setCreateConnectionOnStartup(boolean createConnectionOnStartup) { - this.createConnectionOnStartup = createConnectionOnStartup; - } - - public Duration getExpiryTimeout() { - return this.expiryTimeout; - } - - public void setExpiryTimeout(Duration expiryTimeout) { - this.expiryTimeout = expiryTimeout; - } - - public Duration getIdleTimeout() { - return this.idleTimeout; - } - - public void setIdleTimeout(Duration idleTimeout) { - this.idleTimeout = idleTimeout; - } - - public int getMaxConnections() { - return this.maxConnections; - } - - public void setMaxConnections(int maxConnections) { - this.maxConnections = maxConnections; - } - - public int getMaximumActiveSessionPerConnection() { - return this.maximumActiveSessionPerConnection; - } - - public void setMaximumActiveSessionPerConnection( - int maximumActiveSessionPerConnection) { - this.maximumActiveSessionPerConnection = maximumActiveSessionPerConnection; - } - - public boolean isReconnectOnException() { - return this.reconnectOnException; - } - - public void setReconnectOnException(boolean reconnectOnException) { - this.reconnectOnException = reconnectOnException; - } - - public Duration getTimeBetweenExpirationCheck() { - return this.timeBetweenExpirationCheck; - } - - public void setTimeBetweenExpirationCheck(Duration timeBetweenExpirationCheck) { - this.timeBetweenExpirationCheck = timeBetweenExpirationCheck; - } - - public boolean isUseAnonymousProducers() { - return this.useAnonymousProducers; - } - - public void setUseAnonymousProducers(boolean useAnonymousProducers) { - this.useAnonymousProducers = useAnonymousProducers; - } - - } - public static class Packages { /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryFactory.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryFactory.java new file mode 100644 index 0000000000..33a9c04350 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryFactory.java @@ -0,0 +1,79 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jms.activemq; + +import javax.jms.ConnectionFactory; + +import org.apache.activemq.jms.pool.PooledConnectionFactory; + +/** + * Factory to create a {@link PooledConnectionFactory} from properties defined in + * {@link PooledConnectionFactoryProperties}. + * + * @author Stephane Nicoll + * @since 2.1.0 + */ +public class PooledConnectionFactoryFactory { + + private final PooledConnectionFactoryProperties properties; + + public PooledConnectionFactoryFactory(PooledConnectionFactoryProperties properties) { + this.properties = properties; + } + + /** + * Create a {@link PooledConnectionFactory} based on the specified + * {@link ConnectionFactory}. + * @param connectionFactory the connection factory to wrap + * @return a pooled connection factory + */ + public PooledConnectionFactory createPooledConnectionFactory( + ConnectionFactory connectionFactory) { + PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(); + pooledConnectionFactory.setConnectionFactory(connectionFactory); + + pooledConnectionFactory + .setBlockIfSessionPoolIsFull(this.properties.isBlockIfFull()); + if (this.properties.getBlockIfFullTimeout() != null) { + pooledConnectionFactory.setBlockIfSessionPoolIsFullTimeout( + this.properties.getBlockIfFullTimeout().toMillis()); + } + pooledConnectionFactory.setCreateConnectionOnStartup( + this.properties.isCreateConnectionOnStartup()); + if (this.properties.getExpiryTimeout() != null) { + pooledConnectionFactory + .setExpiryTimeout(this.properties.getExpiryTimeout().toMillis()); + } + if (this.properties.getIdleTimeout() != null) { + pooledConnectionFactory + .setIdleTimeout((int) this.properties.getIdleTimeout().toMillis()); + } + pooledConnectionFactory.setMaxConnections(this.properties.getMaxConnections()); + pooledConnectionFactory.setMaximumActiveSessionPerConnection( + this.properties.getMaximumActiveSessionPerConnection()); + pooledConnectionFactory + .setReconnectOnException(this.properties.isReconnectOnException()); + if (this.properties.getTimeBetweenExpirationCheck() != null) { + pooledConnectionFactory.setTimeBetweenExpirationCheckMillis( + this.properties.getTimeBetweenExpirationCheck().toMillis()); + } + pooledConnectionFactory + .setUseAnonymousProducers(this.properties.isUseAnonymousProducers()); + return pooledConnectionFactory; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryProperties.java new file mode 100644 index 0000000000..75f05b481f --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/PooledConnectionFactoryProperties.java @@ -0,0 +1,178 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jms.activemq; + +import java.time.Duration; + +/** + * Configuration properties for connection factory pooling. + * + * @author Stephane Nicoll + * @since 2.1.0 + */ +public class PooledConnectionFactoryProperties { + + /** + * Whether a PooledConnectionFactory should be created, instead of a regular + * ConnectionFactory. + */ + private boolean enabled; + + /** + * Whether to block when a connection is requested and the pool is full. Set it to + * false to throw a "JMSException" instead. + */ + private boolean blockIfFull = true; + + /** + * Blocking period before throwing an exception if the pool is still full. + */ + private Duration blockIfFullTimeout = Duration.ofMillis(-1); + + /** + * Whether to create a connection on startup. Can be used to warm up the pool on + * startup. + */ + private boolean createConnectionOnStartup = true; + + /** + * Connection expiration timeout. + */ + private Duration expiryTimeout = Duration.ofMillis(0); + + /** + * Connection idle timeout. + */ + private Duration idleTimeout = Duration.ofSeconds(30); + + /** + * Maximum number of pooled connections. + */ + private int maxConnections = 1; + + /** + * Maximum number of active sessions per connection. + */ + private int maximumActiveSessionPerConnection = 500; + + /** + * Reset the connection when a "JMSException" occurs. + */ + private boolean reconnectOnException = true; + + /** + * Time to sleep between runs of the idle connection eviction thread. When negative, + * no idle connection eviction thread runs. + */ + private Duration timeBetweenExpirationCheck = Duration.ofMillis(-1); + + /** + * Whether to use only one anonymous "MessageProducer" instance. Set it to false to + * create one "MessageProducer" every time one is required. + */ + private boolean useAnonymousProducers = true; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isBlockIfFull() { + return this.blockIfFull; + } + + public void setBlockIfFull(boolean blockIfFull) { + this.blockIfFull = blockIfFull; + } + + public Duration getBlockIfFullTimeout() { + return this.blockIfFullTimeout; + } + + public void setBlockIfFullTimeout(Duration blockIfFullTimeout) { + this.blockIfFullTimeout = blockIfFullTimeout; + } + + public boolean isCreateConnectionOnStartup() { + return this.createConnectionOnStartup; + } + + public void setCreateConnectionOnStartup(boolean createConnectionOnStartup) { + this.createConnectionOnStartup = createConnectionOnStartup; + } + + public Duration getExpiryTimeout() { + return this.expiryTimeout; + } + + public void setExpiryTimeout(Duration expiryTimeout) { + this.expiryTimeout = expiryTimeout; + } + + public Duration getIdleTimeout() { + return this.idleTimeout; + } + + public void setIdleTimeout(Duration idleTimeout) { + this.idleTimeout = idleTimeout; + } + + public int getMaxConnections() { + return this.maxConnections; + } + + public void setMaxConnections(int maxConnections) { + this.maxConnections = maxConnections; + } + + public int getMaximumActiveSessionPerConnection() { + return this.maximumActiveSessionPerConnection; + } + + public void setMaximumActiveSessionPerConnection( + int maximumActiveSessionPerConnection) { + this.maximumActiveSessionPerConnection = maximumActiveSessionPerConnection; + } + + public boolean isReconnectOnException() { + return this.reconnectOnException; + } + + public void setReconnectOnException(boolean reconnectOnException) { + this.reconnectOnException = reconnectOnException; + } + + public Duration getTimeBetweenExpirationCheck() { + return this.timeBetweenExpirationCheck; + } + + public void setTimeBetweenExpirationCheck(Duration timeBetweenExpirationCheck) { + this.timeBetweenExpirationCheck = timeBetweenExpirationCheck; + } + + public boolean isUseAnonymousProducers() { + return this.useAnonymousProducers; + } + + public void setUseAnonymousProducers(boolean useAnonymousProducers) { + this.useAnonymousProducers = useAnonymousProducers; + } + +} 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 f48b2cf6fe..51b9947f37 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 @@ -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. @@ -19,9 +19,14 @@ package org.springframework.boot.autoconfigure.jms.artemis; import javax.jms.ConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.jms.pool.PooledConnectionFactory; +import org.apache.commons.pool2.PooledObject; 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.activemq.PooledConnectionFactoryFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -30,16 +35,35 @@ import org.springframework.context.annotation.Configuration; * * @author Eddú Meléndez * @author Phillip Webb + * @author Stephane Nicoll */ @Configuration @ConditionalOnMissingBean(ConnectionFactory.class) class ArtemisConnectionFactoryConfiguration { @Bean + @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); } + @Configuration + @ConditionalOnClass({ PooledConnectionFactory.class, PooledObject.class }) + static class PooledConnectionFactoryConfiguration { + + @Bean(destroyMethod = "stop") + @ConditionalOnProperty(prefix = "spring.artemis.pool", name = "enabled", havingValue = "true", matchIfMissing = false) + public PooledConnectionFactory pooledJmsConnectionFactory( + ListableBeanFactory beanFactory, ArtemisProperties properties) { + ActiveMQConnectionFactory connectionFactory = new ArtemisConnectionFactoryFactory( + beanFactory, properties) + .createConnectionFactory(ActiveMQConnectionFactory.class); + return new PooledConnectionFactoryFactory(properties.getPool()) + .createPooledConnectionFactory(connectionFactory); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisProperties.java index e0f43aba26..0a6fcf4a53 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/ArtemisProperties.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. @@ -23,7 +23,9 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants; +import org.springframework.boot.autoconfigure.jms.activemq.PooledConnectionFactoryProperties; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; /** * Configuration properties for Artemis. @@ -62,6 +64,9 @@ public class ArtemisProperties { private final Embedded embedded = new Embedded(); + @NestedConfigurationProperty + private final PooledConnectionFactoryProperties pool = new PooledConnectionFactoryProperties(); + public ArtemisMode getMode() { return this.mode; } @@ -106,6 +111,10 @@ public class ArtemisProperties { return this.embedded; } + public PooledConnectionFactoryProperties getPool() { + return this.pool; + } + /** * Configuration for an embedded Artemis server. */ 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 c65f6a2bbf..849a9e7c64 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 @@ -20,7 +20,7 @@ import javax.jms.ConnectionFactory; import javax.jms.Session; import org.apache.activemq.ActiveMQConnectionFactory; -import org.apache.activemq.pool.PooledConnectionFactory; +import org.apache.activemq.jms.pool.PooledConnectionFactory; import org.junit.Test; import org.springframework.beans.BeansException; 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 17ac223972..a1337dc94d 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 @@ -19,7 +19,7 @@ package org.springframework.boot.autoconfigure.jms.activemq; import javax.jms.ConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory; -import org.apache.activemq.pool.PooledConnectionFactory; +import org.apache.activemq.jms.pool.PooledConnectionFactory; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; 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 82c166146c..25b015aee4 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 @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; import java.util.UUID; +import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; @@ -36,6 +37,7 @@ import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; import org.apache.activemq.artemis.jms.server.config.impl.JMSQueueConfigurationImpl; import org.apache.activemq.artemis.jms.server.config.impl.TopicConfigurationImpl; import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; +import org.apache.activemq.jms.pool.PooledConnectionFactory; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -291,6 +293,87 @@ public class ArtemisAutoConfigurationTests { }); } + @Test + public void defaultPooledConnectionFactoryIsApplied() { + this.contextRunner.withPropertyValues("spring.artemis.pool.enabled=true") + .run((context) -> { + assertThat(context.getBeansOfType(PooledConnectionFactory.class)) + .hasSize(1); + PooledConnectionFactory connectionFactory = context + .getBean(PooledConnectionFactory.class); + PooledConnectionFactory defaultFactory = new PooledConnectionFactory(); + assertThat(connectionFactory.isBlockIfSessionPoolIsFull()) + .isEqualTo(defaultFactory.isBlockIfSessionPoolIsFull()); + assertThat(connectionFactory.getBlockIfSessionPoolIsFullTimeout()) + .isEqualTo( + defaultFactory.getBlockIfSessionPoolIsFullTimeout()); + assertThat(connectionFactory.isCreateConnectionOnStartup()) + .isEqualTo(defaultFactory.isCreateConnectionOnStartup()); + assertThat(connectionFactory.getExpiryTimeout()) + .isEqualTo(defaultFactory.getExpiryTimeout()); + assertThat(connectionFactory.getIdleTimeout()) + .isEqualTo(defaultFactory.getIdleTimeout()); + assertThat(connectionFactory.getMaxConnections()) + .isEqualTo(defaultFactory.getMaxConnections()); + assertThat(connectionFactory.getMaximumActiveSessionPerConnection()) + .isEqualTo(defaultFactory + .getMaximumActiveSessionPerConnection()); + assertThat(connectionFactory.isReconnectOnException()) + .isEqualTo(defaultFactory.isReconnectOnException()); + assertThat(connectionFactory.getTimeBetweenExpirationCheckMillis()) + .isEqualTo( + defaultFactory.getTimeBetweenExpirationCheckMillis()); + assertThat(connectionFactory.isUseAnonymousProducers()) + .isEqualTo(defaultFactory.isUseAnonymousProducers()); + }); + } + + @Test + public void customPooledConnectionFactoryIsApplied() { + this.contextRunner + .withPropertyValues("spring.artemis.pool.enabled=true", + "spring.artemis.pool.blockIfFull=false", + "spring.artemis.pool.blockIfFullTimeout=64", + "spring.artemis.pool.createConnectionOnStartup=false", + "spring.artemis.pool.expiryTimeout=4096", + "spring.artemis.pool.idleTimeout=512", + "spring.artemis.pool.maxConnections=256", + "spring.artemis.pool.maximumActiveSessionPerConnection=1024", + "spring.artemis.pool.reconnectOnException=false", + "spring.artemis.pool.timeBetweenExpirationCheck=2048", + "spring.artemis.pool.useAnonymousProducers=false") + .run((context) -> { + assertThat(context.getBeansOfType(PooledConnectionFactory.class)) + .hasSize(1); + PooledConnectionFactory connectionFactory = context + .getBean(PooledConnectionFactory.class); + assertThat(connectionFactory.isBlockIfSessionPoolIsFull()).isFalse(); + assertThat(connectionFactory.getBlockIfSessionPoolIsFullTimeout()) + .isEqualTo(64); + assertThat(connectionFactory.isCreateConnectionOnStartup()).isFalse(); + assertThat(connectionFactory.getExpiryTimeout()).isEqualTo(4096); + assertThat(connectionFactory.getIdleTimeout()).isEqualTo(512); + assertThat(connectionFactory.getMaxConnections()).isEqualTo(256); + assertThat(connectionFactory.getMaximumActiveSessionPerConnection()) + .isEqualTo(1024); + assertThat(connectionFactory.isReconnectOnException()).isFalse(); + assertThat(connectionFactory.getTimeBetweenExpirationCheckMillis()) + .isEqualTo(2048); + assertThat(connectionFactory.isUseAnonymousProducers()).isFalse(); + }); + } + + @Test + public void pooledConnectionFactoryConfiguration() { + this.contextRunner.withPropertyValues("spring.artemis.pool.enabled:true") + .run((context) -> { + ConnectionFactory factory = context.getBean(ConnectionFactory.class); + assertThat(factory).isInstanceOf(PooledConnectionFactory.class); + context.getSourceApplicationContext().close(); + assertThat(factory.createConnection()).isNull(); + }); + } + 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 b44fee4a12..8d27a40996 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 @@ -969,6 +969,17 @@ content into your application. Rather, pick only the properties that you need. spring.artemis.host=localhost # Artemis broker host. spring.artemis.mode= # Artemis deployment mode, auto-detected by default. spring.artemis.password= # Login password of the broker. + spring.artemis.pool.block-if-full=true # Whether to block when a connection is requested and the pool is full. Set it to false to throw a "JMSException" instead. + spring.artemis.pool.block-if-full-timeout=-1ms # Blocking period before throwing an exception if the pool is still full. + spring.artemis.pool.create-connection-on-startup=true # Whether to create a connection on startup. Can be used to warm up the pool on startup. + spring.artemis.pool.enabled=false # Whether a PooledConnectionFactory should be created, instead of a regular ConnectionFactory. + spring.artemis.pool.expiry-timeout=0ms # Connection expiration timeout. + spring.artemis.pool.idle-timeout=30s # Connection idle timeout. + spring.artemis.pool.max-connections=1 # Maximum number of pooled connections. + spring.artemis.pool.maximum-active-session-per-connection=500 # Maximum number of active sessions per connection. + spring.artemis.pool.reconnect-on-exception=true # Reset the connection when a "JMSException" occurs. + spring.artemis.pool.time-between-expiration-check=-1ms # Time to sleep between runs of the idle connection eviction thread. When negative, no idle connection eviction thread runs. + spring.artemis.pool.use-anonymous-producers=true # Whether to use only one anonymous "MessageProducer" instance. Set it to false to create one "MessageProducer" every time one is required. spring.artemis.port=61616 # Artemis broker port. spring.artemis.user= # Login user of the broker. 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 49454de7b6..57b690451a 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 @@ -5066,7 +5066,7 @@ ActiveMQ configuration is controlled by external configuration properties in ---- You can also pool JMS resources by adding a dependency to -`org.apache.activemq:activemq-pool` and configuring the `PooledConnectionFactory` +`org.apache.activemq:activemq-jms-pool` and configuring the `PooledConnectionFactory` accordingly, as shown in the following example: [source,properties,indent=0] @@ -5122,6 +5122,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 +`org.apache.activemq:activemq-jms-pool` and configuring the `PooledConnectionFactory` +accordingly, as shown in the following example: + +[source,properties,indent=0] +---- + spring.artemis.pool.enabled=true + spring.artemis.pool.max-connections=50 +---- + See {sc-spring-boot-autoconfigure}/jms/artemis/ArtemisProperties.{sc-ext}[`ArtemisProperties`] for more supported options.