From 9a34d952e9878f509ba775eeb18cc5738605b3c7 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 24 Jul 2017 13:48:53 +0200 Subject: [PATCH] Provide an alternative to `spring.activemq.pool.configuration` This commit expands the support of PooledConnectionFactory so that binding the third party object is no longer necessary. All 3rd party properties are now deprecated in favour of our explicit support. The main reason behind this change is that a `connection-factory` and `properties` property were exposed. The former is used to set the `ConnectionFactory` and makes no sense as a key. The latter is rebuilding the underlying `ActiveMQConnectionFactory` at each call without reusing any existing settings. Closes gh-9837 --- ...ctiveMQConnectionFactoryConfiguration.java | 18 ++- .../jms/activemq/ActiveMQProperties.java | 121 ++++++++++++++++-- ...itional-spring-configuration-metadata.json | 72 +++++++++++ .../ActiveMQAutoConfigurationTests.java | 103 ++++++++++++--- .../appendix-application-properties.adoc | 16 ++- 5 files changed, 292 insertions(+), 38 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 19beb7e808..0e79bde66c 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 @@ -60,11 +60,23 @@ class ActiveMQConnectionFactoryConfiguration { PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory( new ActiveMQConnectionFactoryFactory(properties) .createConnectionFactory(ActiveMQConnectionFactory.class)); - ActiveMQProperties.Pool pool = properties.getPool(); - pooledConnectionFactory.setMaxConnections(pool.getMaxConnections()); - pooledConnectionFactory.setIdleTimeout(pool.getIdleTimeout()); + pooledConnectionFactory.setBlockIfSessionPoolIsFull(pool.isBlockIfFull()); + pooledConnectionFactory.setBlockIfSessionPoolIsFullTimeout( + pool.getBlockIfFullTimeout()); + pooledConnectionFactory.setCreateConnectionOnStartup( + pool.isCreateConnectionOnStartup()); pooledConnectionFactory.setExpiryTimeout(pool.getExpiryTimeout()); + pooledConnectionFactory.setIdleTimeout(pool.getIdleTimeout()); + pooledConnectionFactory.setMaxConnections(pool.getMaxConnections()); + pooledConnectionFactory.setMaximumActiveSessionPerConnection( + pool.getMaximumActiveSessionPerConnection()); + pooledConnectionFactory.setReconnectOnException( + pool.isReconnectOnException()); + pooledConnectionFactory.setTimeBetweenExpirationCheckMillis( + pool.getTimeBetweenExpirationCheck()); + pooledConnectionFactory.setUseAnonymousProducers( + pool.isUseAnonymousProducers()); return pooledConnectionFactory; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java index 6cd2fa7b1d..ec2d555be7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -110,9 +110,26 @@ public class ActiveMQProperties { private boolean enabled; /** - * Maximum number of pooled connections. + * Block when a connection is requested and the pool is full. Set it to false to + * throw a "JMSException" instead. */ - private int maxConnections = 1; + private boolean blockIfFull = true; + + /** + * Blocking period, in milliseconds, before throwing an exception if the pool + * is still full. + */ + private long blockIfFullTimeout = -1; + + /** + * Create a connection on startup. Can be used to warm-up the pool on startup. + */ + private boolean createConnectionOnStartup = true; + + /** + * Connection expiration timeout in milliseconds. + */ + private long expiryTimeout = 0; /** * Connection idle timeout in milliseconds. @@ -120,9 +137,31 @@ public class ActiveMQProperties { private int idleTimeout = 30000; /** - * Connection expiration timeout in milliseconds. + * Maximum number of pooled connections. */ - private long expiryTimeout = 0; + private int maxConnections = 1; + + /** + * Maximum number of active sessions per connection. + */ + private int maximumActiveSessionPerConnection = 500; + + /** + * Reset the connection when a "JMXException" occurs. + */ + private boolean reconnectOnException = true; + + /** + * Time to sleep, in milliseconds, between runs of the idle connection eviction + * thread. When negative, no idle connection eviction thread runs. + */ + private long timeBetweenExpirationCheck = -1; + + /** + * 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; @@ -132,12 +171,36 @@ public class ActiveMQProperties { this.enabled = enabled; } - public int getMaxConnections() { - return this.maxConnections; + public boolean isBlockIfFull() { + return this.blockIfFull; } - public void setMaxConnections(int maxConnections) { - this.maxConnections = maxConnections; + public void setBlockIfFull(boolean blockIfFull) { + this.blockIfFull = blockIfFull; + } + + public long getBlockIfFullTimeout() { + return this.blockIfFullTimeout; + } + + public void setBlockIfFullTimeout(long blockIfFullTimeout) { + this.blockIfFullTimeout = blockIfFullTimeout; + } + + public boolean isCreateConnectionOnStartup() { + return this.createConnectionOnStartup; + } + + public void setCreateConnectionOnStartup(boolean createConnectionOnStartup) { + this.createConnectionOnStartup = createConnectionOnStartup; + } + + public long getExpiryTimeout() { + return this.expiryTimeout; + } + + public void setExpiryTimeout(long expiryTimeout) { + this.expiryTimeout = expiryTimeout; } public int getIdleTimeout() { @@ -148,12 +211,44 @@ public class ActiveMQProperties { this.idleTimeout = idleTimeout; } - public long getExpiryTimeout() { - return this.expiryTimeout; + public int getMaxConnections() { + return this.maxConnections; } - public void setExpiryTimeout(long expiryTimeout) { - this.expiryTimeout = expiryTimeout; + 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 long getTimeBetweenExpirationCheck() { + return this.timeBetweenExpirationCheck; + } + + public void setTimeBetweenExpirationCheck(long timeBetweenExpirationCheck) { + this.timeBetweenExpirationCheck = timeBetweenExpirationCheck; + } + + public boolean isUseAnonymousProducers() { + return this.useAnonymousProducers; + } + + public void setUseAnonymousProducers(boolean useAnonymousProducers) { + this.useAnonymousProducers = useAnonymousProducers; } } diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index fd20b74564..7a544ac03a 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -33,6 +33,78 @@ "description": "Enable the default error page displayed in browsers in case of a server error.", "defaultValue": true }, + { + "name": "spring.activemq.pool.configuration.block-if-session-pool-is-full", + "deprecation": { + "replacement": "spring.activemq.pool.block-if-full" + } + }, + { + "name": "spring.activemq.pool.configuration.block-if-session-pool-is-full-timeout", + "deprecation": { + "replacement": "spring.activemq.pool.block-if-full-timeout" + } + }, + { + "name": "spring.activemq.pool.configuration.connection-factory", + "deprecation": { + "reason": "This third party property cannot be set via the Environment." + } + }, + { + "name": "spring.activemq.pool.configuration.create-connection-on-startup", + "deprecation": { + "replacement": "spring.activemq.pool.create-connection-on-startup" + } + }, + { + "name": "spring.activemq.pool.configuration.expiry-timeout", + "deprecation": { + "replacement": "spring.activemq.pool.expiry-timeout" + } + }, + { + "name": "spring.activemq.pool.configuration.idle-timeout", + "deprecation": { + "replacement": "spring.activemq.pool.idle-timeout" + } + }, + { + "name": "spring.activemq.pool.configuration.max-connections", + "deprecation": { + "replacement": "spring.activemq.pool.max-connections" + } + }, + { + "name": "spring.activemq.pool.configuration.maximum-active-session-per-connection", + "deprecation": { + "replacement": "spring.activemq.pool.maximum-active-session-per-connection" + } + }, + { + "name": "spring.activemq.pool.configuration.properties", + "deprecation": { + "reason": "This third party property cannot be set via the Environment." + } + }, + { + "name": "spring.activemq.pool.configuration.reconnect-on-exception", + "deprecation": { + "replacement": "spring.activemq.pool.reconnect-on-exception" + } + }, + { + "name": "spring.activemq.pool.configuration.time-between-expiration-check-millis", + "deprecation": { + "replacement": "spring.activemq.pool.time-between-expiration-check" + } + }, + { + "name": "spring.activemq.pool.configuration.use-anonymous-producers", + "deprecation": { + "replacement": "spring.activemq.pool.use-anonymous-producers" + } + }, { "name": "spring.aop.auto", "type": "java.lang.Boolean", 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 9daa5a93ff..df0b9c5ff5 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -62,24 +62,93 @@ public class ActiveMQAutoConfigurationTests { } @Test - public void customPooledConnectionFactoryConfiguration() { - load(EmptyConfiguration.class, "spring.activemq.pool.enabled:true", - "spring.activemq.pool.maxConnections:256", - "spring.activemq.pool.idleTimeout:512", - "spring.activemq.pool.expiryTimeout:4096", - "spring.activemq.pool.configuration.maximumActiveSessionPerConnection:1024", - "spring.activemq.pool.configuration.timeBetweenExpirationCheckMillis:2048"); - ConnectionFactory connectionFactory = this.context - .getBean(ConnectionFactory.class); - assertThat(connectionFactory).isInstanceOf(PooledConnectionFactory.class); - PooledConnectionFactory pooledConnectionFactory = (PooledConnectionFactory) connectionFactory; - assertThat(pooledConnectionFactory.getMaxConnections()).isEqualTo(256); - assertThat(pooledConnectionFactory.getIdleTimeout()).isEqualTo(512); - assertThat(pooledConnectionFactory.getMaximumActiveSessionPerConnection()) + public void defaultsPooledConnectionFactoryAreApplied() { + load(EmptyConfiguration.class, "spring.activemq.pool.enabled=true"); + assertThat(this.context.getBeansOfType(PooledConnectionFactory.class)).hasSize(1); + PooledConnectionFactory connectionFactory = this.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 customPooledConnectionFactoryAreApplied() { + load(EmptyConfiguration.class, "spring.activemq.pool.enabled=true", + "spring.activemq.pool.blockIfFull=false", + "spring.activemq.pool.blockIfFullTimeout=64", + "spring.activemq.pool.createConnectionOnStartup=false", + "spring.activemq.pool.expiryTimeout=4096", + "spring.activemq.pool.idleTimeout=512", + "spring.activemq.pool.maxConnections=256", + "spring.activemq.pool.maximumActiveSessionPerConnection=1024", + "spring.activemq.pool.reconnectOnException=false", + "spring.activemq.pool.timeBetweenExpirationCheck=2048", + "spring.activemq.pool.useAnonymousProducers=false"); + assertThat(this.context.getBeansOfType(PooledConnectionFactory.class)).hasSize(1); + PooledConnectionFactory connectionFactory = this.context.getBean( + PooledConnectionFactory.class); + assertThat(connectionFactory.isBlockIfSessionPoolIsFull()).isEqualTo(false); + assertThat(connectionFactory.getBlockIfSessionPoolIsFullTimeout()).isEqualTo(64); + assertThat(connectionFactory.isCreateConnectionOnStartup()).isEqualTo(false); + assertThat(connectionFactory.getExpiryTimeout()).isEqualTo(4096); + assertThat(connectionFactory.getIdleTimeout()).isEqualTo(512); + assertThat(connectionFactory.getMaxConnections()).isEqualTo(256); + assertThat(connectionFactory.getMaximumActiveSessionPerConnection()) .isEqualTo(1024); - assertThat(pooledConnectionFactory.getTimeBetweenExpirationCheckMillis()) + assertThat(connectionFactory.isReconnectOnException()).isEqualTo(false); + assertThat(connectionFactory.getTimeBetweenExpirationCheckMillis()) .isEqualTo(2048); - assertThat(pooledConnectionFactory.getExpiryTimeout()).isEqualTo(4096); + assertThat(connectionFactory.isUseAnonymousProducers()).isEqualTo(false); + } + + @Test + @Deprecated + public void customPooledConnectionFactoryOnTargetInstanceAreApplied() { + load(EmptyConfiguration.class, "spring.activemq.pool.enabled=true", + "spring.activemq.pool.configuration.blockIfSessionPoolIsFull=false", + "spring.activemq.pool.configuration.blockIfSessionPoolIsFullTimeout=64", + "spring.activemq.pool.configuration.createConnectionOnStartup=false", + "spring.activemq.pool.expiryTimeout=4096", + "spring.activemq.pool.idleTimeout=512", + "spring.activemq.pool.maxConnections=256", + "spring.activemq.pool.configuration.maximumActiveSessionPerConnection=1024", + "spring.activemq.pool.configuration.reconnectOnException=false", + "spring.activemq.pool.configuration.timeBetweenExpirationCheckMillis=2048", + "spring.activemq.pool.configuration.useAnonymousProducers=false"); + assertThat(this.context.getBeansOfType(PooledConnectionFactory.class)).hasSize(1); + PooledConnectionFactory connectionFactory = this.context.getBean( + PooledConnectionFactory.class); + assertThat(connectionFactory.isBlockIfSessionPoolIsFull()).isEqualTo(false); + assertThat(connectionFactory.getBlockIfSessionPoolIsFullTimeout()).isEqualTo(64); + assertThat(connectionFactory.isCreateConnectionOnStartup()).isEqualTo(false); + assertThat(connectionFactory.getExpiryTimeout()).isEqualTo(4096); + assertThat(connectionFactory.getIdleTimeout()).isEqualTo(512); + assertThat(connectionFactory.getMaxConnections()).isEqualTo(256); + assertThat(connectionFactory.getMaximumActiveSessionPerConnection()) + .isEqualTo(1024); + assertThat(connectionFactory.isReconnectOnException()).isEqualTo(false); + assertThat(connectionFactory.getTimeBetweenExpirationCheckMillis()) + .isEqualTo(2048); + assertThat(connectionFactory.isUseAnonymousProducers()).isEqualTo(false); } @Test diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 43164eb751..c681daa5f0 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -843,11 +843,17 @@ content into your application; rather pick only the properties that you need. spring.activemq.user= # Login user of the broker. spring.activemq.packages.trust-all=false # Trust all packages. spring.activemq.packages.trusted= # Comma-separated list of specific packages to trust (when not trusting all packages). - spring.activemq.pool.configuration.*= # See PooledConnectionFactory. - spring.activemq.pool.enabled=false # Whether a PooledConnectionFactory should be created instead of a regular ConnectionFactory. - spring.activemq.pool.expiry-timeout=0 # Connection expiration timeout in milliseconds. - spring.activemq.pool.idle-timeout=30000 # Connection idle timeout in milliseconds. - spring.activemq.pool.max-connections=1 # Maximum number of pooled connections. + spring.activemq.pool.block-if-full=true # Block when a connection is requested and the pool is full. Set it to false to throw a "JMSException" instead. + spring.activemq.pool.block-if-full-timeout=-1 # Blocking period, in milliseconds, before throwing an exception if the pool is still full. + spring.activemq.pool.create-connection-on-startup=true # Create a connection on startup. Can be used to warm-up the pool on startup. + spring.activemq.pool.enabled=false # Whether a PooledConnectionFactory should be created instead of a regular ConnectionFactory. + spring.activemq.pool.expiry-timeout=0 # Connection expiration timeout in milliseconds. + spring.activemq.pool.idle-timeout=30000 # Connection idle timeout in milliseconds. + spring.activemq.pool.max-connections=1 # Maximum number of pooled connections. + spring.activemq.pool.maximum-active-session-per-connection=500 # Maximum number of active sessions per connection. + spring.activemq.pool.reconnect-on-exception=true # Reset the connection when a "JMXException" occurs. + spring.activemq.pool.time-between-expiration-check=-1 # Time to sleep, in milliseconds, between runs of the idle connection eviction thread. When negative, no idle connection eviction thread runs. + spring.activemq.pool.use-anonymous-producers=true # Use only one anonymous "MessageProducer" instance. Set it to false to create one "MessageProducer" every time one is required. # ARTEMIS ({sc-spring-boot-autoconfigure}/jms/artemis/ArtemisProperties.{sc-ext}[ArtemisProperties]) spring.artemis.embedded.cluster-password= # Cluster password. Randomly generated on startup by default.