Better customization of the auto-configured ActiveMQConnectionFactory

This commit exposes additional properties and a callback interface to
further tune the auto-configured ActiveMQConnectionFactory.

Closes gh-9667
pull/9758/merge
Stephane Nicoll 7 years ago
parent 9a34d952e9
commit 9fd94720f1

@ -16,11 +16,14 @@
package org.springframework.boot.autoconfigure.jms.activemq;
import java.util.List;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.pool.PooledConnectionFactory;
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;
@ -44,9 +47,11 @@ class ActiveMQConnectionFactoryConfiguration {
@Bean
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "false", matchIfMissing = true)
public ActiveMQConnectionFactory jmsConnectionFactory(ActiveMQProperties properties) {
return new ActiveMQConnectionFactoryFactory(properties)
.createConnectionFactory(ActiveMQConnectionFactory.class);
public ActiveMQConnectionFactory jmsConnectionFactory(ActiveMQProperties properties,
ObjectProvider<List<ActiveMQConnectionFactoryCustomizer>> factoryCustomizers) {
return new ActiveMQConnectionFactoryFactory(properties,
factoryCustomizers.getIfAvailable()).createConnectionFactory(
ActiveMQConnectionFactory.class);
}
@ConditionalOnClass(PooledConnectionFactory.class)
@ -56,10 +61,12 @@ class ActiveMQConnectionFactoryConfiguration {
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true", matchIfMissing = false)
@ConfigurationProperties(prefix = "spring.activemq.pool.configuration")
public PooledConnectionFactory pooledJmsConnectionFactory(
ActiveMQProperties properties) {
ActiveMQProperties properties,
ObjectProvider<List<ActiveMQConnectionFactoryCustomizer>> factoryCustomizers) {
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(
new ActiveMQConnectionFactoryFactory(properties)
.createConnectionFactory(ActiveMQConnectionFactory.class));
new ActiveMQConnectionFactoryFactory(properties,
factoryCustomizers.getIfAvailable()).createConnectionFactory(
ActiveMQConnectionFactory.class));
ActiveMQProperties.Pool pool = properties.getPool();
pooledConnectionFactory.setBlockIfSessionPoolIsFull(pool.isBlockIfFull());
pooledConnectionFactory.setBlockIfSessionPoolIsFullTimeout(

@ -0,0 +1,36 @@
/*
* 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.
* 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 org.apache.activemq.ActiveMQConnectionFactory;
/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link ActiveMQConnectionFactory} whilst retaining default auto-configuration.
*
* @author Stephane Nicoll
* @since 1.5.5
*/
public interface ActiveMQConnectionFactoryCustomizer {
/**
* Customize the {@link ActiveMQConnectionFactory}.
* @param factory the factory to customize
*/
void customize(ActiveMQConnectionFactory factory);
}

@ -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.
@ -17,6 +17,8 @@
package org.springframework.boot.autoconfigure.jms.activemq;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;
import org.apache.activemq.ActiveMQConnectionFactory;
@ -40,9 +42,14 @@ class ActiveMQConnectionFactoryFactory {
private final ActiveMQProperties properties;
ActiveMQConnectionFactoryFactory(ActiveMQProperties properties) {
private final List<ActiveMQConnectionFactoryCustomizer> factoryCustomizers;
ActiveMQConnectionFactoryFactory(ActiveMQProperties properties,
List<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
Assert.notNull(properties, "Properties must not be null");
this.properties = properties;
this.factoryCustomizers = (factoryCustomizers != null ? factoryCustomizers
: Collections.<ActiveMQConnectionFactoryCustomizer>emptyList());
}
public <T extends ActiveMQConnectionFactory> T createConnectionFactory(
@ -59,6 +66,9 @@ class ActiveMQConnectionFactoryFactory {
private <T extends ActiveMQConnectionFactory> T doCreateConnectionFactory(
Class<T> factoryClass) throws Exception {
T factory = createConnectionFactoryInstance(factoryClass);
factory.setCloseTimeout(this.properties.getCloseTimeout());
factory.setNonBlockingRedelivery(this.properties.isNonBlockingRedelivery());
factory.setSendTimeout(this.properties.getSendTimeout());
Packages packages = this.properties.getPackages();
if (packages.getTrustAll() != null) {
factory.setTrustAllPackages(packages.getTrustAll());
@ -66,6 +76,7 @@ class ActiveMQConnectionFactoryFactory {
if (!packages.getTrusted().isEmpty()) {
factory.setTrustedPackages(packages.getTrusted());
}
customize(factory);
return factory;
}
@ -82,6 +93,12 @@ class ActiveMQConnectionFactoryFactory {
return factoryClass.getConstructor(String.class).newInstance(brokerUrl);
}
private void customize(ActiveMQConnectionFactory connectionFactory) {
for (ActiveMQConnectionFactoryCustomizer factoryCustomizer : this.factoryCustomizers) {
factoryCustomizer.customize(connectionFactory);
}
}
String determineBrokerUrl() {
if (this.properties.getBrokerUrl() != null) {
return this.properties.getBrokerUrl();

@ -53,6 +53,24 @@ public class ActiveMQProperties {
*/
private String password;
/**
* Time to wait, in milliseconds, before considering a close complete.
*/
private int closeTimeout = 15000;
/**
* Do not stop message delivery before re-delivering messages from a rolled back
* transaction. This implies that message order will not be preserved when this is
* enabled.
*/
private boolean nonBlockingRedelivery = false;
/**
* Time to wait, in milliseconds, on Message sends for a response. Set it to 0 to
* indicate to wait forever.
*/
private int sendTimeout = 0;
private Pool pool = new Pool();
private Packages packages = new Packages();
@ -89,6 +107,30 @@ public class ActiveMQProperties {
this.password = password;
}
public int getCloseTimeout() {
return this.closeTimeout;
}
public void setCloseTimeout(int closeTimeout) {
this.closeTimeout = closeTimeout;
}
public boolean isNonBlockingRedelivery() {
return this.nonBlockingRedelivery;
}
public void setNonBlockingRedelivery(boolean nonBlockingRedelivery) {
this.nonBlockingRedelivery = nonBlockingRedelivery;
}
public int getSendTimeout() {
return this.sendTimeout;
}
public void setSendTimeout(int sendTimeout) {
this.sendTimeout = sendTimeout;
}
public Pool getPool() {
return this.pool;
}

@ -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.
@ -16,12 +16,15 @@
package org.springframework.boot.autoconfigure.jms.activemq;
import java.util.List;
import javax.jms.ConnectionFactory;
import javax.transaction.TransactionManager;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQXAConnectionFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -47,18 +50,22 @@ class ActiveMQXAConnectionFactoryConfiguration {
@Primary
@Bean(name = { "jmsConnectionFactory", "xaJmsConnectionFactory" })
public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties,
ObjectProvider<List<ActiveMQConnectionFactoryCustomizer>> factoryCustomizers,
XAConnectionFactoryWrapper wrapper) throws Exception {
ActiveMQXAConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(
properties).createConnectionFactory(ActiveMQXAConnectionFactory.class);
properties, factoryCustomizers.getIfAvailable())
.createConnectionFactory(ActiveMQXAConnectionFactory.class);
return wrapper.wrapConnectionFactory(connectionFactory);
}
@Bean
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "false", matchIfMissing = true)
public ActiveMQConnectionFactory nonXaJmsConnectionFactory(
ActiveMQProperties properties) {
return new ActiveMQConnectionFactoryFactory(properties)
.createConnectionFactory(ActiveMQConnectionFactory.class);
ActiveMQProperties properties,
ObjectProvider<List<ActiveMQConnectionFactoryCustomizer>> factoryCustomizers) {
return new ActiveMQConnectionFactoryFactory(properties,
factoryCustomizers.getIfAvailable()).createConnectionFactory(
ActiveMQConnectionFactory.class);
}
}

@ -61,6 +61,54 @@ public class ActiveMQAutoConfigurationTests {
.isTrue();
}
@Test
public void defaultsConnectionFactoryAreApplied() {
load(EmptyConfiguration.class, "spring.activemq.pool.enabled=false");
assertThat(this.context.getBeansOfType(ActiveMQConnectionFactory.class)).hasSize(1);
ActiveMQConnectionFactory connectionFactory = this.context.getBean(
ActiveMQConnectionFactory.class);
ActiveMQConnectionFactory defaultFactory = new ActiveMQConnectionFactory(
"vm://localhost?broker.persistent=false");
assertThat(connectionFactory.getUserName()).isEqualTo(
defaultFactory.getUserName());
assertThat(connectionFactory.getPassword()).isEqualTo(
defaultFactory.getPassword());
assertThat(connectionFactory.getCloseTimeout()).isEqualTo(
defaultFactory.getCloseTimeout());
assertThat(connectionFactory.isNonBlockingRedelivery()).isEqualTo(
defaultFactory.isNonBlockingRedelivery());
assertThat(connectionFactory.getSendTimeout()).isEqualTo(
defaultFactory.getSendTimeout());
assertThat(connectionFactory.isTrustAllPackages()).isEqualTo(
defaultFactory.isTrustAllPackages());
assertThat(connectionFactory.getTrustedPackages()).containsExactly(
defaultFactory.getTrustedPackages().toArray(new String[]{}));
}
@Test
public void customConnectionFactoryAreApplied() {
load(EmptyConfiguration.class, "spring.activemq.pool.enabled=false",
"spring.activemq.brokerUrl=vm://localhost?useJmx=false&broker.persistent=false",
"spring.activemq.user=foo",
"spring.activemq.password=bar",
"spring.activemq.closeTimeout=500",
"spring.activemq.nonBlockingRedelivery=true",
"spring.activemq.sendTimeout=1000",
"spring.activemq.packages.trust-all=false",
"spring.activemq.packages.trusted=com.example.acme");
assertThat(this.context.getBeansOfType(ActiveMQConnectionFactory.class)).hasSize(1);
ActiveMQConnectionFactory connectionFactory = this.context.getBean(
ActiveMQConnectionFactory.class);
assertThat(connectionFactory.getUserName()).isEqualTo("foo");
assertThat(connectionFactory.getPassword()).isEqualTo("bar");
assertThat(connectionFactory.getCloseTimeout()).isEqualTo(500);
assertThat(connectionFactory.isNonBlockingRedelivery()).isEqualTo(true);
assertThat(connectionFactory.getSendTimeout()).isEqualTo(1000);
assertThat(connectionFactory.isTrustAllPackages()).isFalse();
assertThat(connectionFactory.getTrustedPackages()).containsExactly(
"com.example.acme");
}
@Test
public void defaultsPooledConnectionFactoryAreApplied() {
load(EmptyConfiguration.class, "spring.activemq.pool.enabled=true");
@ -161,6 +209,16 @@ public class ActiveMQAutoConfigurationTests {
assertThat(connectionFactory.createConnection()).isNull();
}
@Test
public void customizerOverridesAutConfig() {
load(CustomizerConfiguration.class);
ActiveMQConnectionFactory connectionFactory = this.context.getBean(
ActiveMQConnectionFactory.class);
assertThat(connectionFactory.getBrokerURL()).isEqualTo(
"vm://localhost?useJmx=false&broker.persistent=false");
assertThat(connectionFactory.getUserName()).isEqualTo("foobar");
}
private void load(Class<?> config, String... environment) {
this.context = doLoad(config, environment);
}
@ -191,4 +249,20 @@ public class ActiveMQAutoConfigurationTests {
}
@Configuration
static class CustomizerConfiguration {
@Bean
public ActiveMQConnectionFactoryCustomizer activeMQConnectionFactoryCustomizer() {
return new ActiveMQConnectionFactoryCustomizer() {
@Override
public void customize(ActiveMQConnectionFactory factory) {
factory.setBrokerURL(
"vm://localhost?useJmx=false&broker.persistent=false");
factory.setUserName("foobar");
}
};
}
}
}

@ -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.
@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.jms.activemq;
import java.util.Collections;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;
@ -38,49 +40,53 @@ public class ActiveMQPropertiesTests {
@Test
public void getBrokerUrlIsInMemoryByDefault() {
assertThat(new ActiveMQConnectionFactoryFactory(this.properties)
.determineBrokerUrl()).isEqualTo(DEFAULT_EMBEDDED_BROKER_URL);
assertThat(createFactory(this.properties).determineBrokerUrl()).isEqualTo(
DEFAULT_EMBEDDED_BROKER_URL);
}
@Test
public void getBrokerUrlUseExplicitBrokerUrl() {
this.properties.setBrokerUrl("vm://foo-bar");
assertThat(new ActiveMQConnectionFactoryFactory(this.properties)
.determineBrokerUrl()).isEqualTo("vm://foo-bar");
assertThat(createFactory(this.properties).determineBrokerUrl()).isEqualTo(
"vm://foo-bar");
}
@Test
public void getBrokerUrlWithInMemorySetToFalse() {
this.properties.setInMemory(false);
assertThat(new ActiveMQConnectionFactoryFactory(this.properties)
.determineBrokerUrl()).isEqualTo(DEFAULT_NETWORK_BROKER_URL);
assertThat(createFactory(this.properties).determineBrokerUrl()).isEqualTo(
DEFAULT_NETWORK_BROKER_URL);
}
@Test
public void getExplicitBrokerUrlAlwaysWins() {
this.properties.setBrokerUrl("vm://foo-bar");
this.properties.setInMemory(false);
assertThat(new ActiveMQConnectionFactoryFactory(this.properties)
.determineBrokerUrl()).isEqualTo("vm://foo-bar");
assertThat(createFactory(this.properties).determineBrokerUrl()).isEqualTo(
"vm://foo-bar");
}
@Test
public void setTrustAllPackages() {
this.properties.getPackages().setTrustAll(true);
assertThat(new ActiveMQConnectionFactoryFactory(this.properties)
.createConnectionFactory(ActiveMQConnectionFactory.class)
.isTrustAllPackages()).isEqualTo(true);
assertThat(createFactory(this.properties).createConnectionFactory(
ActiveMQConnectionFactory.class).isTrustAllPackages()).isEqualTo(true);
}
@Test
public void setTrustedPackages() {
this.properties.getPackages().setTrustAll(false);
this.properties.getPackages().getTrusted().add("trusted.package");
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactoryFactory(
this.properties).createConnectionFactory(ActiveMQConnectionFactory.class);
ActiveMQConnectionFactory factory = createFactory(this.properties)
.createConnectionFactory(ActiveMQConnectionFactory.class);
assertThat(factory.isTrustAllPackages()).isEqualTo(false);
assertThat(factory.getTrustedPackages().size()).isEqualTo(1);
assertThat(factory.getTrustedPackages().get(0)).isEqualTo("trusted.package");
}
private ActiveMQConnectionFactoryFactory createFactory(ActiveMQProperties properties) {
return new ActiveMQConnectionFactoryFactory(properties,
Collections.<ActiveMQConnectionFactoryCustomizer>emptyList());
}
}

@ -837,23 +837,26 @@ content into your application; rather pick only the properties that you need.
# ----------------------------------------
# ACTIVEMQ ({sc-spring-boot-autoconfigure}/jms/activemq/ActiveMQProperties.{sc-ext}[ActiveMQProperties])
spring.activemq.broker-url= # URL of the ActiveMQ broker. Auto-generated by default. For instance `tcp://localhost:61616`
spring.activemq.broker-url= # URL of the ActiveMQ broker. Auto-generated by default.
spring.activemq.close-timeout=15000 # Time to wait, in milliseconds, before considering a close complete.
spring.activemq.in-memory=true # Specify if the default broker URL should be in memory. Ignored if an explicit broker has been specified.
spring.activemq.non-blocking-redelivery=false # Do not stop message delivery before re-delivering messages from a rolled back transaction. This implies that message order will not be preserved when this is enabled.
spring.activemq.password= # Login password of the broker.
spring.activemq.send-timeout=0 # Time to wait, in milliseconds, on Message sends for a response. Set it to 0 to indicate to wait forever.
spring.activemq.user= # Login user of the broker.
spring.activemq.packages.trust-all=false # Trust all packages.
spring.activemq.packages.trust-all= # Trust all packages.
spring.activemq.packages.trusted= # Comma-separated list of specific packages to trust (when not trusting all packages).
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.
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.

@ -4460,9 +4460,10 @@ accordingly:
spring.activemq.pool.max-connections=50
----
See
TIP: See
{sc-spring-boot-autoconfigure}/jms/activemq/ActiveMQProperties.{sc-ext}[`ActiveMQProperties`]
for more of the supported options.
for more of the supported options. You can also register an arbitrary number of beans
implementing `ActiveMQConnectionFactoryCustomizer` for more advanced customizations.
By default, ActiveMQ creates a destination if it does not exist yet, so destinations are
resolved against their provided names.

Loading…
Cancel
Save